Skip to content

add patch level expiry warning popup & notification #126

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 364 commits into
base: 15-qpr2
Choose a base branch
from

Conversation

inthewaves
Copy link
Member

@inthewaves inthewaves commented Feb 17, 2025

Closes GrapheneOS/os-issue-tracker#3999
Review and merge with: GrapheneOS/platform_packages_apps_Settings#332

Implemented inside of SystemUI as a periodic JobService with a popup and notification.

Test this by building with the constant DEBUG_ALWAYS_EXPIRED set to true and then using Settings -> Security & privacy -> More security & privacy -> toggling Disable patch level expiry warnings to retrigger the check. Can also toggle with ExtSettings.USER_DISABLE_PATCH_LEVEL_EXPIRY_WARNING using adb directly:

adb shell settings put global grapheneos_patch_level_warning_disabled 1
adb shell settings put global grapheneos_patch_level_warning_disabled 0

Toggling it off will cancel the job, and toggling it on will schedule the periodic job which also runs the job and hence does an expiry check.

Updater disabled (as main user) Updater enabled (as main user) Updater missing (as main user)
Should never happen normally
enableupdater checkupdater missing
Notification
Tapping will open popup
Updater disabled
(as non-main user, who can't open updater)
notif enableupdater-nonsystem

Test: atest KeyguardViewMediatorTest (choose A for all)

Periodic patch level expiry check JobService

Periodically checks for patch level expiry, with checks occurring every CHECK_INTERVAL (currently 7 days). Since AOSP and GrapheneOS issue monthly security updates, a patch level older than one month plus EXPIRY_GRACE_PERIOD (currently 14 days) is considered expired. See getWarningStartDate for details on how the warning start date is calculated.

If expired, a system alert notification (a standard Android notification with high priority) is displayed to all users, and a popup warning (a SystemUI-style dialog that appears over everything) is shown upon the next keyguard unlock. The warnings are re-triggered at each CHECK_INTERVAL or upon device reboot. The warnings will cease once the device is updated to a build with a current patch level. The user can open the popup from the notification; doing this means the popup won't show up again for that user type on keyguard unlock.

See PatchLevelWarningDialogDelegate for the popup dialog behavior on a per-user basis.

If phone is currently being used while the expiry check goes off, only the notification will be shown. The popup is displayed upon the next keyguard unlock. The popup is extremely disruptive and we don't want to block whatever the user is actively doing.

This should never happen normally, but if Build.VERSION.SECURITY_PATCH cannot be parsed using the SECURITY_PATCH_DATE_FORMAT, the warnings will begin showing up on a weekly basis indicating that the security patch level cannot be parsed by the system.

Users can disable this warning (ExtSettings.USER_DISABLE_PATCH_LEVEL_EXPIRY_WARNING), and it can also be disabled on a per-device basis via a read-only device config (ExtSettings.DEVICE_DISABLED_PATCH_LEVEL_EXPIRY_WARNING, which is currently ro.disable_patch_level_expiry_warning) for EOL devices.

CHECK_INTERVAL

How often to check patch level. Also serves as the reminder interval once the device has an expired patch level. Currently set to 7 days.

Implicitly adds to the grace period for the first warning. Worst-case grace period is EXPIRY_GRACE_PERIOD + CHECK_INTERVAL plus Doze restrictions

e.g. if the patch level is 2025-01-05, EXPIRY_GRACE_PERIOD=2 weeks and CHECK_INTERVAL=1 week, then expiry warning starts showing up anytime in between 2 weeks and 3 weeks after the same date in the next month (i.e. 2025-02-05 + 2 to 3 weeks)

Popup dialog

Full-screen popup dialog to warn the user about their patch level being expired. Will show up on keyguard unlock and if the user taps on the system notification that also appears.

For the main / system user, it will direct them to either check updates manually and review the updater settings, or to enable the updater if it is disabled. For non-system users, it'll still display the updater status, but it'll mention automatic updates can only be managed by system user. See PeriodicPatchLevelExpiryCheck.getWarningDescriptionParagraphs for the exact strings that are used on a case-by-case basis.

The dialog is done in SystemUI. It's important that this dialog is tested so that it doesn't crash (e.g. from a upstream SystemUI change); this popup is shown automatically after a keyguard unlock, and any crashes from this dialog would crash SystemUI on an unlock as a result. It wouldn't permanently prevent the user from getting into their phone, as it's only displayed once a week for expired patch level dates (the PeriodicPatchLevelExpiryCheck job that marks the keyguard popup is not rescheduled).

The alternative to doing this in SystemUI that would maintain the show-on-unlock behavior would be to have an app that receives Intent.ACTION_USER_PRESENT broadcasts. However, this is not broadcast to to implicit receivers, so there would likely have to be an app running just to receive it. This dialog wouldn't even show up for users that regularly update, so it doesn't seem worth it. If we do ditch the behavior that it shows up on keyguard unlock and just use a non-dismissible notification that opens this full-screen dialog or an Activity, then a separate app could be feasible. However, it could result in code duplication. Doing it in SystemUI allows us to reuse other classes available here, which also makes consistency in theming super easy with future Android updates (since we're using the same code that launches SystemUI dialogs in general).

Assuming the user hasn't opened the popup from the notification, the popup dialog will only show up once after keyguard unlock for all non-system users and also once for the system user. We split it like this to compromise between two cases:

  • Someone using multiple system users for themselves: It could be annoying to get the popup for every single user that they log into. But we also want to inform them that they should do updates in the main user (especially for the use case where they don't daily drive the main user)
  • Sharing a phone with multiple people: The other people using the phone should be aware that the device is behind on security updates. Right now, only the owner and 1 other person will get the notification; if there are 3+ users accounts for 3+ different people, only 2 of them will be notified.

While we could expose a setting for this, it might actually encourage users to turn warnings off entirely. The user really should just update their phone! (Might be better just to have a per-user setting to indicate whether it's used by the same person as the main user, and then we read this setting. However, this approach could leak info about how the phone is used.)

We don't show buttons or links that would launch an intent when the device is not setup (in SetupWizard) or otherwise not provisioned.

We also have considerations for the screen pinning mode that can be launched via System UI as well as the fully locked mode that can be achieved on fully managed devices. No intents should be launched in locked task mode (especially because we include a web browser intent). Through testing, it doesn't seem like the popup is triggered when screen is pinned, since powering on the screen doesn't count as a keyguard unlock (i.e. whenever Intent.ACTION_USER_PRESENT would be sent out). Nevertheless, still need to be safe and check the locked task state.

muhomorr and others added 24 commits March 5, 2025 15:07
- Conversations: enabled by default on Pixel
- Charging ripple: enabled by default on Pixel
Taken from ProtonAOSP/android_vendor_proton@a32d8c4
Android has had location indicators for a while, but let's use the new
privacy indicator infrastructure for displaying them. This makes them
integrate better with the new camera and microphone indicators.

Change-Id: Ie457bb2dad17bddbf9dc3a818e3ec586023ce918
This resource is overridden with an overlay on stock OS to Google Files app.
If it's empty, SystemUI crashes after screenshot of a work profile app is taken.
Screenshot was still saved, crash happened when constructing post-screenshot UI.
On some devices, there is a telephony-related app that frequently
requests location, so exempting it from privacy indicators is desirable.
For example, the Pixel 6 has an IMS service where this applies.

Change-Id: I5e99c89367bc3ffd31794736b0d66014fdc4faae
PackageInstaller is being made into an updatable app upstream, but it currently depends on several
platform APIs on GrapheneOS.
"App info" screen now has a "Disable" button, which can be used as an
alternative to uninstallation.
Directory can be removed by the time listFiles() is called, which makes it return null. Null can
also be returned due to an I/O error.
APK is not yet available at the time of pre-approval request, which is incompatible with the
"Allow Network permission" checkbox in the confirmation dialog.

See PackageInstaller.Session#requestUserPreapproval() for more info.
Consumer Pixel devices have "MP1.0" as their hardware revision usually.
Devices stolen from Google will have "EVT", "PVT" or "DVT" set in
"ro.revision" by the bootloader. Additionally, check the secure boot
state prop set by the bootloader, "ro.boot.secure_boot", to ensure it is
set to "1", as typically pre-production devices won't have a blown
secure boot efuse, completely destroying any concept of verified boot.
Check both props, if either check fails, notify the user.

Squashed with: 778380b

Co-authored-by: Dmitry Muhomor <[email protected]>
lijilou and others added 16 commits March 7, 2025 09:57
The getOrCreateRootHomeTask method  return value is Nullable ,so we should make NonNull judgment before use the return value.

Test: OEM monkey test
Flag: EXEMPT bugfix
Bug: 386025825
Change-Id: I7efc57e94a3cdaf1db27fb696a975e194e86d8a3
Test: monkey test
Flag: EXEMPT bugfix
Bug: 397517013

Change-Id: I422ed54e62b12278873e38372b067d8eb42b8692
Signed-off-by: luanzhuang <[email protected]>
Test: monkey test
Flag: EXEMPT bugfix
Bug: 391091209
Change-Id: Ic094631fcbf8784b8f037733ea445737c2abec6e
No top non finishing activity exist in task.

Test: monkey test
Flag: EXEMPT bugfix
Bug: 387957062
Change-Id: I58e3fd1bca3088b0f187d3a12994d3ceb6ed7553
The IntentSender could be null, so it is necessary to add a null check.

Bug: 397855638
Flag: EXEMPT bugfix
Change-Id: I5ea32b975ba82b61c75bebf306fc4f25b27417ab
The getPrimaryPointerId method may return the  INVALID_POINTER_ID(-1) value and result to  array index out of bounds exception.The crash log:
java.lang.ArrayIndexOutOfBoundsException: length=32; index=-1
at com.android.server.accessibility.gestures.TouchState$ReceivedPointerTracker.getReceivedPointerDownX(TouchState.java:482)
at com.android.server.accessibility.gestures.TouchExplorer.handleActionMoveStateTouchExploring(TouchExplorer.java:910)
.....

Test: monkey test
Flag: EXEMPT bugfix
Bug: 399557645
Change-Id: Ib0b14471953d8f78cf98122dd868be8b80059a75
Even if the device does not support TelephonyManager (mPlatformType is not PLATFORM_VOICE), the app may still choose stream_voice_call to play sound. At this time, it is also necessary to determine whether stream_voice_call is active.

Change-Id: I88a925605e20e1fbc65974836a0f585b0e00bbcc
Signed-off-by: chenxin20 <[email protected]>
Extend debug-build-only option for weakening system app sepolicy to WebView DCL restriction setting.
It requires WebAssembly, which doesn't work in jitless mode.
With this, the clock and date are aligned with each other properly on
tablets.

Change-Id: Ib0954182892501cddcb6988a6afc17f8c4208d6d
Before this change, keyguard slice view would disappear after rotation,
breaking notification stack scroll layout alignment while at it.

Change-Id: I78a1b40a6aa6a9bb31ddc774e024b6b356e2a39c
Change-Id: Ic559f53de2ba2ee701f8e2335f7494dc91abb5c3
Lockscreen user switcher and its button are rendered below lockscreen notifications as of 15 QPR2.
@inthewaves inthewaves force-pushed the expiration-warning branch from 68a114c to 3060dd4 Compare March 9, 2025 20:04
@inthewaves inthewaves changed the base branch from 15 to 15-qpr2 March 10, 2025 01:37
…ng is on"

This change was needed by a now-reverted GmsCompat app change.
Desktop mode support remains disabled by default, it's controlled by a dev option.
@inthewaves
Copy link
Member Author

inthewaves commented Mar 16, 2025

A consequence of using JobScheduler: The JobService will still run periodically if SystemUI crashes, but it stops running periodically if the main user force stops the SystemUI app. The JobSchedulerService cancels jobs from apps if they're force stopped.

Seems like a simple thing is do to a regular check on unlock (1 per day) to see if job is still scheduled while the patch level expiry isn't disabled by user. The schedule here will make the dialog show up immediately if patch is expired and the job has been unexpectedly cancelled (and it has been more than 1 day since the last job schedule check on keyguard unlock).

Implemented inside of SystemUI as a periodic JobService with a popup
and notification. Set `DEBUG_ALWAYS_EXPIRED` to true to test it.

Pair with corresponding packages/apps/Settings commit

Test: atest KeyguardViewMediatorTest (choose A for all)
The JobScheduler service will cancel jobs from apps that have been force stopped. In SystemUI's
case, the app seems to still run when the main user force stops the app, but the intent that is
broadcast on app force stopped is still fired off. JobSchedulerService log output beforehand:

Got android.intent.action.PACKAGE_RESTARTED for 10125/com.android.systemui
Pulled stopped state of com.android.systemui (10125): false
Removing jobs for pkg com.android.systemui at uid 10125
CANCEL: b9480f5 #u0a125/450000 com.android.systemui/.patchlevelwarning.PeriodicPatchLevelExpiryCheck
Got android.intent.action.PACKAGE_UNSTOPPED for 10125/com.android.systemui
@thestinger thestinger force-pushed the 15-qpr2 branch 5 times, most recently from 5f3142f to 8edf282 Compare April 10, 2025 20:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Bake expiration warnings into GrapheneOS releases?