CVE-2024-0020 AI-generated analysis
platform/packages/apps/Settings, ringtone/notification sound preferences (RingtonePreference, DefaultRingtonePreference, NotificationSoundPreference)
Adds a shared RingtonePreference.isValidRingtoneUri() check that rejects picked ringtone content URIs whose embedded userId (content://{userId}@...) belongs to a user that is not a permitted profile of the current user, and applies it in DefaultRingtonePreference.onSaveRingtone and NotificationSoundPreference.onActivityResult.
Root cause
Ringtone URIs returned from the ringtone picker were accepted with too little validation. DefaultRingtonePreference.onSaveRingtone only resolved the mime type through mUserContext.getContentResolver().getType() and checked that it was audio/* or application/ogg. It never looked at the userId encoded in the URI (content://{userId}@...), so a URI pointing at content of an unrelated user could be stored through RingtoneManager.setActualDefaultRingtoneUri. NotificationSoundPreference.onActivityResult did no validation at all before setRingtone(uri) and callChangeListener(uri). The bulletin classifies the result as information disclosure (ID), High.
How the fix works
- src/com/android/settings/RingtonePreference.java: adds a public isDefaultRingtone(Uri) (null or RingtoneManager.isDefault) and a protected isValidRingtoneUri(Uri). isValidRingtoneUri accepts default URIs and android.resource scheme URIs early. Otherwise it requires a non-null mime type from mUserContext.getContentResolver().getType() that is audio/*, application/ogg or application/x-flac.
- RingtonePreference.isValidRingtoneUri then extracts the user via ContentProvider.getUserIdFromUri(ringtoneUri, mUserId). If that differs from mUserId, it requires, via UserManager: isSameProfileGroup(mUserId, userIdFromUri); that getProfileParent of the profile side (chosen by userManager.isProfile()) equals the parent side; that the parent has no DISALLOW_SHARE_INTO_MANAGED_PROFILE restriction; and that the profile is a managed profile or has UserProperties.isMediaSharedWithParent(). Any failure logs and returns false.
- src/com/android/settings/DefaultRingtonePreference.java:onSaveRingtone: the inline mime-type checks are replaced by a single isValidRingtoneUri(ringtoneUri) call. On failure the URI is ignored and setActualDefaultRingtoneUri is not called. The null-URI early path (silence) is unchanged.
- src/com/android/settings/notification/app/NotificationSoundPreference.java:onActivityResult: the picked EXTRA_RINGTONE_PICKED_URI must now pass isValidRingtoneUri(uri) before setRingtone/callChangeListener. Invalid URIs are logged and dropped.
- tests/unit/src/com/android/settings/DefaultRingtonePreferenceTest.java: adds cases for an other-user URI not in the same profile group, a non-managed profile, a managed profile (allowed) and a default URI (allowed).
Completeness
- Covers both RingtonePreference subclasses shown in the material: DefaultRingtonePreference and NotificationSoundPreference.
- The userId check runs only after the mime-type check succeeds. android.resource URIs and default URIs return true before either check, so they are never compared on userId.
- Cross-user URIs are still accepted for managed profiles and for profiles with isMediaSharedWithParent, unless DISALLOW_SHARE_INTO_MANAGED_PROFILE is set on the parent. This is a deliberate allow-list rather than a blanket rejection.
- The null-URI path in DefaultRingtonePreference.onSaveRingtone skips validation by design (silence).
- The check lives only in the Settings app. It does not change RingtoneManager or the ringtone picker. The material does not show whether other callers of RingtoneManager.setActualDefaultRingtoneUri exist outside these two preferences.
Behavior changes
- Picked ringtone URIs that reference a different user which is not a managed or media-sharing profile in the same profile group are now ignored with only a Log.e. This applies to default ringtones and to notification sounds.
- application/x-flac is now accepted as a ringtone mime type (previously only audio/* and application/ogg).
- android.resource URIs are now accepted without a getType() lookup. Previously, a null mime type for such a URI caused rejection in DefaultRingtonePreference.
- NotificationSoundPreference.onActivityResult now validates mime type and user. Previously it accepted any URI, so non-audio or unresolvable URIs are now dropped there too.
- Any cross-user URI between a parent and its profile is refused when DISALLOW_SHARE_INTO_MANAGED_PROFILE is set on the parent user.
Per major
- Android 14: same fix: commit 460d0b5a63d1 in android-security-14.0.0_r4 (cherry-pick, Change-Id Ib266b285a3a1c6c5265ae2321159e61e08e349f6). At android-security-14.0.0_r29 the code context shows isValidRingtoneUri in use in onSaveRingtone, with 5/8 lines of the patched region present.
- Android 15: not affected per the bulletin. android-security-15.0.0_r17 has 5/8 lines of the patched region present.
- Android 16: not affected per the bulletin. android-security-16.0.0_r8 has 5/8 lines of the patched region present.
- Android 17: not affected per the bulletin. android-security-17.0.0_r1 has 5/8 lines of the patched region present.
Unknowns
- The concrete information-disclosure consequence of persisting a cross-user ringtone URI (which component later reads the other user's content) is not shown in the material.
- Whether any other RingtonePreference subclass or other Settings code path persists picked ringtone URIs without calling isValidRingtoneUri.
- How the fix was adapted for Android 11, 12, 12L and 13, which the bulletin lists as affected but for which no diffs are provided, including whether UserProperties is available there.
- Why only 5/8 lines of the patched region match at the latest tags of 14 through 17.