From e9f13e1341e248a00be88e9ce34e97577c8eb2e1 Mon Sep 17 00:00:00 2001 From: Evan Rosky Date: Wed, 14 Jul 2021 17:15:39 -0700 Subject: [PATCH 1/2] Aggregate stage-split operations into WCT Previously different operations were just applying transactions as they were called. We need better control over this for synchronization purposes, so we basically pass wct into these operations so that everything can be aggregated into one WCT if desired. Bug: 192291727 Test: existing tests pass (this is a refactor) Change-Id: Ie26d634bcd3ab6baf4d2a4788986689914a58c13 --- .../splitscreen/SplitScreenController.java | 23 +++--- .../shell/splitscreen/StageCoordinator.java | 77 +++++++++++++------ 2 files changed, 64 insertions(+), 36 deletions(-) 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..4b657eabca30f 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 @@ -147,7 +147,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 +185,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 = resolveStartStage(stage, position, options, null /* wct */); try { ActivityTaskManager.getService().startActivityFromRecents(taskId, options); @@ -197,7 +197,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 = resolveStartStage(stage, position, options, null /* wct */); try { LauncherApps launcherApps = @@ -222,32 +222,33 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, } private Bundle resolveStartStage(@SplitScreen.StageType int stage, - @SplitPosition int position, @Nullable Bundle options) { + @SplitPosition int position, @Nullable Bundle options, + @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 == mStageCoordinator.getSideStagePosition()) { - options = resolveStartStage(STAGE_TYPE_SIDE, position, options); + options = resolveStartStage(STAGE_TYPE_SIDE, position, options, wct); } else { - options = resolveStartStage(STAGE_TYPE_MAIN, position, options); + options = resolveStartStage(STAGE_TYPE_MAIN, position, options, wct); } } else { // Exit split-screen and launch fullscreen since stage wasn't specified. - mStageCoordinator.exitSplitScreen(); + mStageCoordinator.prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct); } break; } case STAGE_TYPE_SIDE: { if (position != SPLIT_POSITION_UNDEFINED) { - mStageCoordinator.setSideStagePosition(position); + mStageCoordinator.setSideStagePosition(position, wct); } else { position = mStageCoordinator.getSideStagePosition(); } if (options == null) { options = new Bundle(); } - mStageCoordinator.updateActivityOptions(options, position); + mStageCoordinator.updateActivityOptions(options, position, wct); break; } case STAGE_TYPE_MAIN: { @@ -255,14 +256,14 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, // 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); + mStageCoordinator.setSideStagePosition(sideStagePosition, wct); } else { position = mStageCoordinator.getMainStagePosition(); } if (options == null) { options = new Bundle(); } - mStageCoordinator.updateActivityOptions(options, position); + mStageCoordinator.updateActivityOptions(options, position, wct); break; } default: 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..94c9bcc923392 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 @@ -211,7 +211,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 +243,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 +312,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. @@ -342,18 +342,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 +394,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 +414,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 +641,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 +895,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 */); From 6139cf51b2b5e39c834676f1289dce6e20a607f4 Mon Sep 17 00:00:00 2001 From: Evan Rosky Date: Wed, 14 Jul 2021 14:20:30 -0700 Subject: [PATCH 2/2] Create a LegacyTransitions to manage combined WCT + remoteanimation Use this to implement Drag-to-split transition BYPASS_INCLUSIVE_LANGUAGE_REASON=using existing API. Bug: 192291727 Test: drag from taskbar into split and observe. Also, drag same-app into split and observe no-op. Change-Id: I710805c85c7d57ae8eebe18e5df7fca899f1b882 --- .../android/app/ActivityManagerInternal.java | 5 + .../window/IWindowOrganizerController.aidl | 12 ++ .../window/WindowContainerTransaction.java | 40 ++++++ core/java/android/window/WindowOrganizer.java | 22 ++++ .../android/wm/shell/apppairs/AppPair.java | 10 +- .../wm/shell/common/SyncTransactionQueue.java | 57 +++++++- .../common/split/SplitWindowManager.java | 2 +- .../splitscreen/SplitScreenController.java | 119 +++++++++-------- .../shell/splitscreen/StageCoordinator.java | 66 ++++++++++ .../shell/transition/LegacyTransitions.java | 124 ++++++++++++++++++ .../wm/shell/transition/Transitions.java | 3 + .../server/am/ActivityManagerService.java | 9 ++ .../com/android/server/wm/AppTransition.java | 17 ++- .../com/android/server/wm/DisplayContent.java | 5 + .../server/wm/WindowOrganizerController.java | 74 +++++++++++ 15 files changed, 494 insertions(+), 71 deletions(-) create mode 100644 libs/WindowManager/Shell/src/com/android/wm/shell/transition/LegacyTransitions.java 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 4b657eabca30f..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; @@ -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, null /* wct */); + 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, null /* wct */); + options = mStageCoordinator.resolveStartStage(stage, position, options, null /* wct */); try { LauncherApps launcherApps = @@ -212,65 +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, - @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 == mStageCoordinator.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. - mStageCoordinator.prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct); - } - break; - } - case STAGE_TYPE_SIDE: { - if (position != SPLIT_POSITION_UNDEFINED) { - mStageCoordinator.setSideStagePosition(position, wct); - } else { - position = mStageCoordinator.getSideStagePosition(); - } - if (options == null) { - options = new Bundle(); - } - mStageCoordinator.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; - mStageCoordinator.setSideStagePosition(sideStagePosition, wct); - } else { - position = mStageCoordinator.getMainStagePosition(); - } - if (options == null) { - options = new Bundle(); - } - mStageCoordinator.updateActivityOptions(options, position, wct); - 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 94c9bcc923392..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; @@ -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; 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();