CVE-2025-22433 AI-generated analysis
platform/frameworks/base (core/java/com/android/internal/app/IntentForwarderActivity.java) and platform/packages/modules/IntentResolver (java/src/com/android/intentresolver/IntentForwarderActivity.java), cross-profile intent forwarding
IntentForwarderActivity.canForward now runs the cross-profile forwarding check (ACTION_CHOOSER rejection plus IPackageManager.canForwardTo) on both the forwarded intent and its selector, instead of checking only the selector when one is present.
Root cause
In both copies of IntentForwarderActivity.canForward(), forwardIntent was sanitized, and the ACTION_CHOOSER rejection was applied to forwardIntent. When forwardIntent.getSelector() was non-null, however, the local intentToCheck was replaced by the selector, and only the selector was type-resolved and passed to packageManager.canForwardTo(). The underlying intent, which is what is actually forwarded (the method returns forwardIntent), therefore never went through the cross-profile forwarding policy check itself. Per the commit message, an intent that would not be allowed could be given a different selector and launched across profiles.
How the fix works
- platform/frameworks/base core/java/com/android/internal/app/IntentForwarderActivity.java: a new private static helper canForwardInner(Intent, sourceUserId, targetUserId, IPackageManager, ContentResolver) holds the per-intent check. It returns false if the action is Intent.ACTION_CHOOSER. Otherwise it computes resolvedType via intent.resolveTypeIfNeeded(contentResolver) and returns the result of packageManager.canForwardTo(intent, resolvedType, sourceUserId, targetUserId). A RemoteException is logged and treated as false.
- canForward() now first calls canForwardInner(forwardIntent, ...) and returns null on failure. Then, if forwardIntent.getSelector() != null, it calls sanitizeIntent() on the selector and canForwardInner(selector, ...), returning null on failure. forwardIntent is returned only when both checks pass. The old intentToCheck substitution is removed.
- platform/packages/modules/IntentResolver java/src/com/android/intentresolver/IntentForwarderActivity.java: the same logic is applied to the module copy of canForward()/canForwardInner(). In addition, canForward changes from package-private to public static.
- Net effect: the ACTION_CHOOSER rejection now also applies to the selector, and the canForwardTo policy check now also applies to the underlying intent, each with its own resolved MIME type.
Completeness
- On Android 14, both the framework copy (frameworks/base) and the module copy (packages/modules/IntentResolver) of IntentForwarderActivity.canForward are patched with the same logic.
- The check covers the forwarded intent and one level of selector. The material does not show whether a selector can carry its own selector, or whether that would be checked.
- In the fix commit the selector goes through sanitizeIntent() before it is checked. In the code shown at android-security-14.0.0_r29, the sanitizeIntent(forwardIntent.getSelector()) line is gone, and canForward/canForwardInner take a single precomputed resolvedType String instead of a ContentResolver. The code in the latest tag therefore differs from the fix diff (7/8 patched lines present per the per-major facts), but the dual-check structure is present.
- The fix does not change other callers of canForward or the surrounding helpers (isDialerIntent, isTextMessageIntent, isTargetResolverOrChooserActivity).
Behavior changes
- Intents with a selector are now rejected (canForward returns null) if the underlying intent fails packageManager.canForwardTo or has action ACTION_CHOOSER, even when the selector would pass.
- Selector intents with action ACTION_CHOOSER are now rejected. Previously only the outer intent was checked for ACTION_CHOOSER.
- In the fix commit the MIME type is resolved separately for the outer intent and for the selector (two resolveTypeIfNeeded calls).
- In the IntentResolver copy, canForward becomes a public static method.
Per major
- Android 14: Same fix, two commits in android-security-14.0.0_r19: platform/frameworks/base d2216fc62a9e and platform/packages/modules/IntentResolver 20cef18d8d9b. At android-security-14.0.0_r29 the canForward/canForwardInner structure is present in both files, but it takes a resolvedType String instead of a ContentResolver and has no sanitizeIntent(selector) call (7/8 patched lines present).
- Android 15: Same fix in platform/frameworks/base 8612a293fb4a in android-security-15.0.0_r7: identical diff, hunk at line 586. At android-security-15.0.0_r17, 3/8 patched lines of the region are present; the code there is not shown. The material does not show whether an IntentResolver module commit also exists on 15.
- Android 16: Not affected: the bulletin does not list Android 16. At android-security-16.0.0_r8, 3/8 lines of the patched region are present; the code is not shown.
- Android 17: Not affected: the bulletin does not list Android 17. At android-security-17.0.0_r1, 3/8 lines of the patched region are present; the code is not shown.
Unknowns
- Which actions/types canForwardTo denies in practice, and the concrete intent shape used in the report.
- Whether the IntentResolver module copy was separately fixed on Android 15, and what the regions look like on 15 (latest tag), 16 and 17.
- Why the later code dropped sanitizeIntent(selector) and switched to a resolvedType parameter (the follow-up change is not in the material).