CVE-2024-49735 AI-generated analysis
platform/frameworks/base, NotificationChannel / VibrationEffect (core/java/android/app, core/java/android/os)
Caps the size of vibration data stored on a NotificationChannel by truncating vibration patterns to 500 entries, cropping non-repeating composed VibrationEffects (new VibrationEffect.cropToLengthOrNull) or dropping effects whose serialized form exceeds 32,768 characters, and no longer serializing a VibrationEffect to XML when an equivalent pattern already exists.
Root cause
NotificationChannel accepted a VibrationEffect (behind Flags.notificationChannelVibrationEffectApi) from the Parcel constructor and from setVibrationEffect() without any size limit, and setVibrationPattern() stored the raw long[] without truncating it in the setter. MAX_VIBRATION_LENGTH (1000) was not applied to the effect, and writeXml() always serialized the effect with vibrationToString() even when an equivalent vibration pattern was also written. Per the commit message, serialized vibration effects take much more space than the pattern array, so an app-supplied channel could carry a very large vibration effect into notification channel state that is written to XML.
How the fix works
- core/java/android/os/VibrationEffect.java: adds abstract @hide cropToLengthOrNull(int length). VibrationEffect.Composed implements it: returns null if mRepeatIndex >= 0 (repeating effects are never cropped), returns this if mSegments.size() <= length, otherwise builds a new Composed from mSegments.subList(0, length) with the same repeat index, calls validate(), and returns null if validation throws IllegalArgumentException.
- core/java/android/app/NotificationChannel.java: MAX_VIBRATION_LENGTH lowered from 1000 to 500; new MAX_SERIALIZED_VIBRATION_LENGTH = 32_768.
- NotificationChannel.java:getTrimmedVibrationEffect(effect) (new private helper): calls effect.cropToLengthOrNull(MAX_VIBRATION_LENGTH), uses the cropped result if non-null, then returns null if vibrationToString(result).length() exceeds MAX_SERIALIZED_VIBRATION_LENGTH, else returns the (possibly cropped) effect.
- NotificationChannel.java:NotificationChannel(Parcel) constructor: after reading mVibrationEffect from the parcel (flag-gated path), passes it through getTrimmedVibrationEffect so oversized effects arriving over Binder are cropped or dropped.
- NotificationChannel.java:setVibrationPattern(long[]): if the array is longer than MAX_VIBRATION_LENGTH, stores Arrays.copyOf(vibrationPattern, MAX_VIBRATION_LENGTH); the subsequent flag-gated conversion to mVibrationEffect therefore operates on the truncated pattern.
- NotificationChannel.java:setVibrationEffect(effect): when the effect converts to a pattern via computeCreateWaveformOffOnTimingsOrNull() and that pattern exceeds MAX_VIBRATION_LENGTH, delegates to setVibrationPattern(pattern) so both pattern and effect are regenerated from the truncated array; when it does not convert to a pattern, stores getTrimmedVibrationEffect(mVibrationEffect) and sets mVibrationPattern to null; a null effect clears both.
- NotificationChannel.java:writeXml: the ATT_VIBRATION_EFFECT attribute is only written when getVibrationPattern() == null, so an effect that has an equivalent pattern is no longer serialized twice. Comment in the XML restore path updated accordingly (restore still calls setVibrationEffect, which now trims).
- Tests: NotificationChannelTest.testLongVibrationFields_canWriteToXml checks a 32,775-entry pattern is cut to MAX_VIBRATION_LENGTH through parcel and XML round trips; VibrationEffectTest adds cropToLength_* cases for waveform, amplitude waveform, composed, and repeating effects.
- platform/build/release commits 2283b66e8a4d and 3eae801ea3d0: add then remove the android.app.notif_channel_crop_vibration_effects flag value in RELEASE_CONFIG_TRUNKFOOD_STAGING. Net effect is nil; the frameworks/base security backport inlines the flag (per commit message), and the flag file is not present at android-security-15.0.0_r17.
Completeness
- All three entry points that populate the vibration fields are covered: Parcel constructor (effect), setVibrationPattern (pattern, and derived effect), setVibrationEffect (effect and derived pattern). The XML restore path reaches the setters and is covered indirectly.
- cropToLengthOrNull is implemented only for VibrationEffect.Composed and deliberately returns null for repeating effects; such effects are not cropped. They are still bounded by the MAX_SERIALIZED_VIBRATION_LENGTH check in getTrimmedVibrationEffect, which drops the effect entirely (returns null) if its serialized form exceeds 32,768 characters.
- The pattern-length truncation in the Parcel constructor for mVibrationPattern itself is not in this diff; the pre-existing test testLongAlertFields already expected MAX_VIBRATION_LENGTH after unparceling, so that path is handled by code not shown here.
- When getTrimmedVibrationEffect drops an effect in setVibrationEffect, mVibrationEnabled was already set to true earlier in the method and is not reset; the diff does not adjust that.
- The diff contains no check of the new bugfix flag (inlined for the security backport), so the new truncation is unconditional for the pattern path; the effect paths remain gated by the pre-existing Flags.notificationChannelVibrationEffectApi.
Behavior changes
- Vibration patterns on a NotificationChannel are now truncated to 500 entries (previously the limit constant was 1000, and setVibrationPattern did not truncate).
- Non-repeating composed VibrationEffects with more than 500 segments are silently cropped; effects whose serialized form exceeds 32,768 characters (including uncroppable repeating effects) are silently dropped (stored as null) on parcel read and, for non-pattern-convertible effects, in setVibrationEffect.
- writeXml no longer emits the vibration_effect attribute when a vibration pattern is present; on restore the effect is regenerated from the pattern via setVibrationPattern.
- setVibrationEffect with a pattern-equivalent effect longer than 500 entries now stores a regenerated, truncated effect rather than the original object.
Per major
- Android 14: Not affected per the bulletin (not listed). 5/8 lines of the patched region present at android-security-14.0.0_r29; the material does not show what differs there.
- Android 15: Resolved via frameworks/base a44beb0ca3f7 in android-security-15.0.0_r4 (cherry-pick of 1181fd1b6769, flag inlined for the security backport). At android-security-15.0.0_r17 the constants (500 / 32_768) and cropToLengthOrNull are present; 7/8 lines of the patched region present.
- Android 16: Not affected per the bulletin (not listed). 7/8 lines of the patched region present at android-security-16.0.0_r8.
- Android 17: Not affected per the bulletin (not listed). 7/8 lines of the patched region present at android-security-17.0.0_r1.
Unknowns
- The concrete impact of an oversized persisted vibration effect (e.g. on notification policy persistence in the system server) is not shown in the material; only the bulletin rating (High, EoP) is known.
- How mVibrationPattern is bounded when read from the Parcel (that code is outside the diff).
- Why Android 14 is not affected and which line of the patched region differs on 14, 16 and 17; the material gives only line counts.