diff options
14 files changed, 186 insertions, 31 deletions
diff --git a/apct-tests/perftests/core/src/android/wm/RecentsAnimationPerfTest.java b/apct-tests/perftests/core/src/android/wm/RecentsAnimationPerfTest.java index 73b4a1914ad1..836e6b617395 100644 --- a/apct-tests/perftests/core/src/android/wm/RecentsAnimationPerfTest.java +++ b/apct-tests/perftests/core/src/android/wm/RecentsAnimationPerfTest.java @@ -192,6 +192,11 @@ public class RecentsAnimationPerfTest extends WindowManagerPerfTestBase { Assume.assumeNoException( new AssertionError("onAnimationCanceled should not be called")); } + + @Override + public void onTaskAppeared(RemoteAnimationTarget app) throws RemoteException { + /* no-op */ + } }; recentsSemaphore.tryAcquire(); diff --git a/core/java/android/view/IRecentsAnimationController.aidl b/core/java/android/view/IRecentsAnimationController.aidl index a60a5cca08bd..983ab2eedf5c 100644 --- a/core/java/android/view/IRecentsAnimationController.aidl +++ b/core/java/android/view/IRecentsAnimationController.aidl @@ -114,4 +114,16 @@ interface IRecentsAnimationController { * animation is cancelled through fail safe mechanism. */ void setWillFinishToHome(boolean willFinishToHome); + + /** + * Stops controlling a task that is currently controlled by this recents animation. + * + * This method should be called when a task that has been received via {@link #onAnimationStart} + * or {@link #onTaskAppeared} is no longer needed. After calling this method, the task will + * either disappear from the screen, or jump to its final position in case it was the top task. + * + * @param taskId Id of the Task target to remove + * @return {@code true} when target removed successfully, {@code false} otherwise. + */ + boolean removeTask(int taskId); } diff --git a/core/java/android/view/IRecentsAnimationRunner.aidl b/core/java/android/view/IRecentsAnimationRunner.aidl index 6eb90fc54286..925786f82e00 100644 --- a/core/java/android/view/IRecentsAnimationRunner.aidl +++ b/core/java/android/view/IRecentsAnimationRunner.aidl @@ -56,4 +56,10 @@ oneway interface IRecentsAnimationRunner { void onAnimationStart(in IRecentsAnimationController controller, in RemoteAnimationTarget[] apps, in RemoteAnimationTarget[] wallpapers, in Rect homeContentInsets, in Rect minimizedHomeBounds) = 2; + + /** + * Called when the task of an activity that has been started while the recents animation + * was running becomes ready for control. + */ + void onTaskAppeared(in RemoteAnimationTarget app) = 3; } diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 07cf41560cf3..618e4c02cfcd 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -883,6 +883,12 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, + "-242787066": { + "message": "addTaskToRecentsAnimationIfNeeded, control: %s, task: %s, transit: %s", + "level": "DEBUG", + "group": "WM_DEBUG_RECENTS_ANIMATIONS", + "at": "com\/android\/server\/wm\/WindowContainer.java" + }, "-198463978": { "message": "updateRotationUnchecked: alwaysSendConfiguration=%b forceRelayout=%b", "level": "VERBOSE", @@ -901,6 +907,12 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/ScreenRotationAnimation.java" }, + "-172900257": { + "message": "addTaskToTargets, target: %s", + "level": "DEBUG", + "group": "WM_DEBUG_RECENTS_ANIMATIONS", + "at": "com\/android\/server\/wm\/RecentsAnimationController.java" + }, "-167822951": { "message": "Attempted to add starting window to token with already existing starting window", "level": "WARN", @@ -1513,6 +1525,12 @@ "group": "WM_DEBUG_RECENTS_ANIMATIONS", "at": "com\/android\/server\/wm\/RecentsAnimation.java" }, + "854237232": { + "message": "addTaskToRecentsAnimationIfNeeded, control: %s, task: %s, transit: %s", + "level": "DEBUG", + "group": "WM_DEBUG_RECENTS_ANIMATIONS", + "at": "com\/android\/server\/wm\/Task.java" + }, "873914452": { "message": "goodToGo()", "level": "DEBUG", diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java index 3bda3c8df699..806678f23bb3 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java @@ -261,6 +261,11 @@ public class ActivityManagerWrapper { animationHandler.onAnimationCanceled( taskSnapshot != null ? new ThumbnailData(taskSnapshot) : null); } + + @Override + public void onTaskAppeared(RemoteAnimationTarget app) { + animationHandler.onTaskAppeared(new RemoteAnimationTargetCompat(app)); + } }; } ActivityTaskManager.getService().startRecentsActivity(intent, receiver, runner); diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java index bbb83c73446c..76513c6ff3d5 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java @@ -109,4 +109,16 @@ public class RecentsAnimationControllerCompat { Log.e(TAG, "Failed to set overview reached state", e); } } + + /** + * @see IRecentsAnimationController#removeTask + */ + public boolean removeTask(int taskId) { + try { + return mAnimationController.removeTask(taskId); + } catch (RemoteException e) { + Log.e(TAG, "Failed to remove remote animation target", e); + return false; + } + } }
\ No newline at end of file diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java index 2c99c5c84015..c4cd192212a0 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationListener.java @@ -32,4 +32,10 @@ public interface RecentsAnimationListener { * Called when the animation into Recents was canceled. This call is made on the binder thread. */ void onAnimationCanceled(ThumbnailData thumbnailData); + + /** + * Called when the task of an activity that has been started while the recents animation + * was running becomes ready for control. + */ + void onTaskAppeared(RemoteAnimationTargetCompat app); } diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java index 78ee1de78079..6f1ddcd793a9 100644 --- a/services/core/java/com/android/server/wm/AppTransition.java +++ b/services/core/java/com/android/server/wm/AppTransition.java @@ -403,11 +403,18 @@ public class AppTransition implements Dump { mNextAppTransitionType == NEXT_TRANSIT_TYPE_THUMBNAIL_ASPECT_SCALE_DOWN; } - boolean isNextAppTransitionOpenCrossProfileApps() { return mNextAppTransitionType == NEXT_TRANSIT_TYPE_OPEN_CROSS_PROFILE_APPS; } + boolean isNextAppTransitionCustomFromRecents() { + final RecentTasks recentTasks = mService.mAtmService.getRecentTasks(); + final String recentsPackageName = + (recentTasks != null) ? recentTasks.getRecentsComponent().getPackageName() : null; + return mNextAppTransitionType == NEXT_TRANSIT_TYPE_CUSTOM + && mNextAppTransitionPackage.equals(recentsPackageName); + } + /** * @return true if and only if we are currently fetching app transition specs from the future * passed into {@link #overridePendingAppTransitionMultiThumbFuture} diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java index a031fe82d48b..0a9878dd660b 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimation.java +++ b/services/core/java/com/android/server/wm/RecentsAnimation.java @@ -442,10 +442,6 @@ class RecentsAnimation implements RecentsAnimationCallbacks, // Always prepare an app transition since we rely on the transition callbacks to cleanup mWindowManager.prepareAppTransition(TRANSIT_NONE, false); controller.setCancelOnNextTransitionStart(); - } else { - // Just cancel directly to unleash from launcher when the next launching task is the - // current top task. - mWindowManager.cancelRecentsAnimation(REORDER_KEEP_IN_PLACE, "stackOrderChanged"); } } diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index 6fda1170a3f5..54210ae1c0b0 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -43,6 +43,7 @@ import android.os.RemoteException; import android.os.SystemClock; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.IntArray; import android.util.Slog; import android.util.SparseBooleanArray; import android.util.proto.ProtoOutputStream; @@ -99,6 +100,8 @@ public class RecentsAnimationController implements DeathRecipient { private IRecentsAnimationRunner mRunner; private final RecentsAnimationCallbacks mCallbacks; private final ArrayList<TaskAnimationAdapter> mPendingAnimations = new ArrayList<>(); + private final IntArray mPendingNewTaskTargets = new IntArray(0); + private final ArrayList<WallpaperAnimationAdapter> mPendingWallpaperAnimations = new ArrayList<>(); private final int mDisplayId; @@ -220,6 +223,10 @@ public class RecentsAnimationController implements DeathRecipient { if (mCanceled) { return; } + // Remove all new task targets. + for (int i = mPendingNewTaskTargets.size() - 1; i >= 0; i--) { + removeTaskInternal(mPendingNewTaskTargets.get(i)); + } } // Note, the callback will handle its own synchronization, do not lock on WM lock @@ -310,6 +317,18 @@ public class RecentsAnimationController implements DeathRecipient { mWillFinishToHome = willFinishToHome; } } + + @Override + public boolean removeTask(int taskId) { + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mService.getWindowManagerLock()) { + return removeTaskInternal(taskId); + } + } finally { + Binder.restoreCallingIdentity(token); + } + } }; /** @@ -405,11 +424,17 @@ public class RecentsAnimationController implements DeathRecipient { @VisibleForTesting AnimationAdapter addAnimation(Task task, boolean isRecentTaskInvisible) { + return addAnimation(task, isRecentTaskInvisible, null /* finishedCallback */); + } + + @VisibleForTesting + AnimationAdapter addAnimation(Task task, boolean isRecentTaskInvisible, + OnAnimationFinishedCallback finishedCallback) { ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "addAnimation(%s)", task.getName()); final TaskAnimationAdapter taskAdapter = new TaskAnimationAdapter(task, isRecentTaskInvisible); task.startAnimation(task.getPendingTransaction(), taskAdapter, false /* hidden */, - ANIMATION_TYPE_RECENTS); + ANIMATION_TYPE_RECENTS, finishedCallback); task.commitPendingTransaction(); mPendingAnimations.add(taskAdapter); return taskAdapter; @@ -489,6 +514,49 @@ public class RecentsAnimationController implements DeathRecipient { } } + void addTaskToTargets(Task task, OnAnimationFinishedCallback finishedCallback) { + if (mRunner != null) { + final RemoteAnimationTarget target = createTaskRemoteAnimation(task, finishedCallback); + if (target == null) return; + + ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "addTaskToTargets, target: %s", target); + try { + mRunner.onTaskAppeared(target); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to report task appeared", e); + } + } + } + + private RemoteAnimationTarget createTaskRemoteAnimation(Task task, + OnAnimationFinishedCallback finishedCallback) { + final SparseBooleanArray recentTaskIds = + mService.mAtmService.getRecentTasks().getRecentTaskIds(); + TaskAnimationAdapter adapter = (TaskAnimationAdapter) addAnimation(task, + !recentTaskIds.get(task.mTaskId), finishedCallback); + mPendingNewTaskTargets.add(task.mTaskId); + return adapter.createRemoteAnimationTarget(); + } + + private boolean removeTaskInternal(int taskId) { + boolean result = false; + for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { + // Only allows when task target has became visible to user, to prevent + // the flickering during remove animation and task visible. + final TaskAnimationAdapter target = mPendingAnimations.get(i); + if (target.mTask.mTaskId == taskId && target.mTask.isOnTop()) { + removeAnimation(target); + final int taskIndex = mPendingNewTaskTargets.indexOf(taskId); + if (taskIndex != -1) { + mPendingNewTaskTargets.remove(taskIndex); + } + result = true; + break; + } + } + return result; + } + private RemoteAnimationTarget[] createAppAnimations() { final ArrayList<RemoteAnimationTarget> targets = new ArrayList<>(); for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index 80b8b5854966..2acfcbf70f90 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -66,6 +66,7 @@ import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_KEEP_SCREEN_ON; import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION; import static com.android.server.wm.ProtoLogGroup.WM_SHOW_SURFACE_ALLOC; import static com.android.server.wm.ProtoLogGroup.WM_SHOW_TRANSACTIONS; +import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_IN_PLACE; import static com.android.server.wm.RootWindowContainerProto.IS_HOME_RECENTS_COMPONENT; import static com.android.server.wm.RootWindowContainerProto.KEYGUARD_CONTROLLER; import static com.android.server.wm.RootWindowContainerProto.PENDING_ACTIVITIES; @@ -1515,6 +1516,7 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // Updates the extra information of the intent. if (fromHomeKey) { homeIntent.putExtra(WindowManagerPolicy.EXTRA_FROM_HOME_KEY, true); + mWindowManager.cancelRecentsAnimation(REORDER_KEEP_IN_PLACE, "startHomeActivity"); } // Update the reason for ANR debugging to verify if the user activity is the one that // actually launched. diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index d31939dec509..4b37bd06f549 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -86,6 +86,8 @@ import static com.android.server.wm.IdentifierProto.HASH_CODE; import static com.android.server.wm.IdentifierProto.TITLE; import static com.android.server.wm.IdentifierProto.USER_ID; import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ADD_REMOVE; +import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS; +import static com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback; import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN; import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; import static com.android.server.wm.WindowContainerChildProto.TASK; @@ -135,6 +137,7 @@ import android.view.RemoteAnimationAdapter; import android.view.RemoteAnimationTarget; import android.view.Surface; import android.view.SurfaceControl; +import android.view.WindowManager; import android.window.ITaskOrganizer; import com.android.internal.annotations.VisibleForTesting; @@ -3404,6 +3407,24 @@ class Task extends WindowContainer<WindowContainer> { } @Override + protected void applyAnimationUnchecked(WindowManager.LayoutParams lp, boolean enter, + int transit, boolean isVoiceInteraction, + @Nullable OnAnimationFinishedCallback finishedCallback) { + final RecentsAnimationController control = mWmService.getRecentsAnimationController(); + if (control != null && enter + && getDisplayContent().mAppTransition.isNextAppTransitionCustomFromRecents()) { + ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, + "addTaskToRecentsAnimationIfNeeded, control: %s, task: %s, transit: %s", + control, asTask(), AppTransition.appTransitionToString(transit)); + // We let the transition to be controlled by RecentsAnimation, and callback task's + // RemoteAnimationTarget for remote runner to animate. + control.addTaskToTargets(getRootTask(), finishedCallback); + } else { + super.applyAnimationUnchecked(lp, enter, transit, isVoiceInteraction, finishedCallback); + } + } + + @Override void dump(PrintWriter pw, String prefix, boolean dumpAll) { super.dump(pw, prefix, dumpAll); final String doublePrefix = prefix + " "; diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index f6e952c4cea1..e3cdfa13ba85 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -2076,8 +2076,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< * @see #getAnimationAdapter */ boolean applyAnimation(WindowManager.LayoutParams lp, int transit, boolean enter, - boolean isVoiceInteraction, - @Nullable OnAnimationFinishedCallback animationFinishedCallback) { + boolean isVoiceInteraction, @Nullable OnAnimationFinishedCallback finishedCallback) { if (mWmService.mDisableTransitionAnimation) { ProtoLog.v(WM_DEBUG_APP_TRANSITIONS_ANIM, "applyAnimation: transition animation is disabled or skipped. " @@ -2092,22 +2091,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< try { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "WC#applyAnimation"); if (okToAnimate()) { - final Pair<AnimationAdapter, AnimationAdapter> adapters = getAnimationAdapter(lp, - transit, enter, isVoiceInteraction); - AnimationAdapter adapter = adapters.first; - AnimationAdapter thumbnailAdapter = adapters.second; - if (adapter != null) { - startAnimation(getPendingTransaction(), adapter, !isVisible(), - ANIMATION_TYPE_APP_TRANSITION, animationFinishedCallback); - if (adapter.getShowWallpaper()) { - getDisplayContent().pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; - } - if (thumbnailAdapter != null) { - mSurfaceFreezer.mSnapshot.startAnimation(getPendingTransaction(), - thumbnailAdapter, ANIMATION_TYPE_APP_TRANSITION, - (type, anim) -> { }); - } - } + applyAnimationUnchecked(lp, enter, transit, isVoiceInteraction, finishedCallback); } else { cancelAnimation(); } @@ -2201,6 +2185,26 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< return resultAdapters; } + protected void applyAnimationUnchecked(WindowManager.LayoutParams lp, boolean enter, + int transit, boolean isVoiceInteraction, + @Nullable OnAnimationFinishedCallback finishedCallback) { + final Pair<AnimationAdapter, AnimationAdapter> adapters = getAnimationAdapter(lp, + transit, enter, isVoiceInteraction); + AnimationAdapter adapter = adapters.first; + AnimationAdapter thumbnailAdapter = adapters.second; + if (adapter != null) { + startAnimation(getPendingTransaction(), adapter, !isVisible(), + ANIMATION_TYPE_APP_TRANSITION, finishedCallback); + if (adapter.getShowWallpaper()) { + getDisplayContent().pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; + } + if (thumbnailAdapter != null) { + mSurfaceFreezer.mSnapshot.startAnimation(getPendingTransaction(), + thumbnailAdapter, ANIMATION_TYPE_APP_TRANSITION, (type, anim) -> { }); + } + } + } + final SurfaceAnimationRunner getSurfaceAnimationRunner() { return mWmService.mSurfaceAnimationRunner; } diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java index 881561f5750b..1f6ba7adf114 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java @@ -238,9 +238,6 @@ public class RecentsAnimationTest extends ActivityTestsBase { assertTrue(targetActivity.mLaunchTaskBehind); anotherHomeActivity.moveFocusableActivityToTop("launchAnotherHome"); - // The current top activity is not the recents so the animation should be canceled. - verify(mService.mWindowManager, times(1)).cancelRecentsAnimation( - eq(REORDER_KEEP_IN_PLACE), any() /* reason */); // The test uses mocked RecentsAnimationController so we have to invoke the callback // manually to simulate the flow. @@ -279,10 +276,6 @@ public class RecentsAnimationTest extends ActivityTestsBase { fullscreenStack.moveToFront("Activity start"); - // Ensure that the recents animation was canceled by cancelAnimationSynchronously(). - verify(mService.mWindowManager, times(1)).cancelRecentsAnimation( - eq(REORDER_KEEP_IN_PLACE), any()); - // Assume recents animation already started, set a state that cancel recents animation // with screenshot. doReturn(true).when(mRecentsAnimationController).shouldDeferCancelUntilNextTransition(); |