CVE-2025-48555 AI-generated analysis
platform/packages/apps/Settings, notification history (NotificationStation fragment)
Settings' NotificationStation (notification log/history screen) now works out in onResume which profiles of the current user are secure, currently locked and have LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS disabled. For history entries from those profiles it hides the title, the text and the notification/ranking extras.
Root cause
NotificationStation builds a HistoricalNotificationInfo from every StatusBarNotification that reaches its NotificationListenerService, which is registered as a system service for ActivityManager.getCurrentUser(). HistoricalNotificationPreference.updatePreference always rendered info.title, info.text, mInfo.notificationExtra and mInfo.rankingExtra. Nothing checked whether the notification's user (info.user) was a profile locked with a secure credential and set not to show private notification content while locked. As a result, a locked profile's notification content could be shown in the Settings UI.
How the fix works
- NotificationStation.java:onResume: the new field mContentRestrictedUsers (ArrayList<Integer>) is cleared and rebuilt on every resume. For each UserInfo in UserManager.getProfiles(mContext.getUserId()), the user id is added to the set when three things hold: Settings.Secure LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS for that user is 0 (read with a default of 0), LockPatternUtils.isSecure(user.id) is true, and KeyguardManager.isDeviceLocked(user.id) is true.
- NotificationStation.java: every HistoricalNotificationPreference construction and updatePreference call in the diff now passes mContentRestrictedUsers.contains(<info>.user) as a new boolean. These call sites are the full-list rebuild loop, the removal path, the posted-update and addFirst branches, and the path that calls updateFromRanking.
- NotificationStation.java:HistoricalNotificationPreference: the constructor takes a new restrictContent parameter and stores it in mRestrictContent. The view-binding method calls updatePreference(mInfo, mRestrictContent).
- NotificationStation.java:HistoricalNotificationPreference.updatePreference(info, restrictContent): when restrictContent is true, R.id.title and R.id.text are set to View.GONE (the same branch as the empty-string case) and their text is not set. R.id.notification_extra and R.id.ranking_extra are also set to View.GONE and do not receive mInfo.notificationExtra / mInfo.rankingExtra. Package name, icon and timestamp are still shown.
- Two hunks in createFromSbn only add whitespace and change nothing functionally.
Completeness
- The fix covers all four content fields that updatePreference renders (title, text, notification_extra, ranking_extra), and every call site changed in the diff passes the new flag.
- The restricted set is computed only in onResume. If a profile becomes locked while the fragment stays resumed, it is not re-evaluated until the next resume.
- In the non-restricted branch for notification_extra / ranking_extra, visibility is never set back to View.VISIBLE. Once a preference has been rendered restricted, those two views can stay GONE.
- Only the UI rendering is gated. HistoricalNotificationInfo still holds title, text and extras in memory for restricted users.
- Restriction depends only on LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, isSecure and isDeviceLocked for profiles of mContext.getUserId(). Notifications with sbn.getUserId()==USER_ALL are mapped to USER_SYSTEM, so they are restricted only if the system user meets those conditions. Other redaction inputs, such as the DevicePolicyManager keyguard DISABLE_UNREDACTED_NOTIFICATIONS feature, are not consulted in this code.
- Icon, package name and timestamp of restricted-profile notifications stay visible.
Behavior changes
- In the Settings notification log, entries from a locked, secure profile that disallows private notifications on the lock screen show only package name, icon and timestamp. Title, text and the expandable notification/ranking extras are hidden.
- onResume now does extra per-profile lookups (a Settings.Secure read, LockPatternUtils.isSecure, KeyguardManager.isDeviceLocked) on every resume.
- The HistoricalNotificationPreference constructor and updatePreference each take an extra boolean parameter.
Per major
- Android 13: The bulletin lists 13 as affected, but the material has no Android 13 commit or code context, so the status of the fix on 13 is unknown.
- Android 14: Same fix: commit 06a1643e44a6 in android-security-14.0.0_r24 (cherry-pick of b1bfda5a03c6, Change-Id Ie7705a1ceea54203585c3b3737d0bada68aa1469). The patched region is fully present (8/8) at android-security-14.0.0_r29.
- Android 15: Same fix: commit 0db1143041a6 in android-security-15.0.0_r12, with the same hunks as the 14 patch (file index e79a4ac58c6..4315c6dbfaa, which differs from 14's 3b27e1947d2..278f6ec198c). 8/8 lines are present at android-security-15.0.0_r17.
- Android 16: Same fix: commit 596c7b9911f2 in android-security-16.0.0_r3, with a diff identical to 15 (same file indexes). 8/8 lines are present at android-security-16.0.0_r8.
- Android 17: Not affected: the bulletin does not list Android 17. The patched region is present (8/8) at android-security-17.0.0_r1.
Unknowns
- No Android 13 commit is provided, even though 13 is listed as affected, so whether the patch applies cleanly there is unknown.
- The disclosure scenario reported in bug 396666065 is not shown.
- It is not shown whether a profile lock-state change while the fragment is resumed is handled some other way.
- The enclosing method names of the changed call sites, and whether other call sites exist in the full file, are not shown.