CVE-2026-0012 AI-generated analysis
platform/frameworks/base, SystemUI notification row (packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java)
In SystemUI ExpandableNotificationRow.setHideSensitive, the decision to skip the hide-sensitive (public/private layout) animation and apply visibility and content alphas immediately is now taken also when the row is showing the public (redacted) layout and the showing layout did not change; before the fix this depended only on the `animated` flag (Android 14/15) or used a feature flag plus layout-unchanged (Android 16).
Root cause
setHideSensitive(hideSensitive, animated, delay, duration) computes mShowingPublic = mSensitive && hideSensitive, returns early if mShowingPublicInitialized and the value did not change, and then either applies the new state immediately (cancel animations on mPublicLayout/mPrivateLayout/mChildrenContainer, resetAllContentAlphas(), set mPublicLayout visibility, updateChildrenVisibility()) or calls animateShowingPublic(). On Android 16 before the fix, the immediate path was also taken whenever Flags.skipHideSensitiveNotifAnimation() was on and the showing layout was unchanged, regardless of whether the row was showing the public or the private layout; the commit message calls the prior code too aggressive and says it can cause 0 content alpha. On Android 14/15 before the fix the immediate path depended only on !animated, so with animated=true the unchanged-layout, not-yet-initialized case with mShowingPublic true went through animateShowingPublic(). The fix makes all branches use one skip condition: mShowingPublic && (mShowingPublic == oldShowingPublic).
How the fix works
- ExpandableNotificationRow.java:setHideSensitive (Android 14 commit 334c15a0ebee, Android 15 commit 679801a99ccd): introduces `boolean isShowingLayoutNotChanged = mShowingPublic == oldShowingPublic;`, reuses it in the existing early return `if (mShowingPublicInitialized && isShowingLayoutNotChanged) return;`, adds `final boolean shouldSkipHideSensitiveAnimation = mShowingPublic && isShowingLayoutNotChanged;` and changes the branch to `if (!animated || shouldSkipHideSensitiveAnimation)` so the immediate (non-animated) path, which cancels pending animations, calls resetAllContentAlphas() and sets mPublicLayout visibility from mShowingPublic, is also taken in that case instead of animateShowingPublic().
- ExpandableNotificationRow.java:setHideSensitive (Android 16 commit ef2a8e203164): the skip condition already existed as `Flags.skipHideSensitiveNotifAnimation() && isShowingLayoutNotChanged`; the fix replaces the flag check with `mShowingPublic`, so the animation is skipped only when the row is showing the public layout and the layout did not change.
- Because of the early return, the new skip condition can only be true on a call where mShowingPublicInitialized is false while mShowingPublic is already true and unchanged.
Completeness
- The change is confined to the branch selection inside setHideSensitive; animateShowingPublic(), resetAllContentAlphas() and updateChildrenVisibility() are not modified.
- On Android 16 the fix narrows an over-broad flag-gated skip; on Android 14/15 there was no skip at all, so the backport adds the narrowed skip rather than removing a broad one. Whether the 14/15 code exhibited the same alpha problem via animateShowingPublic() is not shown in the material.
- The code context at android-security-14.0.0_r29 shows the fixed lines (isShowingLayoutNotChanged, shouldSkipHideSensitiveAnimation, `!animated || shouldSkipHideSensitiveAnimation`); the per-major check reports 6/7 lines of the patched region present.
- The Flags.skipHideSensitiveNotifAnimation() flag is no longer consulted at this site on Android 16 after the fix; the material does not show whether the flag declaration itself was removed.
Behavior changes
- Android 14/15: when setHideSensitive is called with animated=true on a row whose mShowingPublic is true and unchanged (only possible when mShowingPublicInitialized is false), the transition is now applied immediately (animations cancelled, content alphas reset, mPublicLayout set VISIBLE) instead of running animateShowingPublic().
- Android 16: when the row is showing the private layout (mShowingPublic false) and the layout is unchanged, the hide-sensitive animation is no longer skipped even if Flags.skipHideSensitiveNotifAnimation() is enabled; the skip at this site is now independent of that flag.
- No new error paths or API changes; the change is SystemUI-internal UI state handling.
Per major
- Android 14: adapted fix: commit 334c15a0ebee in android-security-14.0.0_r25 (cherry-pick of e93d4b015c28); the branch had no skip condition, so the fix adds `shouldSkipHideSensitiveAnimation = mShowingPublic && isShowingLayoutNotChanged` to the `!animated` test. At android-security-14.0.0_r29 the fixed code is visible (lines 2804-2812); the per-major check reports 6/7 lines of the patched region present.
- Android 15: adapted fix: commit 679801a99ccd in android-security-15.0.0_r13, same hunk as Android 14 with an extra pre-existing `float oldAlpha = getContentView().getAlpha();` line in context. 7/8 lines of the patched region present at android-security-15.0.0_r17.
- Android 16: same fix (one-line form): commit ef2a8e203164 in android-security-16.0.0_r4 replaces `Flags.skipHideSensitiveNotifAnimation() && isShowingLayoutNotChanged` with `mShowingPublic && isShowingLayoutNotChanged`. 4/5 lines of the patched region present at android-security-16.0.0_r8.
- Android 17: not affected per the bulletin (not listed); 6/7 lines of the patched region present at android-security-17.0.0_r1.
Unknowns
- How the wrong skip condition leads to information disclosure (bulletin type ID): the material only states it 'can cause 0 content alpha'.
- Which callers set mShowingPublicInitialized back to false while leaving mShowingPublic true, making the new skip path reachable.
- Body of animateShowingPublic() and resetAllContentAlphas(), and whether the Flags.skipHideSensitiveNotifAnimation() declaration was removed on Android 16.
- Which line of the patched region the per-major check counts as missing on each branch.
- Why Android 17 is not listed by the bulletin.