CVE-2026-0091 AI-generated analysis
platform/frameworks/base, WindowManager transitions (ActivityTaskManagerService, TransitionController, WindowOrganizerController) and WM Shell transition handlers
WM core now records the remote-animation delegate process on the server-side Transition, strips the IApplicationThread from the RemoteTransition it sends to shell, and changes IActivityTaskManager.setRunningRemoteTransitionDelegate to take a transition token instead of an IApplicationThread.
Root cause
Before the fix, TransitionController.requestStartTransition put the full RemoteTransition, including its IApplicationThread (getAppThread()), into the TransitionRequestInfo it sent to the transition player. Shell then passed an IApplicationThread back into ActivityTaskManagerService.setRunningRemoteTransitionDelegate(IApplicationThread delegate), which used it to look up a WindowProcessController and mark that process as running a remote transition (mRemotePlayer.reportRunning / update). So system_server sent an IApplicationThread token out of WMCore, and the delegate process was whatever IApplicationThread the caller supplied, not one WM had recorded for the transition. The commit message says the change "removes the need to send an IApplicationThread token out of WMCore", and the test is named testActivityManager_stripsAppThreadFromRemoteTransition.
How the fix works
- Transition.java: adds the package-private field IApplicationThread mRemoteDelegate, which tracks the process that will play the animation.
- TransitionController.requestStartTransition: when remoteTransition != null, stores remoteTransition.getAppThread() in transition.mRemoteDelegate and sends shell a new RemoteTransition(getRemoteTransition(), getDebugName()) with no app thread. setRemoteAnimationApp still gets the original app thread on the server side.
- WindowContainerTransaction: adds setAnimationDelegate(IBinder) and a new hierarchy op, HIERARCHY_OP_TYPE_SET_ANIMATION_DELEGATE (18 on 14/17, 19 on 15, 27 on 16), plus a type-to-string case and a toString case.
- WindowOrganizerController (hierarchy-op switch): the new op reads the binder (hop.getNewParent() on 14/17, hop.getInsetsFrameOwner() on 15/16) and sets transition.mRemoteDelegate = IApplicationThread.Stub.asInterface(binder). If there is no transition or no binder, it logs an error and skips.
- IActivityTaskManager.aidl / ActivityTaskManagerService.setRunningRemoteTransitionDelegate: the parameter is now IBinder transitionToken. The delegate comes from Transition.fromBinder(token).mRemoteDelegate, and the method returns early if fromBinder returns null. The existing CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS check and the check that the caller is itself running a remote transition stay in place.
- Shell: Transitions.setRunningRemoteTransitionDelegate takes the transition token. OneShotRemoteHandler, RemoteTransitionHandler and RecentsTransitionHandler pass `transition`. RecentsTransitionHandler.startRecentsTransition and SplitScreenTransitions.startEnterTransition call wct.setAnimationDelegate(appThread.asBinder()). The StageCoordinator boost calls that used adapter.getCallingApplication() are removed.
Completeness
- setRunningRemoteTransitionDelegate no longer accepts a process token from the caller. It only boosts the delegate WM recorded on the Transition the token resolves to.
- The IApplicationThread is stripped only from the RemoteTransition in the TransitionRequestInfo that requestStartTransition builds. The material does not show whether any other path sends RemoteTransition app threads to the player.
- HIERARCHY_OP_TYPE_SET_ANIMATION_DELEGATE lets whoever applies a WindowContainerTransaction tied to a transition set mRemoteDelegate to any binder. The diff adds no extra check on that op, so it relies on the existing permission checks of the WindowOrganizerController apply path (not shown).
- In the StageCoordinator paths that run a RemoteAnimationAdapter runner, the priority boost is removed, not re-plumbed.
Behavior changes
- AIDL signature change: IActivityTaskManager.setRunningRemoteTransitionDelegate(IBinder) replaces (IApplicationThread). Callers built against the old signature break.
- The RemoteTransition in TransitionRequestInfo, as seen by the shell/player, no longer carries an app thread.
- If the token does not resolve to a Transition (Transition.fromBinder returns null), setRunningRemoteTransitionDelegate returns silently.
- The StageCoordinator RemoteAnimationAdapter paths no longer boost the animating process.
Per major
- Android 14: Fixed by caffcf6be82b in android-security-14.0.0_r28. The new hop constant is 18 and the binder is carried in mReparent (setReparentContainer/getNewParent).
- Android 15: Adapted fix f7488ad342c2 in android-security-15.0.0_r16. The constant is 19 (18 is taken by MOVE_PIP_ACTIVITY_TO_PINNED_TASK) and the binder is carried via setInsetsFrameOwner/getInsetsFrameOwner. Two OneShotRemoteHandler call sites are updated, and RemoteTransitionHandler calls the boost after startAnimation.
- Android 16: Adapted fix 40c30bb5613e in android-security-16.0.0_r7. The constant is 27 and is added to the @IntDef. The binder goes via insetsFrameOwner, and the transition is read from chain.mTransition. RecentsTransitionHandler passes appThread into startRealRecentsTransition, and the mAnimApp assignment is removed. StageCoordinator is not touched in this diff.
- Android 17: a29de493b054 in android-17.0.0_r1. The changes are the same as the 14 patch, with the same blob hashes for most files.
Unknowns
- The exact security impact and attacker model behind the High EoP rating.
- Which permission gates applying a WindowContainerTransaction with SET_ANIMATION_DELEGATE.
- Whether other code paths still send RemoteTransition app threads outside system_server.
- Whether Transition.fromBinder returns null for finished transitions.
- Whether the StageCoordinator paths whose boost was removed are used with shell transitions.
- Content of the test ActivityManagerTest#testActivityManager_stripsAppThreadFromRemoteTransition.