diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index 0d68df48c316d..d5f51adc2297c 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -646,4 +646,9 @@ public abstract class ActivityManagerInternal { */ @Nullable public abstract List getIsolatedProcesses(int uid); + + /** @see ActivityManagerService#sendIntentSender */ + public abstract int sendIntentSender(IIntentSender target, IBinder allowlistToken, int code, + Intent intent, String resolvedType, + IIntentReceiver finishedReceiver, String requiredPermission, Bundle options); } diff --git a/core/java/android/window/IWindowOrganizerController.aidl b/core/java/android/window/IWindowOrganizerController.aidl index 39cdf5af50ce8..e65fcdd7b13bb 100644 --- a/core/java/android/window/IWindowOrganizerController.aidl +++ b/core/java/android/window/IWindowOrganizerController.aidl @@ -19,6 +19,7 @@ package android.window; import android.view.SurfaceControl; import android.os.IBinder; +import android.view.RemoteAnimationAdapter; import android.window.IDisplayAreaOrganizerController; import android.window.ITaskFragmentOrganizerController; import android.window.ITaskOrganizerController; @@ -60,6 +61,17 @@ interface IWindowOrganizerController { IBinder startTransition(int type, in @nullable IBinder transitionToken, in @nullable WindowContainerTransaction t); + /** + * Starts a legacy transition. + * @param type The transition type. + * @param adapter The animation to use. + * @param syncCallback A sync callback for the contents of `t` + * @param t Operations that are part of the transition. + * @return sync-id or -1 if this no-op'd because a transition is already running. + */ + int startLegacyTransition(int type, in RemoteAnimationAdapter adapter, + in IWindowContainerTransactionCallback syncCallback, in WindowContainerTransaction t); + /** * Finishes a transition. This must be called for all created transitions. * @param transitionToken Which transition to finish diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java index 8735ed841ebc3..3c360fb99f42b 100644 --- a/core/java/android/window/WindowContainerTransaction.java +++ b/core/java/android/window/WindowContainerTransaction.java @@ -19,6 +19,7 @@ package android.window; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.TestApi; +import android.app.PendingIntent; import android.app.WindowConfiguration; import android.content.Intent; import android.content.pm.ActivityInfo; @@ -388,6 +389,24 @@ public final class WindowContainerTransaction implements Parcelable { return this; } + /** + * Sends a pending intent in sync. + * @param sender The PendingIntent sender. + * @param intent The fillIn intent to patch over the sender's base intent. + * @param options bundle containing ActivityOptions for the task's top activity. + * @hide + */ + @NonNull + public WindowContainerTransaction sendPendingIntent(PendingIntent sender, Intent intent, + @Nullable Bundle options) { + mHierarchyOps.add(new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_PENDING_INTENT) + .setLaunchOptions(options) + .setPendingIntent(sender) + .setActivityIntent(intent) + .build()); + return this; + } + /** * Creates a new TaskFragment with the given options. * @param taskFragmentOptions the options used to create the TaskFragment. @@ -886,6 +905,7 @@ public final class WindowContainerTransaction implements Parcelable { public static final int HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT = 9; public static final int HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT = 10; public static final int HIERARCHY_OP_TYPE_REPARENT_CHILDREN = 11; + public static final int HIERARCHY_OP_TYPE_PENDING_INTENT = 12; // The following key(s) are for use with mLaunchOptions: // When launching a task (eg. from recents), this is the taskId to be launched. @@ -920,6 +940,9 @@ public final class WindowContainerTransaction implements Parcelable { @Nullable private TaskFragmentCreationParams mTaskFragmentCreationOptions; + @Nullable + private PendingIntent mPendingIntent; + public static HierarchyOp createForReparent( @NonNull IBinder container, @Nullable IBinder reparent, boolean toTop) { return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_REPARENT) @@ -998,6 +1021,7 @@ public final class WindowContainerTransaction implements Parcelable { mLaunchOptions = copy.mLaunchOptions; mActivityIntent = copy.mActivityIntent; mTaskFragmentCreationOptions = copy.mTaskFragmentCreationOptions; + mPendingIntent = copy.mPendingIntent; } protected HierarchyOp(Parcel in) { @@ -1010,6 +1034,7 @@ public final class WindowContainerTransaction implements Parcelable { mLaunchOptions = in.readBundle(); mActivityIntent = in.readTypedObject(Intent.CREATOR); mTaskFragmentCreationOptions = in.readTypedObject(TaskFragmentCreationParams.CREATOR); + mPendingIntent = in.readTypedObject(PendingIntent.CREATOR); } public int getType() { @@ -1062,6 +1087,11 @@ public final class WindowContainerTransaction implements Parcelable { return mTaskFragmentCreationOptions; } + @Nullable + public PendingIntent getPendingIntent() { + return mPendingIntent; + } + @Override public String toString() { switch (mType) { @@ -1117,6 +1147,7 @@ public final class WindowContainerTransaction implements Parcelable { dest.writeBundle(mLaunchOptions); dest.writeTypedObject(mActivityIntent, flags); dest.writeTypedObject(mTaskFragmentCreationOptions, flags); + dest.writeTypedObject(mPendingIntent, flags); } @Override @@ -1163,6 +1194,9 @@ public final class WindowContainerTransaction implements Parcelable { @Nullable private TaskFragmentCreationParams mTaskFragmentCreationOptions; + @Nullable + private PendingIntent mPendingIntent; + Builder(int type) { mType = type; } @@ -1202,6 +1236,11 @@ public final class WindowContainerTransaction implements Parcelable { return this; } + Builder setPendingIntent(@Nullable PendingIntent sender) { + mPendingIntent = sender; + return this; + } + Builder setTaskFragmentCreationOptions( @Nullable TaskFragmentCreationParams taskFragmentCreationOptions) { mTaskFragmentCreationOptions = taskFragmentCreationOptions; @@ -1221,6 +1260,7 @@ public final class WindowContainerTransaction implements Parcelable { hierarchyOp.mToTop = mToTop; hierarchyOp.mLaunchOptions = mLaunchOptions; hierarchyOp.mActivityIntent = mActivityIntent; + hierarchyOp.mPendingIntent = mPendingIntent; hierarchyOp.mTaskFragmentCreationOptions = mTaskFragmentCreationOptions; return hierarchyOp; diff --git a/core/java/android/window/WindowOrganizer.java b/core/java/android/window/WindowOrganizer.java index 78dbebaf27387..e9b8174567a8a 100644 --- a/core/java/android/window/WindowOrganizer.java +++ b/core/java/android/window/WindowOrganizer.java @@ -25,6 +25,7 @@ import android.app.ActivityTaskManager; import android.os.IBinder; import android.os.RemoteException; import android.util.Singleton; +import android.view.RemoteAnimationAdapter; /** * Base class for organizing specific types of windows like Tasks and DisplayAreas @@ -124,6 +125,27 @@ public class WindowOrganizer { } } + /** + * Start a legacy transition. + * @param type The type of the transition. This is ignored if a transitionToken is provided. + * @param adapter An existing transition to start. If null, a new transition is created. + * @param t The set of window operations that are part of this transition. + * @return true on success, false if a transition was already running. + * @hide + */ + @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) + @NonNull + public int startLegacyTransition(int type, @NonNull RemoteAnimationAdapter adapter, + @NonNull WindowContainerTransactionCallback syncCallback, + @NonNull WindowContainerTransaction t) { + try { + return getWindowOrganizerController().startLegacyTransition( + type, adapter, syncCallback.mInterface, t); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * Register an ITransitionPlayer to handle transition animations. * @hide diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java index e6d088e6537de..94a87583da892 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apppairs/AppPair.java @@ -187,6 +187,7 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.SplitLayou .setPosition(mTaskLeash2, mTaskInfo2.positionInParent.x, mTaskInfo2.positionInParent.y) .setPosition(dividerLeash, dividerBounds.left, dividerBounds.top) + .show(dividerLeash) .show(mRootTaskLeash) .show(mTaskLeash1) .show(mTaskLeash2); @@ -212,9 +213,12 @@ class AppPair implements ShellTaskOrganizer.TaskListener, SplitLayout.SplitLayou } mRootTaskInfo = taskInfo; - if (mSplitLayout != null - && mSplitLayout.updateConfiguration(mRootTaskInfo.configuration)) { - onBoundsChanged(mSplitLayout); + if (mSplitLayout != null) { + if (mSplitLayout.updateConfiguration(mRootTaskInfo.configuration)) { + onBoundsChanged(mSplitLayout); + } + // updateConfiguration re-inits the dividerbar, so show it now + mSyncQueue.runInSync(t -> t.show(mSplitLayout.getDividerLeash())); } } else if (taskInfo.taskId == getTaskId1()) { mTaskInfo1 = taskInfo; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java index 33beab5ee3f13..f3a8620b1693a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SyncTransactionQueue.java @@ -18,13 +18,15 @@ package com.android.wm.shell.common; import android.annotation.BinderThread; import android.annotation.NonNull; +import android.os.RemoteException; import android.util.Slog; import android.view.SurfaceControl; +import android.view.WindowManager; import android.window.WindowContainerTransaction; import android.window.WindowContainerTransactionCallback; import android.window.WindowOrganizer; -import com.android.wm.shell.common.annotations.ShellMainThread; +import com.android.wm.shell.transition.LegacyTransitions; import java.util.ArrayList; @@ -76,6 +78,21 @@ public final class SyncTransactionQueue { } } + /** + * Queues a legacy transition to be sent serially to WM + */ + public void queue(LegacyTransitions.ILegacyTransition transition, + @WindowManager.TransitionType int type, WindowContainerTransaction wct) { + SyncCallback cb = new SyncCallback(transition, type, wct); + synchronized (mQueue) { + if (DEBUG) Slog.d(TAG, "Queueing up legacy transition " + wct); + mQueue.add(cb); + if (mQueue.size() == 1) { + cb.send(); + } + } + } + /** * Queues a sync transaction only if there are already sync transaction(s) queued or in flight. * Otherwise just returns without queueing. @@ -118,12 +135,12 @@ public final class SyncTransactionQueue { // Synchronized on mQueue private void onTransactionReceived(@NonNull SurfaceControl.Transaction t) { if (DEBUG) Slog.d(TAG, " Running " + mRunnables.size() + " sync runnables"); - for (int i = 0, n = mRunnables.size(); i < n; ++i) { + final int n = mRunnables.size(); + for (int i = 0; i < n; ++i) { mRunnables.get(i).runWithTransaction(t); } - mRunnables.clear(); - t.apply(); - t.close(); + // More runnables may have been added, so only remove the ones that ran. + mRunnables.subList(0, n).clear(); } /** Task to run with transaction. */ @@ -135,20 +152,38 @@ public final class SyncTransactionQueue { private class SyncCallback extends WindowContainerTransactionCallback { int mId = -1; final WindowContainerTransaction mWCT; + final LegacyTransitions.LegacyTransition mLegacyTransition; SyncCallback(WindowContainerTransaction wct) { mWCT = wct; + mLegacyTransition = null; + } + + SyncCallback(LegacyTransitions.ILegacyTransition legacyTransition, + @WindowManager.TransitionType int type, WindowContainerTransaction wct) { + mWCT = wct; + mLegacyTransition = new LegacyTransitions.LegacyTransition(type, legacyTransition); } // Must be sychronized on mQueue void send() { + if (mInFlight == this) { + // This was probably queued up and sent during a sync runnable of the last callback. + // Don't queue it again. + return; + } if (mInFlight != null) { throw new IllegalStateException("Sync Transactions must be serialized. In Flight: " + mInFlight.mId + " - " + mInFlight.mWCT); } mInFlight = this; if (DEBUG) Slog.d(TAG, "Sending sync transaction: " + mWCT); - mId = new WindowOrganizer().applySyncTransaction(mWCT, this); + if (mLegacyTransition != null) { + mId = new WindowOrganizer().startLegacyTransition(mLegacyTransition.getType(), + mLegacyTransition.getAdapter(), this, mWCT); + } else { + mId = new WindowOrganizer().applySyncTransaction(mWCT, this); + } if (DEBUG) Slog.d(TAG, " Sent sync transaction. Got id=" + mId); mMainExecutor.executeDelayed(mOnReplyTimeout, REPLY_TIMEOUT); } @@ -169,6 +204,16 @@ public final class SyncTransactionQueue { if (DEBUG) Slog.d(TAG, "onTransactionReady id=" + mId); mQueue.remove(this); onTransactionReceived(t); + if (mLegacyTransition != null) { + try { + mLegacyTransition.getSyncCallback().onTransactionReady(mId, t); + } catch (RemoteException e) { + Slog.e(TAG, "Error sending callback to legacy transition: " + mId, e); + } + } else { + t.apply(); + t.close(); + } if (!mQueue.isEmpty()) { mQueue.get(0).send(); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java index 0cea0efc0057b..b7bbe807cf0c7 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitWindowManager.java @@ -95,7 +95,7 @@ public final class SplitWindowManager extends WindowlessWindowManager { final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession()) .setContainerLayer() .setName(TAG) - .setHidden(false) + .setHidden(true) .setCallsite("SplitWindowManager#attachToParentSurface"); mParentContainerCallbacks.attachToParentSurface(builder); mLeash = builder.build(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java index d60fa29d8f77c..aca545b4b211c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java @@ -17,14 +17,12 @@ package com.android.wm.shell.splitscreen; import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.RemoteAnimationTarget.MODE_OPENING; import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission; import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_BOTTOM_OR_RIGHT; import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_TOP_OR_LEFT; -import static com.android.wm.shell.common.split.SplitLayout.SPLIT_POSITION_UNDEFINED; import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_MAIN; -import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_SIDE; -import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED; import android.app.ActivityManager; import android.app.ActivityTaskManager; @@ -39,9 +37,13 @@ import android.os.IBinder; import android.os.RemoteException; import android.os.UserHandle; import android.util.Slog; +import android.view.IRemoteAnimationFinishedCallback; import android.view.RemoteAnimationAdapter; import android.view.RemoteAnimationTarget; +import android.view.SurfaceControl; +import android.view.WindowManager; import android.window.IRemoteTransition; +import android.window.WindowContainerTransaction; import androidx.annotation.BinderThread; import androidx.annotation.NonNull; @@ -58,6 +60,7 @@ import com.android.wm.shell.common.annotations.ExternalThread; import com.android.wm.shell.common.split.SplitLayout.SplitPosition; import com.android.wm.shell.draganddrop.DragAndDropPolicy; import com.android.wm.shell.splitscreen.ISplitScreenListener; +import com.android.wm.shell.transition.LegacyTransitions; import com.android.wm.shell.transition.Transitions; import java.io.PrintWriter; @@ -147,7 +150,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, } public void setSideStagePosition(@SplitPosition int sideStagePosition) { - mStageCoordinator.setSideStagePosition(sideStagePosition); + mStageCoordinator.setSideStagePosition(sideStagePosition, null /* wct */); } public void setSideStageVisibility(boolean visible) { @@ -185,7 +188,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, public void startTask(int taskId, @SplitScreen.StageType int stage, @SplitPosition int position, @Nullable Bundle options) { - options = resolveStartStage(stage, position, options); + options = mStageCoordinator.resolveStartStage(stage, position, options, null /* wct */); try { ActivityTaskManager.getService().startActivityFromRecents(taskId, options); @@ -197,7 +200,7 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, public void startShortcut(String packageName, String shortcutId, @SplitScreen.StageType int stage, @SplitPosition int position, @Nullable Bundle options, UserHandle user) { - options = resolveStartStage(stage, position, options); + options = mStageCoordinator.resolveStartStage(stage, position, options, null /* wct */); try { LauncherApps launcherApps = @@ -212,64 +215,61 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, public void startIntent(PendingIntent intent, Intent fillInIntent, @SplitScreen.StageType int stage, @SplitPosition int position, @Nullable Bundle options) { - options = resolveStartStage(stage, position, options); - - try { - intent.send(mContext, 0, fillInIntent, null, null, null, options); - } catch (PendingIntent.CanceledException e) { - Slog.e(TAG, "Failed to launch activity", e); + if (!Transitions.ENABLE_SHELL_TRANSITIONS) { + startIntentLegacy(intent, fillInIntent, stage, position, options); + return; } + mStageCoordinator.startIntent(intent, fillInIntent, stage, position, options, + null /* remote */); } - private Bundle resolveStartStage(@SplitScreen.StageType int stage, - @SplitPosition int position, @Nullable Bundle options) { - switch (stage) { - case STAGE_TYPE_UNDEFINED: { - // Use the stage of the specified position is valid. - if (position != SPLIT_POSITION_UNDEFINED) { - if (position == mStageCoordinator.getSideStagePosition()) { - options = resolveStartStage(STAGE_TYPE_SIDE, position, options); - } else { - options = resolveStartStage(STAGE_TYPE_MAIN, position, options); - } - } else { - // Exit split-screen and launch fullscreen since stage wasn't specified. - mStageCoordinator.exitSplitScreen(); - } - break; - } - case STAGE_TYPE_SIDE: { - if (position != SPLIT_POSITION_UNDEFINED) { - mStageCoordinator.setSideStagePosition(position); - } else { - position = mStageCoordinator.getSideStagePosition(); - } - if (options == null) { - options = new Bundle(); - } - mStageCoordinator.updateActivityOptions(options, position); - break; - } - case STAGE_TYPE_MAIN: { - if (position != SPLIT_POSITION_UNDEFINED) { - // Set the side stage opposite of what we want to the main stage. - final int sideStagePosition = position == SPLIT_POSITION_TOP_OR_LEFT - ? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT; - mStageCoordinator.setSideStagePosition(sideStagePosition); - } else { - position = mStageCoordinator.getMainStagePosition(); - } - if (options == null) { - options = new Bundle(); - } - mStageCoordinator.updateActivityOptions(options, position); - break; - } - default: - throw new IllegalArgumentException("Unknown stage=" + stage); - } + private void startIntentLegacy(PendingIntent intent, Intent fillInIntent, + @SplitScreen.StageType int stage, @SplitPosition int position, + @Nullable Bundle options) { + final boolean wasInSplit = isSplitScreenVisible(); - return options; + LegacyTransitions.ILegacyTransition transition = new LegacyTransitions.ILegacyTransition() { + @Override + public void onAnimationStart(int transit, RemoteAnimationTarget[] apps, + RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps, + IRemoteAnimationFinishedCallback finishedCallback, + SurfaceControl.Transaction t) { + boolean cancelled = apps == null || apps.length == 0; + mStageCoordinator.updateSurfaceBounds(null /* layout */, t); + if (cancelled) { + if (!wasInSplit) { + final WindowContainerTransaction undoWct = new WindowContainerTransaction(); + mStageCoordinator.prepareExitSplitScreen(STAGE_TYPE_MAIN, undoWct); + mSyncQueue.queue(undoWct); + mSyncQueue.runInSync(undoT -> { + // looks weird, but we want undoT to execute after t but still want the + // rest of the syncQueue runnables to aggregate. + t.merge(undoT); + undoT.merge(t); + }); + return; + } + } + for (int i = 0; i < apps.length; ++i) { + if (apps[i].mode == MODE_OPENING) { + t.show(apps[i].leash); + } + } + RemoteAnimationTarget divider = mStageCoordinator.getDividerBarLegacyTarget(); + t.show(divider.leash); + t.apply(); + if (cancelled) return; + try { + finishedCallback.onAnimationFinished(); + } catch (RemoteException e) { + Slog.e(TAG, "Error finishing legacy transition: ", e); + } + } + }; + WindowContainerTransaction wct = new WindowContainerTransaction(); + options = mStageCoordinator.resolveStartStage(stage, position, options, wct); + wct.sendPendingIntent(intent, fillInIntent, options); + mSyncQueue.queue(transition, WindowManager.TRANSIT_OPEN, wct); } RemoteAnimationTarget[] onGoingToRecentsLegacy(boolean cancel) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java index a89ce9ae76f20..81ce2b7bd2a58 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java @@ -36,6 +36,7 @@ import static com.android.wm.shell.splitscreen.SplitScreen.stageTypeToString; import static com.android.wm.shell.splitscreen.SplitScreenTransitions.FLAG_IS_DIVIDER_BAR; import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS; import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_DISMISS_SNAP; +import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE; import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_SCREEN_PAIR_OPEN; import static com.android.wm.shell.transition.Transitions.isClosingType; import static com.android.wm.shell.transition.Transitions.isOpeningType; @@ -45,8 +46,10 @@ import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityOptions; import android.app.ActivityTaskManager; +import android.app.PendingIntent; import android.app.WindowConfiguration; import android.content.Context; +import android.content.Intent; import android.graphics.Rect; import android.hardware.devicestate.DeviceStateManager; import android.os.Bundle; @@ -211,7 +214,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, boolean moveToSideStage(ActivityManager.RunningTaskInfo task, @SplitPosition int sideStagePosition) { final WindowContainerTransaction wct = new WindowContainerTransaction(); - setSideStagePosition(sideStagePosition); + setSideStagePosition(sideStagePosition, wct); mMainStage.activate(getMainStageBounds(), wct); mSideStage.addTask(task, getSideStageBounds(), wct); mTaskOrganizer.applyTransaction(wct); @@ -243,7 +246,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, final WindowContainerTransaction wct = new WindowContainerTransaction(); mainOptions = mainOptions != null ? mainOptions : new Bundle(); sideOptions = sideOptions != null ? sideOptions : new Bundle(); - setSideStagePosition(sidePosition); + setSideStagePosition(sidePosition, wct); // Build a request WCT that will launch both apps such that task 0 is on the main stage // while task 1 is on the side stage. @@ -312,7 +315,7 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, } sideOptions = sideOptions != null ? sideOptions : new Bundle(); - setSideStagePosition(sidePosition); + setSideStagePosition(sidePosition, wct); // Build a request WCT that will launch both apps such that task 0 is on the main stage // while task 1 is on the side stage. @@ -331,6 +334,69 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, mTaskOrganizer.applyTransaction(wct); } + public void startIntent(PendingIntent intent, Intent fillInIntent, + @SplitScreen.StageType int stage, @SplitPosition int position, + @androidx.annotation.Nullable Bundle options, + @Nullable IRemoteTransition remoteTransition) { + final WindowContainerTransaction wct = new WindowContainerTransaction(); + options = resolveStartStage(stage, position, options, wct); + wct.sendPendingIntent(intent, fillInIntent, options); + mSplitTransitions.startEnterTransition( + TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE, wct, remoteTransition, this); + } + + Bundle resolveStartStage(@SplitScreen.StageType int stage, + @SplitPosition int position, @androidx.annotation.Nullable Bundle options, + @androidx.annotation.Nullable WindowContainerTransaction wct) { + switch (stage) { + case STAGE_TYPE_UNDEFINED: { + // Use the stage of the specified position is valid. + if (position != SPLIT_POSITION_UNDEFINED) { + if (position == getSideStagePosition()) { + options = resolveStartStage(STAGE_TYPE_SIDE, position, options, wct); + } else { + options = resolveStartStage(STAGE_TYPE_MAIN, position, options, wct); + } + } else { + // Exit split-screen and launch fullscreen since stage wasn't specified. + prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct); + } + break; + } + case STAGE_TYPE_SIDE: { + if (position != SPLIT_POSITION_UNDEFINED) { + setSideStagePosition(position, wct); + } else { + position = getSideStagePosition(); + } + if (options == null) { + options = new Bundle(); + } + updateActivityOptions(options, position, wct); + break; + } + case STAGE_TYPE_MAIN: { + if (position != SPLIT_POSITION_UNDEFINED) { + // Set the side stage opposite of what we want to the main stage. + final int sideStagePosition = position == SPLIT_POSITION_TOP_OR_LEFT + ? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT; + setSideStagePosition(sideStagePosition, wct); + } else { + position = getMainStagePosition(); + } + if (options == null) { + options = new Bundle(); + } + updateActivityOptions(options, position, wct); + break; + } + default: + throw new IllegalArgumentException("Unknown stage=" + stage); + } + + return options; + } + @SplitLayout.SplitPosition int getSideStagePosition() { return mSideStagePosition; @@ -342,18 +408,24 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, ? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT; } - void setSideStagePosition(@SplitPosition int sideStagePosition) { - setSideStagePosition(sideStagePosition, true /* updateBounds */); + void setSideStagePosition(@SplitPosition int sideStagePosition, + @Nullable WindowContainerTransaction wct) { + setSideStagePosition(sideStagePosition, true /* updateBounds */, wct); } private void setSideStagePosition(@SplitPosition int sideStagePosition, - boolean updateBounds) { + boolean updateBounds, @Nullable WindowContainerTransaction wct) { if (mSideStagePosition == sideStagePosition) return; mSideStagePosition = sideStagePosition; sendOnStagePositionChanged(); if (mSideStageListener.mVisible && updateBounds) { - onBoundsChanged(mSplitLayout); + if (wct == null) { + // onBoundsChanged builds/applies a wct with the contents of updateWindowBounds. + onBoundsChanged(mSplitLayout); + } else { + updateWindowBounds(mSplitLayout, wct); + } } } @@ -388,7 +460,12 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, mSplitLayout.resetDividerPosition(); } - private void prepareExitSplitScreen(@SplitScreen.StageType int stageToTop, + /** + * Unlike exitSplitScreen, this takes a stagetype vs an actual stage-reference and populates + * an existing WindowContainerTransaction (rather than applying immediately). This is intended + * to be used when exiting split might be bundled with other window operations. + */ + void prepareExitSplitScreen(@SplitScreen.StageType int stageToTop, @NonNull WindowContainerTransaction wct) { mSideStage.removeAllTasks(wct, stageToTop == STAGE_TYPE_SIDE); mMainStage.deactivate(wct, stageToTop == STAGE_TYPE_MAIN); @@ -403,15 +480,21 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, opts.putParcelable(KEY_LAUNCH_ROOT_TASK_TOKEN, stage.mRootTaskInfo.token); } - void updateActivityOptions(Bundle opts, @SplitPosition int position) { + void updateActivityOptions(Bundle opts, @SplitPosition int position, + @Nullable WindowContainerTransaction wct) { addActivityOptions(opts, position == mSideStagePosition ? mSideStage : mMainStage); if (!mMainStage.isActive()) { // Activate the main stage in anticipation of an app launch. - final WindowContainerTransaction wct = new WindowContainerTransaction(); + boolean needsApply = wct == null; + if (needsApply) { + wct = new WindowContainerTransaction(); + } mMainStage.activate(getMainStageBounds(), wct); mSideStage.setBounds(getSideStageBounds(), wct); - mTaskOrganizer.applyTransaction(wct); + if (needsApply) { + mTaskOrganizer.applyTransaction(wct); + } } } @@ -624,32 +707,41 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, @Override public void onDoubleTappedDivider() { setSideStagePosition(mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT - ? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT); + ? SPLIT_POSITION_BOTTOM_OR_RIGHT : SPLIT_POSITION_TOP_OR_LEFT, null /* wct */); } @Override public void onBoundsChanging(SplitLayout layout) { - final StageTaskListener topLeftStage = - mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage; - final StageTaskListener bottomRightStage = - mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage; - - mSyncQueue.runInSync(t -> layout.applySurfaceChanges(t, topLeftStage.mRootLeash, - bottomRightStage.mRootLeash, topLeftStage.mDimLayer, bottomRightStage.mDimLayer)); + mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t)); } @Override public void onBoundsChanged(SplitLayout layout) { + final WindowContainerTransaction wct = new WindowContainerTransaction(); + updateWindowBounds(layout, wct); + mSyncQueue.queue(wct); + mSyncQueue.runInSync(t -> updateSurfaceBounds(layout, t)); + } + + /** + * Populates `wct` with operations that match the split windows to the current layout. + * To match relevant surfaces, make sure to call updateSurfaceBounds after `wct` is applied + */ + private void updateWindowBounds(SplitLayout layout, WindowContainerTransaction wct) { final StageTaskListener topLeftStage = mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage; final StageTaskListener bottomRightStage = mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage; - - final WindowContainerTransaction wct = new WindowContainerTransaction(); layout.applyTaskChanges(wct, topLeftStage.mRootTaskInfo, bottomRightStage.mRootTaskInfo); - mSyncQueue.queue(wct); - mSyncQueue.runInSync(t -> layout.applySurfaceChanges(t, topLeftStage.mRootLeash, - bottomRightStage.mRootLeash, topLeftStage.mDimLayer, bottomRightStage.mDimLayer)); + } + + void updateSurfaceBounds(@Nullable SplitLayout layout, @NonNull SurfaceControl.Transaction t) { + final StageTaskListener topLeftStage = + mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mSideStage : mMainStage; + final StageTaskListener bottomRightStage = + mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT ? mMainStage : mSideStage; + (layout != null ? layout : mSplitLayout).applySurfaceChanges(t, topLeftStage.mRootLeash, + bottomRightStage.mRootLeash, topLeftStage.mDimLayer, bottomRightStage.mDimLayer); } @Override @@ -869,7 +961,8 @@ class StageCoordinator implements SplitLayout.SplitLayoutHandler, // Update local states (before animating). setDividerVisibility(true); - setSideStagePosition(SPLIT_POSITION_BOTTOM_OR_RIGHT, false /* updateBounds */); + setSideStagePosition(SPLIT_POSITION_BOTTOM_OR_RIGHT, false /* updateBounds */, + null /* wct */); setSplitsVisible(true); addDividerBarToTransition(info, t, true /* show */); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/LegacyTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/LegacyTransitions.java new file mode 100644 index 0000000000000..61e11e877b907 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/LegacyTransitions.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.transition; + +import android.annotation.NonNull; +import android.os.RemoteException; +import android.view.IRemoteAnimationFinishedCallback; +import android.view.IRemoteAnimationRunner; +import android.view.RemoteAnimationAdapter; +import android.view.RemoteAnimationTarget; +import android.view.SurfaceControl; +import android.view.WindowManager; +import android.window.IWindowContainerTransactionCallback; + +/** + * Utilities and interfaces for transition-like usage on top of the legacy app-transition and + * synctransaction tools. + */ +public class LegacyTransitions { + + /** + * Interface for a "legacy" transition. Effectively wraps a sync callback + remoteAnimation + * into one callback. + */ + public interface ILegacyTransition { + /** + * Called when both the associated sync transaction finishes and the remote animation is + * ready. + */ + void onAnimationStart(int transit, RemoteAnimationTarget[] apps, + RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps, + IRemoteAnimationFinishedCallback finishedCallback, SurfaceControl.Transaction t); + } + + /** + * Makes sure that a remote animation and corresponding sync callback are called together + * such that the sync callback is called first. This assumes that both the callback receiver + * and the remoteanimation are in the same process so that order is preserved on both ends. + */ + public static class LegacyTransition { + private final ILegacyTransition mLegacyTransition; + private int mSyncId = -1; + private SurfaceControl.Transaction mTransaction; + private int mTransit; + private RemoteAnimationTarget[] mApps; + private RemoteAnimationTarget[] mWallpapers; + private RemoteAnimationTarget[] mNonApps; + private IRemoteAnimationFinishedCallback mFinishCallback = null; + private boolean mCancelled = false; + private final SyncCallback mSyncCallback = new SyncCallback(); + private final RemoteAnimationAdapter mAdapter = + new RemoteAnimationAdapter(new RemoteAnimationWrapper(), 0, 0); + + public LegacyTransition(@WindowManager.TransitionType int type, + @NonNull ILegacyTransition legacyTransition) { + mLegacyTransition = legacyTransition; + mTransit = type; + } + + public @WindowManager.TransitionType int getType() { + return mTransit; + } + + public IWindowContainerTransactionCallback getSyncCallback() { + return mSyncCallback; + } + + public RemoteAnimationAdapter getAdapter() { + return mAdapter; + } + + private class SyncCallback extends IWindowContainerTransactionCallback.Stub { + @Override + public void onTransactionReady(int id, SurfaceControl.Transaction t) + throws RemoteException { + mSyncId = id; + mTransaction = t; + checkApply(); + } + } + + private class RemoteAnimationWrapper extends IRemoteAnimationRunner.Stub { + @Override + public void onAnimationStart(int transit, RemoteAnimationTarget[] apps, + RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps, + IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException { + mTransit = transit; + mApps = apps; + mWallpapers = wallpapers; + mNonApps = nonApps; + mFinishCallback = finishedCallback; + checkApply(); + } + + @Override + public void onAnimationCancelled() throws RemoteException { + mCancelled = true; + mApps = mWallpapers = mNonApps = null; + checkApply(); + } + } + + + private void checkApply() throws RemoteException { + if (mSyncId < 0 || (mFinishCallback == null && !mCancelled)) return; + mLegacyTransition.onAnimationStart(mTransit, mApps, mWallpapers, + mNonApps, mFinishCallback, mTransaction); + } + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java index 1ca71af4d0179..01ef2a6d51115 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java @@ -83,6 +83,9 @@ public class Transitions implements RemoteCallable { /** Transition type for removing PIP via the Shell, either via Dismiss bubble or Close. */ public static final int TRANSIT_REMOVE_PIP = TRANSIT_FIRST_CUSTOM + 4; + /** Transition type for entering split by opening an app into side-stage. */ + public static final int TRANSIT_SPLIT_SCREEN_OPEN_TO_SIDE = TRANSIT_FIRST_CUSTOM + 5; + private final WindowOrganizer mOrganizer; private final Context mContext; private final ShellExecutor mMainExecutor; diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 99ae52c009959..f6510e615e72f 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -16348,6 +16348,15 @@ public class ActivityManagerService extends IActivityManager.Stub return mProcessList.getIsolatedProcessesLocked(uid); } } + + /** @see ActivityManagerService#sendIntentSender */ + @Override + public int sendIntentSender(IIntentSender target, IBinder allowlistToken, int code, + Intent intent, String resolvedType, + IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) { + return ActivityManagerService.this.sendIntentSender(target, allowlistToken, code, + intent, resolvedType, finishedReceiver, requiredPermission, options); + } } long inputDispatchingTimedOut(int pid, final boolean aboveSystem, String reason) { diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java index ac687dc064ce7..174b3965f11a4 100644 --- a/services/core/java/com/android/server/wm/AppTransition.java +++ b/services/core/java/com/android/server/wm/AppTransition.java @@ -213,6 +213,7 @@ public class AppTransition implements Dump { private int mNextAppTransitionEnter; private int mNextAppTransitionExit; private int mNextAppTransitionInPlace; + private boolean mNextAppTransitionIsSync; // Keyed by WindowContainer hashCode. private final SparseArray mNextAppTransitionAnimationsSpecs @@ -348,6 +349,13 @@ public class AppTransition implements Dump { fetchAppTransitionSpecsFromFuture(); } + void abort() { + if (mRemoteAnimationController != null) { + mRemoteAnimationController.cancelAnimation("aborted"); + } + clear(); + } + boolean isRunning() { return mAppTransitionState == APP_STATE_RUNNING; } @@ -466,6 +474,7 @@ public class AppTransition implements Dump { mNextAppTransitionAnimationsSpecsFuture = null; mDefaultNextAppTransitionAnimationSpec = null; mAnimationFinishedCallback = null; + mNextAppTransitionIsSync = false; } void freeze() { @@ -1146,13 +1155,19 @@ public class AppTransition implements Dump { } void overridePendingAppTransitionRemote(RemoteAnimationAdapter remoteAnimationAdapter) { + overridePendingAppTransitionRemote(remoteAnimationAdapter, false /* sync */); + } + + void overridePendingAppTransitionRemote(RemoteAnimationAdapter remoteAnimationAdapter, + boolean sync) { ProtoLog.i(WM_DEBUG_APP_TRANSITIONS, "Override pending remote transitionSet=%b adapter=%s", isTransitionSet(), remoteAnimationAdapter); - if (isTransitionSet()) { + if (isTransitionSet() && !mNextAppTransitionIsSync) { clear(); mNextAppTransitionType = NEXT_TRANSIT_TYPE_REMOTE; mRemoteAnimationController = new RemoteAnimationController(mService, mDisplayContent, remoteAnimationAdapter, mHandler); + mNextAppTransitionIsSync = sync; } } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index c7b3d23641b88..eb56afafdd063 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -5014,6 +5014,11 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } } + void cancelAppTransition() { + if (!mAppTransition.isTransitionSet() || mAppTransition.isRunning()) return; + mAppTransition.abort(); + } + /** * Update pendingLayoutChanges after app transition has finished. */ diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java index ffd89d3a94fe6..520a104f80217 100644 --- a/services/core/java/com/android/server/wm/WindowOrganizerController.java +++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java @@ -17,10 +17,12 @@ package com.android.server.wm; import static android.Manifest.permission.START_TASKS_FROM_RECENTS; +import static android.view.Display.DEFAULT_DISPLAY; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_LAUNCH_TASK; +import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_PENDING_INTENT; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT; import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT; @@ -39,6 +41,8 @@ import static com.android.server.wm.WindowContainer.POSITION_TOP; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.ActivityManager; +import android.app.ActivityOptions; import android.app.WindowConfiguration; import android.content.Intent; import android.content.pm.ActivityInfo; @@ -52,6 +56,7 @@ import android.os.RemoteException; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Slog; +import android.view.RemoteAnimationAdapter; import android.view.SurfaceControl; import android.window.IDisplayAreaOrganizerController; import android.window.ITaskFragmentOrganizer; @@ -237,6 +242,44 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub } } + @Override + public int startLegacyTransition(int type, @NonNull RemoteAnimationAdapter adapter, + @NonNull IWindowContainerTransactionCallback callback, + @NonNull WindowContainerTransaction t) { + enforceTaskPermission("startLegacyTransition()"); + final CallerInfo caller = new CallerInfo(); + final long ident = Binder.clearCallingIdentity(); + int syncId; + try { + synchronized (mGlobalLock) { + if (type < 0) { + throw new IllegalArgumentException("Can't create transition with no type"); + } + if (mTransitionController.getTransitionPlayer() != null) { + throw new IllegalArgumentException("Can't use legacy transitions in" + + " when shell transitions are enabled."); + } + final DisplayContent dc = + mService.mRootWindowContainer.getDisplayContent(DEFAULT_DISPLAY); + if (dc.mAppTransition.isTransitionSet()) { + // a transition already exists, so the callback probably won't be called. + return -1; + } + adapter.setCallingPidUid(caller.mPid, caller.mUid); + dc.prepareAppTransition(type); + dc.mAppTransition.overridePendingAppTransitionRemote(adapter, true /* sync */); + syncId = startSyncWithOrganizer(callback); + applyTransaction(t, syncId, null /* transition */, caller); + setSyncReady(syncId); + mService.mRootWindowContainer.getDisplayContent(DEFAULT_DISPLAY) + .executeAppTransition(); + } + } finally { + Binder.restoreCallingIdentity(ident); + } + return syncId; + } + @Override public int finishTransition(@NonNull IBinder transitionToken, @Nullable WindowContainerTransaction t, @@ -578,6 +621,37 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub mService.mTaskSupervisor.startActivityFromRecents(caller.mPid, caller.mUid, taskId, safeOptions); break; + case HIERARCHY_OP_TYPE_PENDING_INTENT: + String resolvedType = hop.getActivityIntent() != null + ? hop.getActivityIntent().resolveTypeIfNeeded( + mService.mContext.getContentResolver()) + : null; + + Bundle options = null; + if (hop.getPendingIntent().isActivity()) { + // Set the context display id as preferred for this activity launches, so that + // it can land on caller's display. Or just brought the task to front at the + // display where it was on since it has higher preference. + ActivityOptions activityOptions = hop.getLaunchOptions() != null + ? new ActivityOptions(hop.getLaunchOptions()) + : ActivityOptions.makeBasic(); + activityOptions.setCallerDisplayId(DEFAULT_DISPLAY); + options = activityOptions.toBundle(); + } + + int res = mService.mAmInternal.sendIntentSender(hop.getPendingIntent().getTarget(), + hop.getPendingIntent().getWhitelistToken(), 0 /* code */, + hop.getActivityIntent(), resolvedType, null /* finishReceiver */, + null /* requiredPermission */, options); + if (res != ActivityManager.START_SUCCESS + && res != ActivityManager.START_TASK_TO_FRONT) { + if (!mTransitionController.isShellTransitionsEnabled()) { + final DisplayContent dc = + mService.mRootWindowContainer.getDisplayContent(DEFAULT_DISPLAY); + dc.cancelAppTransition(); + } + } + break; case HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT: final TaskFragmentCreationParams taskFragmentCreationOptions = hop.getTaskFragmentCreationOptions();