CVE-2025-0089 AI-generated analysis
platform/frameworks/base, WindowManager/ActivityTaskManager activity start (services/core/java/com/android/server/wm)
Adds a home-intent normalization step to ActivityStartInterceptor. ACTION_MAIN intents that carry CATEGORY_HOME/CATEGORY_SECONDARY_HOME in a non-standard format (extra categories, data/type, or an explicit component naming the default home) are rewritten into the canonical home intent and re-resolved before the activity is started.
Root cause
ActivityRecord.isHomeIntent only treats an intent as a home intent when it is ACTION_MAIN with exactly one category (HOME or SECONDARY_HOME) and no data/type. ActivityStarter had no step that put home-category intents into canonical form before they reached that check. So an intent such as ACTION_MAIN + CATEGORY_HOME + CATEGORY_LAUNCHER, one with data/type, or one with the default home component set explicitly was not classified as a home intent. Per the commit message, this let home activities be started as a standard-type activity.
How the fix works
- services/core/java/com/android/server/wm/ActivityRecord.java: isHomeIntent(Intent) changed from private to package-private static, so the interceptor and tests can reuse the canonical home-intent definition.
- services/core/java/com/android/server/wm/ActivityStarter.java: the mInterceptor.intercept(...) call now passes request.componentSpecified, so the interceptor knows whether the caller set an explicit component.
- services/core/java/com/android/server/wm/ActivityStartInterceptor.java: intercept(...) gains a componentSpecified parameter, stored in the new field mComponentSpecified. A 9-arg overload that delegates with false is kept for tests. The new interceptHomeIfNeeded() is called after interceptLockedManagedProfileIfNeeded() and before the ActivityInterceptorCallback loop. If it returns true, intercept() returns true immediately.
- ActivityStartInterceptor.interceptHomeIfNeeded(): returns false if mService.mRootWindowContainer is null, or if the intent is not ACTION_MAIN with CATEGORY_HOME or CATEGORY_SECONDARY_HOME. If mComponentSpecified is set, it resolves the default home via mService.getHomeIntent() and mRootWindowContainer.resolveHomeActivity(mUserId, homeIntent), and returns false unless the intent's component equals that default home component. Then, if !ActivityRecord.isHomeIntent(mIntent) || mComponentSpecified, it calls normalizeHomeIntent() and marks the start as intercepted. When intercepted, it sets mCallingPid/mCallingUid to mRealCallingPid/mRealCallingUid and clears mResolvedType. It then re-resolves mRInfo via mSupervisor.resolveIntent(mIntent, null, mUserId, 0, mRealCallingUid, mRealCallingPid) and mAInfo via mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null).
- ActivityStartInterceptor.normalizeHomeIntent(): if there is more than one category, it removes all categories and re-adds only CATEGORY_SECONDARY_HOME (if it was present) or else CATEGORY_HOME. If type or data is set, it calls mIntent.setType(null) (Intent.setType also clears data (general platform knowledge)). If mComponentSpecified is set, it calls mIntent.setComponent(null). It always adds FLAG_ACTIVITY_NEW_TASK.
- services/tests/wmtests/.../ActivityStartInterceptorTest.java: the new testInterceptIncorrectHomeIntent checks that ACTION_MAIN + HOME + LAUNCHER is intercepted and afterwards satisfies ActivityRecord.isHomeIntent.
Completeness
- Covers home-category intents (HOME or SECONDARY_HOME) that go through the ActivityStarter interceptor with no explicit component. Also covers intents whose explicit component equals the resolved default home component for mUserId.
- Intents with an explicit component that names a home activity other than the resolved default home are left untouched (interceptHomeIfNeeded returns false, per the code comment 'Do nothing if the intent is not for the default home component'), so they still go through the standard path.
- The normalization only runs if the earlier interceptors in intercept() (such as quiet profile and locked managed profile, as shown in the diff) have not already intercepted the start. When normalization happens, intercept() returns true immediately, so the ActivityInterceptorCallback loop is not reached for that start.
- Normalization is skipped entirely when mService.mRootWindowContainer is null.
- interceptHomeIfNeeded dereferences the ActivityInfo returned by resolveHomeActivity without a null check. The material does not show whether that method can return null.
Behavior changes
- Home-category intents with extra categories, data/type, or an explicit default-home component are rewritten in place: categories reduced to a single HOME/SECONDARY_HOME, type (and with it data) cleared, component cleared, FLAG_ACTIVITY_NEW_TASK added.
- When normalized, the start is treated as intercepted. callingPid/callingUid are replaced by the real calling pid/uid, resolvedType is cleared, and the intent is re-resolved with flags 0. Per existing ActivityStarter logic, intentGrants is set to null, so no grants from the original intent are applied.
- New Slog.w/Slog.d log lines under tag ActivityStartInterceptor when a home intent is normalized.
- ActivityStartInterceptor.intercept gains a boolean componentSpecified parameter. The old 9-arg overload is kept, for tests only.
Per major
- Android 14: same fix: commit ee0d85dd3f50 (RESTRICT AUTOMERGE cherry-pick, Change-Id I300f705798909014329f92118c266dbc04173e05) landed in android-security-14.0.0_r20. At android-security-14.0.0_r29, the code context shows the new imports, static isHomeIntent, the componentSpecified argument in ActivityStarter and the new test. 7/8 lines of the patched region are present.
- Android 15: resolved per bug_id at android-security-15.0.0_r10, and 7/8 lines of the patched region are present at android-security-15.0.0_r17. However, the Android 15 diff supplied in the material (f27918b39cff, mStrictModeCallbacks locking in ActivityManagerService) does not touch the home-intent code, so the exact Android 15 commit for this change is not shown.
- Android 16: not affected (the bulletin does not list Android 16). 7/8 lines of the patched region are present at android-security-16.0.0_r8.
- Android 17: not affected (the bulletin does not list Android 17). 7/8 lines of the patched region are present at android-security-17.0.0_r1.
Unknowns
- Which home-specific checks were bypassed in practice when a home activity ran as a standard-type activity. The material only gives the outcome, from the commit message.
- Whether RootWindowContainer.resolveHomeActivity can return null in the path used by interceptHomeIfNeeded.
- Where the suspended-package and lock-task interceptors sit relative to interceptHomeIfNeeded inside intercept().
- Which of the 8 patched-region lines is missing in each latest security tag (7/8 present), and whether it matters functionally.
- The actual Android 15 commit carrying this change (the supplied Android 15 diff is a different change).