CVE-2025-26449 AI-generated analysis
platform/frameworks/base, NotificationManagerService / Zen mode (ZenModeConfig, AutomaticZenRule, INotificationManager)
Changes Zen-mode rule parceling in frameworks/base so that the automatic rules inside ZenModeConfig and the INotificationManager.getZenRules() return value are carried in a ParceledListSlice instead of an inline typed array/List, and switches the string members of ZenModeConfig.ZenRule and AutomaticZenRule (and the rule id array) to String8 encoding.
Root cause
Before the fix, ZenModeConfig.writeToParcel serialized every entry of automaticRules inline (dest.writeStringArray(ids) plus dest.writeTypedArray(rules, 0)) and ZenModeConfig(Parcel) read them back with readStringArray/readTypedArray, so the whole rule set was written into a single parcel. NotificationManagerService.getZenRules() returned a plain List<ZenModeConfig.ZenRule> over Binder. String members of ZenRule (name, id, enabler, pkg) and AutomaticZenRule (name, mPkg) were written with writeString. The bulletin classifies the issue as DoS; the exact failure mechanism is inferred from the fix, not shown in the material.
How the fix works
- core/java/android/service/notification/ZenModeConfig.java: writeToParcel now builds an ArrayList<ZenRule> and writes dest.writeString8Array(ids) followed by dest.writeParcelable(new ParceledListSlice<>(rules), flags) after the length int, instead of writeStringArray/writeTypedArray.
- core/java/android/service/notification/ZenModeConfig.java: the ZenModeConfig(Parcel) constructor reads ids with readString8Array, reads a ParceledListSlice via source.readParcelable(ZenRule.class.getClassLoader(), ParceledListSlice.class), tolerates a null slice (empty list), and if the slice size differs from the declared len it logs Slog.wtf and sets len = 0 so no rules are inserted; otherwise entries are cast to ZenRule and put into automaticRules.
- core/java/android/service/notification/ZenModeConfig.java ZenRule(Parcel)/ZenRule.writeToParcel: name, id, enabler and pkg switched from readString/writeString to readString8/writeString8.
- core/java/android/app/AutomaticZenRule.java AutomaticZenRule(Parcel)/writeToParcel: name and mPkg switched to readString8/writeString8.
- core/java/android/app/INotificationManager.aidl: getZenRules() return type changed from List<ZenModeConfig.ZenRule> to ParceledListSlice<ZenModeConfig.ZenRule>.
- services/core/java/com/android/server/notification/NotificationManagerService.java getZenRules(): still calls enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRules") and now wraps mZenModeHelper.getZenRules() in new ParceledListSlice<ZenModeConfig.ZenRule>(...).
- core/java/android/app/NotificationManager.java getAutomaticZenRules(): consumes the ParceledListSlice and iterates rules.getList() to build the Map<String, AutomaticZenRule>.
- services/tests/uiservicestests/.../ZenModeConfigTest.java: adds testWriteConfigToParcel, a round-trip of a ZenModeConfig with two automatic rules through Parcel asserting equality.
Completeness
- Covers the automaticRules map in ZenModeConfig parceling and the getZenRules() Binder return path; on Android 15/16 the same treatment is also applied to deletedRules (writeRulesToParcel/readRulesFromParcel) and to the Map-returning getAutomaticZenRules() via a new AutomaticZenRule.AzrWithId parcelable.
- The reader drops all rules (Slog.wtf) when the slice size differs from the declared count rather than throwing; a null slice is treated as empty.
- manualRule in ZenModeConfig is still written with a single dest.writeParcelable(manualRule, 0); it is a single rule and is not changed.
- Strings in nested parcelables referenced from rules (ZenPolicy, Condition, Uri, ComponentName) are not touched by this change; only ZenRule and AutomaticZenRule string members move to String8.
- getZenModeConfig() in INotificationManager still returns a ZenModeConfig directly (restricted by enforceSystemOrSystemUI); it is affected only through the ZenModeConfig parcel format change.
- No cap on the number or size of rules is introduced; the fix changes transport encoding, not the amount of data stored.
Behavior changes
- ZenModeConfig's parcel wire format changes (String8 arrays, ParceledListSlice for rules); writer and reader must both be patched to interoperate.
- INotificationManager.getZenRules() AIDL signature changes to ParceledListSlice<ZenModeConfig.ZenRule>; any out-of-tree caller or implementer of this hidden interface must be updated.
- If the deserialized rule count does not match the declared count, all automatic rules are discarded from that ZenModeConfig instance with a Slog.wtf log.
- AutomaticZenRule and ZenModeConfig.ZenRule string fields are encoded as String8 (UTF-8) on the wire.
- On Android 15 the getZenRules() enforcePolicyAccess label changed from "getZenRules" to "getAutomaticZenRules".
Per major
- Android 14: adapted fix: commit e3c4ba3c7963 in android-security-14.0.0_r21 (cherry-picked from 3965123f4403 and a35b3ddd3893). Only getZenRules() and the automaticRules parcel path are changed in 14 (no getAutomaticZenRules Binder method in the 14 AIDL context, no deletedRules, no AzrWithId); 7/8 patched lines present at android-security-14.0.0_r29.
- Android 15: adapted fix: commit e0a1e45a3d20 in android-security-15.0.0_r9. Additionally adds AutomaticZenRule.AzrWithId (declared in AutomaticZenRule.aidl), changes getAutomaticZenRules() to ParceledListSlice<AzrWithId> under Flags.modesApi(), applies ParceledListSlice to both automaticRules and deletedRules via writeRulesToParcel/readRulesFromParcel, and converts the flag-gated String fields (mTriggerDescription, iconResName, triggerDescription); 7/8 lines present at android-security-15.0.0_r17.
- Android 16: base fix: commit 3965123f4403 in android-16.0.0_r2. getZenRules() is not present in the 16 AIDL; getAutomaticZenRules() returns a raw ParceledListSlice of AzrWithId; the modesApi flag checks are absent from these parcel paths (modesUi checks remain); 7/8 lines present at android-security-16.0.0_r8.
- Android 17: not affected (not listed by the bulletin); 6/8 lines of the patched region present at android-security-17.0.0_r1.
Unknowns
- The material does not state the concrete failure mode (which process fails, which exception) or any size threshold.
- Why the String8 switch is part of the security fix is not explained in the material.
- Whether any limit on the number of automatic zen rules per package or per user exists in these branches is not shown.
- The index results labelled Android 14 contain Flags.modesApi code that does not match the 14 security-branch diff, so the index snapshot's branch is uncertain.