CVE-2025-32326 AI-generated analysis
platform/packages/apps/Settings, users / AppRestrictionsFragment (app restrictions UI)
In Settings' AppRestrictionsFragment, the custom restrictions Intent supplied by an app is now launched only as a copy pinned to the component and package that the safety check resolved, instead of relaunching a copy of the original Intent after vetting.
Root cause
RestrictionsResultReceiver.onReceive reads an Intent from the broadcast result extras (results.getParcelable(Intent.EXTRA_RESTRICTIONS_INTENT)). assertSafeToStartCustomActivity resolved it with mPackageManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) and threw a SecurityException if the resolved activity was not allowed, but it returned void. The caller then called startActivityForResult(new Intent(restrictionsIntent), ...) with a copy of the original Intent. Nothing tied what was launched to the ResolveInfo that passed the check.
How the fix works
- src/com/android/settings/users/AppRestrictionsFragment.java : RestrictionsResultReceiver.assertSafeToStartCustomActivity now returns an Intent. After the existing resolveActivity call and the existing check pass, it builds vettedIntent = new Intent(intent), calls vettedIntent.setComponent(activityInfo.getComponentName()) and vettedIntent.setPackage(activityInfo.packageName), and returns it.
- src/com/android/settings/users/AppRestrictionsFragment.java : RestrictionsResultReceiver.onReceive stores the return value in a final Intent vettedIntent assigned inside the try block and passes vettedIntent to AppRestrictionsFragment.this.startActivityForResult instead of new Intent(restrictionsIntent). The existing catch of ActivityNotFoundException | SecurityException (log, EventLog 200688991, return) is unchanged.
Completeness
- Covers the EXTRA_RESTRICTIONS_INTENT launch in AppRestrictionsFragment.RestrictionsResultReceiver.onReceive, the only launch site shown in the material. The launched Intent's component and package are set from the vetted ActivityInfo.
- Other fields of the supplied Intent (action, data, extras, flags) are still copied into vettedIntent. The fix pins the target and does not sanitize the rest of the Intent.
- The resolve step (MATCH_DEFAULT_ONLY) and the existing check are unchanged by this fix (the check's body is not fully shown in the material).
- In the code shown at android-security-14.0.0_r29, 'final Intent vettedIntent = new Intent(intent);' sits at the top of assertSafeToStartCustomActivity (line 672). This layout differs from this commit.
Behavior changes
- The custom restrictions activity is now started with an explicit ComponentName and package taken from the vetted resolution.
- No new error paths: failures still go through the existing ActivityNotFoundException/SecurityException catch, which logs and returns without calling startActivity.
Per major
- Android 14: same fix: commit 33a1cbf7bd0e in android-security-14.0.0_r22 (cherry-pick of d3e34060803c); 8/8 patched lines present at android-security-14.0.0_r29
- Android 15: same fix: commit e8a17485771e in android-security-15.0.0_r10, same diff content; 8/8 lines present at android-security-15.0.0_r17
- Android 16: same fix: commit e12156e2d805 in android-security-16.0.0_r1, same diff content; 8/8 lines present at android-security-16.0.0_r8
- Android 17: not affected (not listed by the bulletin); patched region present (8/8 lines) at android-security-17.0.0_r1
Unknowns
- The full body of the pre-existing check in assertSafeToStartCustomActivity.
- The concrete way the launched Intent could target something other than what was vetted.
- Why the code layout at android-security-14.0.0_r29 differs from this commit (whether a follow-up change exists is not shown).
- The contents of bugs 353680402 and 365739560.