From 6a38fca2d81e5d5bc0343c57e4db3629c3a9a619 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Wed, 28 Mar 2018 17:57:09 -0700 Subject: [PATCH 1/3] Prevent unnecessary reordering of the home stack - Just cancel the recents animation in-place when handling the home button to prevent it from being repositioned to the bottom (which stops home) and then starting it again (which restarts it) Bug: 74405472 Test: Press home from an app, ensure launcher stop is only called once Change-Id: Id41aa2f77c01767cc2c35445458b8f2db81200fc --- config/hiddenapi-vendor-list.txt | 2 +- core/java/android/app/IActivityManager.aidl | 2 +- .../shared/system/ActivityManagerWrapper.java | 4 +- .../statusbar/policy/KeyButtonView.java | 9 ++++- .../server/am/ActivityManagerService.java | 9 +++-- .../android/server/am/RecentsAnimation.java | 18 ++++++--- .../server/wm/BoundsAnimationController.java | 7 +++- .../server/wm/BoundsAnimationTarget.java | 6 +++ .../server/wm/RecentsAnimationController.java | 39 +++++++++++++------ .../java/com/android/server/wm/TaskStack.java | 25 ++++++++++-- .../server/wm/WallpaperController.java | 4 +- .../server/wm/WindowManagerService.java | 8 ++-- .../android/server/am/RecentTasksTest.java | 2 +- .../wm/BoundsAnimationControllerTests.java | 5 +++ 14 files changed, 102 insertions(+), 38 deletions(-) diff --git a/config/hiddenapi-vendor-list.txt b/config/hiddenapi-vendor-list.txt index 61ae6e77cbfd9..3a3a708a20cb1 100644 --- a/config/hiddenapi-vendor-list.txt +++ b/config/hiddenapi-vendor-list.txt @@ -20,7 +20,7 @@ Landroid/app/AppOpsManager$PackageOps;->getOps()Ljava/util/List; Landroid/app/AppOpsManager$PackageOps;->getPackageName()Ljava/lang/String; Landroid/app/AppOpsManager$PackageOps;->getUid()I Landroid/app/IActivityController$Stub;->()V -Landroid/app/IActivityManager;->cancelRecentsAnimation()V +Landroid/app/IActivityManager;->cancelRecentsAnimation(Z)V Landroid/app/IActivityManager;->cancelTaskWindowTransition(I)V Landroid/app/IActivityManager;->closeSystemDialogs(Ljava/lang/String;)V Landroid/app/IActivityManager;->getCurrentUser()Landroid/content/pm/UserInfo; diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index e1a02fa497f73..919f71472d333 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -450,7 +450,7 @@ interface IActivityManager { in Intent intent, in String resolvedType, in Bundle options, int userId); void startRecentsActivity(in Intent intent, in IAssistDataReceiver assistDataReceiver, in IRecentsAnimationRunner recentsAnimationRunner); - void cancelRecentsAnimation(); + void cancelRecentsAnimation(boolean restoreHomeStackPosition); int startActivityFromRecents(int taskId, in Bundle options); Bundle getActivityOptions(in IBinder token); List getAppTasks(in String callingPackage); 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 1aad27f99cdce..6aa27542da9a0 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 @@ -258,9 +258,9 @@ public class ActivityManagerWrapper { /** * Cancels the remote recents animation started from {@link #startRecentsActivity}. */ - public void cancelRecentsAnimation() { + public void cancelRecentsAnimation(boolean restoreHomeStackPosition) { try { - ActivityManager.getService().cancelRecentsAnimation(); + ActivityManager.getService().cancelRecentsAnimation(restoreHomeStackPosition); } catch (RemoteException e) { Log.e(TAG, "Failed to cancel recents animation", e); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java index 5d7e9383930a4..e51efa643b8fb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java @@ -52,6 +52,7 @@ import com.android.systemui.plugins.statusbar.phone.NavBarButtonProvider.ButtonI import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.statusbar.VibratorHelper; +import static android.view.KeyEvent.KEYCODE_HOME; import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK; import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK; @@ -270,8 +271,12 @@ public class KeyButtonView extends ImageView implements ButtonInterface { if (mCode != 0) { if (doIt) { // If there was a pending remote recents animation, then we need to - // cancel the animation now before we handle the button itself - ActivityManagerWrapper.getInstance().cancelRecentsAnimation(); + // cancel the animation now before we handle the button itself. In the case + // where we are going home and the recents animation has already started, + // just cancel the recents animation, leaving the home stack in place + boolean isHomeKey = mCode == KEYCODE_HOME; + ActivityManagerWrapper.getInstance().cancelRecentsAnimation(!isHomeKey); + sendEvent(KeyEvent.ACTION_UP, 0); sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED); } else { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 08e5d44c88323..3b22cd959ff8a 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -204,6 +204,8 @@ import static android.view.WindowManager.TRANSIT_NONE; import static android.view.WindowManager.TRANSIT_TASK_IN_PLACE; import static android.view.WindowManager.TRANSIT_TASK_OPEN; import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT; +import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_HOME_TO_ORIGINAL_POSITION; +import static com.android.server.wm.RecentsAnimationController.REORDER_KEEP_HOME_IN_PLACE; import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; import static org.xmlpull.v1.XmlPullParser.START_TAG; @@ -257,7 +259,6 @@ import android.app.WaitResult; import android.app.WindowConfiguration.ActivityType; import android.app.WindowConfiguration.WindowingMode; import android.app.admin.DevicePolicyCache; -import android.app.admin.DevicePolicyManager; import android.app.assist.AssistContent; import android.app.assist.AssistStructure; import android.app.backup.IBackupManager; @@ -5225,12 +5226,14 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override - public void cancelRecentsAnimation() { + public void cancelRecentsAnimation(boolean restoreHomeStackPosition) { enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "cancelRecentsAnimation()"); final long origId = Binder.clearCallingIdentity(); try { synchronized (this) { - mWindowManager.cancelRecentsAnimation(); + mWindowManager.cancelRecentsAnimation(restoreHomeStackPosition + ? REORDER_MOVE_HOME_TO_ORIGINAL_POSITION + : REORDER_KEEP_HOME_IN_PLACE); } } finally { Binder.restoreCallingIdentity(origId); diff --git a/services/core/java/com/android/server/am/RecentsAnimation.java b/services/core/java/com/android/server/am/RecentsAnimation.java index 9df321c64c9a8..5e74619be3834 100644 --- a/services/core/java/com/android/server/am/RecentsAnimation.java +++ b/services/core/java/com/android/server/am/RecentsAnimation.java @@ -23,6 +23,8 @@ import static android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION; import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER; import static android.view.WindowManager.TRANSIT_NONE; import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS; +import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_HOME_TO_ORIGINAL_POSITION; +import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_HOME_TO_TOP; import android.app.ActivityOptions; import android.content.ComponentName; @@ -32,6 +34,7 @@ import android.os.RemoteException; import android.os.Trace; import android.util.Slog; import android.view.IRecentsAnimationRunner; +import com.android.server.wm.RecentsAnimationController; import com.android.server.wm.RecentsAnimationController.RecentsAnimationCallbacks; import com.android.server.wm.WindowManagerService; @@ -123,7 +126,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks { // Fetch all the surface controls and pass them to the client to get the animation // started - mWindowManager.cancelRecentsAnimation(); + mWindowManager.cancelRecentsAnimation(REORDER_MOVE_HOME_TO_ORIGINAL_POSITION); mWindowManager.initializeRecentsAnimation(recentsAnimationRunner, this, display.mDisplayId, mStackSupervisor.mRecentTasks.getRecentTaskIds()); @@ -140,7 +143,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks { } @Override - public void onAnimationFinished(boolean moveHomeToTop) { + public void onAnimationFinished(@RecentsAnimationController.ReorderMode int reorderMode) { synchronized (mService) { if (mWindowManager.getRecentsAnimationController() == null) return; @@ -151,9 +154,8 @@ class RecentsAnimation implements RecentsAnimationCallbacks { "RecentsAnimation#onAnimationFinished_inSurfaceTransaction"); mWindowManager.deferSurfaceLayout(); try { - mWindowManager.cleanupRecentsAnimation(moveHomeToTop); + mWindowManager.cleanupRecentsAnimation(reorderMode); - // Move the home stack to the front final ActivityRecord homeActivity = mStackSupervisor.getHomeActivity(); if (homeActivity == null) { return; @@ -162,15 +164,19 @@ class RecentsAnimation implements RecentsAnimationCallbacks { // Restore the launched-behind state homeActivity.mLaunchTaskBehind = false; - if (moveHomeToTop) { + if (reorderMode == REORDER_MOVE_HOME_TO_TOP) { // Bring the home stack to the front final ActivityStack homeStack = homeActivity.getStack(); mStackSupervisor.mNoAnimActivities.add(homeActivity); homeStack.moveToFront("RecentsAnimation.onAnimationFinished()"); - } else { + } else if (reorderMode == REORDER_MOVE_HOME_TO_ORIGINAL_POSITION){ // Restore the home stack to its previous position final ActivityDisplay display = homeActivity.getDisplay(); display.moveHomeStackBehindStack(mRestoreHomeBehindStack); + } else { + // Keep home stack in place, nothing changes, so ignore the transition logic + // below + return; } mWindowManager.prepareAppTransition(TRANSIT_NONE, false); diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java index ba67ff6a678ec..112d93c750597 100644 --- a/services/core/java/com/android/server/wm/BoundsAnimationController.java +++ b/services/core/java/com/android/server/wm/BoundsAnimationController.java @@ -161,7 +161,10 @@ public class BoundsAnimationController { // Timeout callback to ensure we continue the animation if waiting for resuming or app // windows drawn fails - private final Runnable mResumeRunnable = () -> resume(); + private final Runnable mResumeRunnable = () -> { + if (DEBUG) Slog.d(TAG, "pause: timed out waiting for windows drawn"); + resume(); + }; BoundsAnimator(BoundsAnimationTarget target, Rect from, Rect to, @SchedulePipModeChangedState int schedulePipModeChangedState, @@ -213,7 +216,7 @@ public class BoundsAnimationController { // When starting an animation from fullscreen, pause here and wait for the // windows-drawn signal before we start the rest of the transition down into PiP. - if (mMoveFromFullscreen) { + if (mMoveFromFullscreen && mTarget.shouldDeferStartOnMoveToFullscreen()) { pause(); } } else if (mPrevSchedulePipModeChangedState == SCHEDULE_PIP_MODE_CHANGED_ON_END && diff --git a/services/core/java/com/android/server/wm/BoundsAnimationTarget.java b/services/core/java/com/android/server/wm/BoundsAnimationTarget.java index 647a2d6deac62..68be4e8420a29 100644 --- a/services/core/java/com/android/server/wm/BoundsAnimationTarget.java +++ b/services/core/java/com/android/server/wm/BoundsAnimationTarget.java @@ -33,6 +33,12 @@ interface BoundsAnimationTarget { */ void onAnimationStart(boolean schedulePipModeChangedCallback, boolean forceUpdate); + /** + * @return Whether the animation should be paused waiting for the windows to draw before + * entering PiP. + */ + boolean shouldDeferStartOnMoveToFullscreen(); + /** * Sets the size of the target (without any intermediate steps, like scheduling animation) * but freezes the bounds of any tasks in the target at taskBounds, to allow for more diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index 7274aee3c9872..29077f97ccdc6 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -29,6 +29,7 @@ import static com.android.server.wm.WindowManagerService.H.NOTIFY_APP_TRANSITION import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET; import static com.android.server.wm.AnimationAdapterProto.REMOTE; +import android.annotation.IntDef; import android.app.ActivityManager.TaskSnapshot; import android.app.WindowConfiguration; import android.graphics.Point; @@ -67,12 +68,25 @@ public class RecentsAnimationController implements DeathRecipient { private static final boolean DEBUG = false; private static final long FAILSAFE_DELAY = 1000; + public static final int REORDER_KEEP_HOME_IN_PLACE = 0; + public static final int REORDER_MOVE_HOME_TO_TOP = 1; + public static final int REORDER_MOVE_HOME_TO_ORIGINAL_POSITION = 2; + + @IntDef(prefix = { "REORDER_MODE_" }, value = { + REORDER_KEEP_HOME_IN_PLACE, + REORDER_MOVE_HOME_TO_TOP, + REORDER_MOVE_HOME_TO_ORIGINAL_POSITION + }) + public @interface ReorderMode {} + private final WindowManagerService mService; private final IRecentsAnimationRunner mRunner; private final RecentsAnimationCallbacks mCallbacks; private final ArrayList mPendingAnimations = new ArrayList<>(); private final int mDisplayId; - private final Runnable mFailsafeRunnable = this::cancelAnimation; + private final Runnable mFailsafeRunnable = () -> { + cancelAnimation(REORDER_MOVE_HOME_TO_ORIGINAL_POSITION); + }; // The recents component app token that is shown behind the visibile tasks private AppWindowToken mHomeAppToken; @@ -93,7 +107,7 @@ public class RecentsAnimationController implements DeathRecipient { private Rect mTmpRect = new Rect(); public interface RecentsAnimationCallbacks { - void onAnimationFinished(boolean moveHomeToTop); + void onAnimationFinished(@ReorderMode int reorderMode); } private final IRecentsAnimationController mController = @@ -141,7 +155,9 @@ public class RecentsAnimationController implements DeathRecipient { // Note, the callback will handle its own synchronization, do not lock on WM lock // prior to calling the callback - mCallbacks.onAnimationFinished(moveHomeToTop); + mCallbacks.onAnimationFinished(moveHomeToTop + ? REORDER_MOVE_HOME_TO_TOP + : REORDER_MOVE_HOME_TO_ORIGINAL_POSITION); } finally { Binder.restoreCallingIdentity(token); } @@ -222,14 +238,14 @@ public class RecentsAnimationController implements DeathRecipient { // Skip the animation if there is nothing to animate if (mPendingAnimations.isEmpty()) { - cancelAnimation(); + cancelAnimation(REORDER_MOVE_HOME_TO_ORIGINAL_POSITION); return; } try { mRunner.asBinder().linkToDeath(this, 0); } catch (RemoteException e) { - cancelAnimation(); + cancelAnimation(REORDER_MOVE_HOME_TO_ORIGINAL_POSITION); return; } @@ -299,7 +315,7 @@ public class RecentsAnimationController implements DeathRecipient { reasons).sendToTarget(); } - void cancelAnimation() { + void cancelAnimation(@ReorderMode int reorderMode) { if (DEBUG) Log.d(TAG, "cancelAnimation()"); synchronized (mService.getWindowManagerLock()) { if (mCanceled) { @@ -316,16 +332,17 @@ public class RecentsAnimationController implements DeathRecipient { } // Clean up and return to the previous app // Don't hold the WM lock here as it calls back to AM/RecentsAnimation - mCallbacks.onAnimationFinished(false /* moveHomeToTop */); + mCallbacks.onAnimationFinished(reorderMode); } - void cleanupAnimation(boolean moveHomeToTop) { + void cleanupAnimation(@ReorderMode int reorderMode) { if (DEBUG) Log.d(TAG, "cleanupAnimation(): mPendingAnimations=" + mPendingAnimations.size()); for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { final TaskAnimationAdapter adapter = mPendingAnimations.get(i); adapter.mTask.setCanAffectSystemUiFlags(true); - if (moveHomeToTop) { + if (reorderMode == REORDER_MOVE_HOME_TO_TOP + || reorderMode == REORDER_KEEP_HOME_IN_PLACE) { adapter.mTask.dontAnimateDimExit(); } adapter.mCapturedFinishCallback.onAnimationFinished(adapter); @@ -344,7 +361,7 @@ public class RecentsAnimationController implements DeathRecipient { @Override public void binderDied() { - cancelAnimation(); + cancelAnimation(REORDER_MOVE_HOME_TO_ORIGINAL_POSITION); } void checkAnimationReady(WallpaperController wallpaperController) { @@ -458,7 +475,7 @@ public class RecentsAnimationController implements DeathRecipient { @Override public void onAnimationCancelled(SurfaceControl animationLeash) { - cancelAnimation(); + cancelAnimation(REORDER_MOVE_HOME_TO_ORIGINAL_POSITION); } @Override diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index 62754ada7900d..c97f394b39b7a 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -33,8 +33,6 @@ import static android.view.WindowManager.DOCKED_LEFT; import static android.view.WindowManager.DOCKED_RIGHT; import static android.view.WindowManager.DOCKED_TOP; import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER; -import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT; -import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import static com.android.server.wm.StackProto.ADJUSTED_BOUNDS; import static com.android.server.wm.StackProto.ADJUSTED_FOR_IME; import static com.android.server.wm.StackProto.ADJUST_DIVIDER_AMOUNT; @@ -47,6 +45,8 @@ import static com.android.server.wm.StackProto.ID; import static com.android.server.wm.StackProto.MINIMIZE_AMOUNT; import static com.android.server.wm.StackProto.TASKS; import static com.android.server.wm.StackProto.WINDOW_CONTAINER; +import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_MOVEMENT; +import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import android.annotation.CallSuper; import android.content.res.Configuration; @@ -62,12 +62,10 @@ import android.util.proto.ProtoOutputStream; import android.view.DisplayInfo; import android.view.Surface; import android.view.SurfaceControl; - import com.android.internal.policy.DividerSnapAlgorithm; import com.android.internal.policy.DividerSnapAlgorithm.SnapTarget; import com.android.internal.policy.DockedDividerUtils; import com.android.server.EventLogTags; - import java.io.PrintWriter; public class TaskStack extends WindowContainer implements @@ -1687,6 +1685,25 @@ public class TaskStack extends WindowContainer implements } } + @Override + public boolean shouldDeferStartOnMoveToFullscreen() { + // Workaround for the recents animation -- normally we need to wait for the new activity to + // show before starting the PiP animation, but because we start and show the home activity + // early for the recents animation prior to the PiP animation starting, there is no + // subsequent all-drawn signal. In this case, we can skip the pause when the home stack is + // already visible and drawn. + final TaskStack homeStack = mDisplayContent.getHomeStack(); + if (homeStack == null) { + return true; + } + final Task homeTask = homeStack.getTopChild(); + final AppWindowToken homeApp = homeTask.getTopVisibleAppToken(); + if (!homeTask.isVisible() || homeApp == null) { + return true; + } + return !homeApp.allDrawn; + } + /** * @return True if we are currently animating the pinned stack from fullscreen to non-fullscreen * bounds and we have a deferred PiP mode changed callback set with the animation. diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java index c509980e1e9e2..6de1579292438 100644 --- a/services/core/java/com/android/server/wm/WallpaperController.java +++ b/services/core/java/com/android/server/wm/WallpaperController.java @@ -26,6 +26,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_WITH_WALLPAPER; +import static com.android.server.wm.RecentsAnimationController.REORDER_MOVE_HOME_TO_ORIGINAL_POSITION; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER; @@ -618,7 +619,8 @@ class WallpaperController { // If there was a recents animation in progress, cancel that animation if (mService.getRecentsAnimationController() != null) { - mService.getRecentsAnimationController().cancelAnimation(); + mService.getRecentsAnimationController().cancelAnimation( + REORDER_MOVE_HOME_TO_ORIGINAL_POSITION); } return true; } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index f1cd46bc16d01..28a57f5a46956 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2687,20 +2687,20 @@ public class WindowManagerService extends IWindowManager.Stub } } - public void cancelRecentsAnimation() { + public void cancelRecentsAnimation(@RecentsAnimationController.ReorderMode int reorderMode) { // Note: Do not hold the WM lock, this will lock appropriately in the call which also // calls through to AM/RecentsAnimation.onAnimationFinished() if (mRecentsAnimationController != null) { // This call will call through to cleanupAnimation() below after the animation is // canceled - mRecentsAnimationController.cancelAnimation(); + mRecentsAnimationController.cancelAnimation(reorderMode); } } - public void cleanupRecentsAnimation(boolean moveHomeToTop) { + public void cleanupRecentsAnimation(@RecentsAnimationController.ReorderMode int reorderMode) { synchronized (mWindowMap) { if (mRecentsAnimationController != null) { - mRecentsAnimationController.cleanupAnimation(moveHomeToTop); + mRecentsAnimationController.cleanupAnimation(reorderMode); mRecentsAnimationController = null; mAppTransition.updateBooster(); } diff --git a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java index 77653872e5ee8..6290ff191847b 100644 --- a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java +++ b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java @@ -649,7 +649,7 @@ public class RecentTasksTest extends ActivityTestsBase { assertSecurityException(expectCallable, () -> mService.cancelTaskWindowTransition(0)); assertSecurityException(expectCallable, () -> mService.startRecentsActivity(null, null, null)); - assertSecurityException(expectCallable, () -> mService.cancelRecentsAnimation()); + assertSecurityException(expectCallable, () -> mService.cancelRecentsAnimation(true)); } private void testGetTasksApis(boolean expectCallable) { diff --git a/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java index 2bfe2742d419b..6019958d06501 100644 --- a/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java @@ -158,6 +158,11 @@ public class BoundsAnimationControllerTests extends WindowTestsBase { mForcePipModeChangedCallback = forceUpdate; } + @Override + public boolean shouldDeferStartOnMoveToFullscreen() { + return true; + } + @Override public boolean setPinnedStackSize(Rect stackBounds, Rect taskBounds) { // TODO: Once we break the runs apart, we should fail() here if this is called outside From f557c3b56547de332b1f116f316b3a81dabd230d Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Fri, 16 Mar 2018 10:55:20 -0700 Subject: [PATCH 2/3] Allow recents animation to override divider minimized state - When swiping up while in split-screen, the animation should be able to adjust the docked divider minimized state so that we don't wait until launcher is repositioned to the front in order for the divider to update. Bug: 73118672 Test: Enter split screen, swipe up and ensure the dock divider moves (Note, there are still unrelated clipping issues) Change-Id: Id71f91eaf2f06b3c33628a2199cc94c82e235471 --- .../view/IRecentsAnimationController.aidl | 5 +++ .../RecentsAnimationControllerCompat.java | 8 ++++ .../android/server/am/RecentsAnimation.java | 5 +++ .../wm/DockedStackDividerController.java | 5 ++- .../server/wm/RecentsAnimationController.java | 38 ++++++++++++++++--- 5 files changed, 54 insertions(+), 7 deletions(-) diff --git a/core/java/android/view/IRecentsAnimationController.aidl b/core/java/android/view/IRecentsAnimationController.aidl index 89684ca46003c..c0b40c8ceb3b9 100644 --- a/core/java/android/view/IRecentsAnimationController.aidl +++ b/core/java/android/view/IRecentsAnimationController.aidl @@ -59,4 +59,9 @@ interface IRecentsAnimationController { * taken. */ void setAnimationTargetsBehindSystemBars(boolean behindSystemBars); + + /** + * Informs the system that the primary split-screen stack should be minimized. + */ + void setSplitScreenMinimized(boolean minimized); } 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 5fa6c79a6e85f..80e226d929cd5 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 @@ -61,6 +61,14 @@ public class RecentsAnimationControllerCompat { } } + public void setSplitScreenMinimized(boolean minimized) { + try { + mAnimationController.setSplitScreenMinimized(minimized); + } catch (RemoteException e) { + Log.e(TAG, "Failed to set minimize dock", e); + } + } + public void finish(boolean toHome) { try { mAnimationController.finish(toHome); diff --git a/services/core/java/com/android/server/am/RecentsAnimation.java b/services/core/java/com/android/server/am/RecentsAnimation.java index 5e74619be3834..99337b83d0017 100644 --- a/services/core/java/com/android/server/am/RecentsAnimation.java +++ b/services/core/java/com/android/server/am/RecentsAnimation.java @@ -186,6 +186,11 @@ class RecentsAnimation implements RecentsAnimationCallbacks { // No reason to wait for the pausing activity in this case, as the hiding of // surfaces needs to be done immediately. mWindowManager.executeAppTransition(); + + // After reordering the stacks, reset the minimized state. At this point, either + // home is now top-most and we will stay minimized (if in split-screen), or we + // will have returned to the app, and the minimized state should be reset + mWindowManager.checkSplitScreenMinimizedChanged(true /* animate */); } finally { mWindowManager.continueSurfaceLayout(); Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java index 5e2bb10fe629b..2cd2ef1203cb5 100644 --- a/services/core/java/com/android/server/wm/DockedStackDividerController.java +++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java @@ -666,13 +666,16 @@ public class DockedStackDividerController { } final TaskStack topSecondaryStack = mDisplayContent.getTopStackInWindowingMode( WINDOWING_MODE_SPLIT_SCREEN_SECONDARY); + final RecentsAnimationController recentsAnim = mService.getRecentsAnimationController(); + final boolean minimizedForRecentsAnimation = recentsAnim != null && + recentsAnim.isSplitScreenMinimized(); boolean homeVisible = homeTask.getTopVisibleAppToken() != null; if (homeVisible && topSecondaryStack != null) { // Home should only be considered visible if it is greater or equal to the top secondary // stack in terms of z-order. homeVisible = homeStack.compareTo(topSecondaryStack) >= 0; } - setMinimizedDockedStack(homeVisible, animate); + setMinimizedDockedStack(homeVisible || minimizedForRecentsAnimation, animate); } private boolean isWithinDisplay(Task task) { diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index 29077f97ccdc6..b12c36e6aa969 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -98,12 +98,16 @@ public class RecentsAnimationController implements DeathRecipient { private boolean mPendingStart = true; // Set when the animation has been canceled - private boolean mCanceled = false; + private boolean mCanceled; // Whether or not the input consumer is enabled. The input consumer must be both registered and // enabled for it to start intercepting touch events. private boolean mInputConsumerEnabled; + // Whether or not the recents animation should cause the primary split-screen stack to be + // minimized + private boolean mSplitScreenMinimized; + private Rect mTmpRect = new Rect(); public interface RecentsAnimationCallbacks { @@ -116,7 +120,7 @@ public class RecentsAnimationController implements DeathRecipient { @Override public TaskSnapshot screenshotTask(int taskId) { if (DEBUG) Log.d(TAG, "screenshotTask(" + taskId + "): mCanceled=" + mCanceled); - long token = Binder.clearCallingIdentity(); + final long token = Binder.clearCallingIdentity(); try { synchronized (mService.getWindowManagerLock()) { if (mCanceled) { @@ -145,7 +149,7 @@ public class RecentsAnimationController implements DeathRecipient { @Override public void finish(boolean moveHomeToTop) { if (DEBUG) Log.d(TAG, "finish(" + moveHomeToTop + "): mCanceled=" + mCanceled); - long token = Binder.clearCallingIdentity(); + final long token = Binder.clearCallingIdentity(); try { synchronized (mService.getWindowManagerLock()) { if (mCanceled) { @@ -166,7 +170,7 @@ public class RecentsAnimationController implements DeathRecipient { @Override public void setAnimationTargetsBehindSystemBars(boolean behindSystemBars) throws RemoteException { - long token = Binder.clearCallingIdentity(); + final long token = Binder.clearCallingIdentity(); try { synchronized (mService.getWindowManagerLock()) { for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { @@ -183,7 +187,7 @@ public class RecentsAnimationController implements DeathRecipient { public void setInputConsumerEnabled(boolean enabled) { if (DEBUG) Log.d(TAG, "setInputConsumerEnabled(" + enabled + "): mCanceled=" + mCanceled); - long token = Binder.clearCallingIdentity(); + final long token = Binder.clearCallingIdentity(); try { synchronized (mService.getWindowManagerLock()) { if (mCanceled) { @@ -198,6 +202,23 @@ public class RecentsAnimationController implements DeathRecipient { Binder.restoreCallingIdentity(token); } } + + @Override + public void setSplitScreenMinimized(boolean minimized) { + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mService.getWindowManagerLock()) { + if (mCanceled) { + return; + } + + mSplitScreenMinimized = minimized; + mService.checkSplitScreenMinimizedChanged(true /* animate */); + } + } finally { + Binder.restoreCallingIdentity(token); + } + } }; /** @@ -330,6 +351,7 @@ public class RecentsAnimationController implements DeathRecipient { Slog.e(TAG, "Failed to cancel recents animation", e); } } + // Clean up and return to the previous app // Don't hold the WM lock here as it calls back to AM/RecentsAnimation mCallbacks.onAnimationFinished(reorderMode); @@ -350,7 +372,7 @@ public class RecentsAnimationController implements DeathRecipient { mPendingAnimations.clear(); mRunner.asBinder().unlinkToDeath(this, 0); - + // Clear associated input consumers mService.mInputMonitor.updateInputWindowsLw(true /*force*/); mService.destroyInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION); } @@ -375,6 +397,10 @@ public class RecentsAnimationController implements DeathRecipient { } } + boolean isSplitScreenMinimized() { + return mSplitScreenMinimized; + } + boolean isWallpaperVisible(WindowState w) { return w != null && w.mAppToken != null && mHomeAppToken == w.mAppToken && isHomeAppOverWallpaper(); From d41f71d21806ce7950ed91662b2ddb7f615c3c1f Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Fri, 16 Mar 2018 15:26:07 -0700 Subject: [PATCH 3/3] Reparent recents animation task leash to app animation layer - When swiping up in split screen, the task is clipped by the unminimized stack bounds, instead reparent to app animation layer to allow it to be unconstrained. Currently, this will bump it up to the top, but we need a mechanism later to bump it only to under the primary splitscreen stack. Bug: 73118672 Test: Swipe up from split screen, ensure app window is not clipped Change-Id: Iacc6d6b82274b8b26b2dab85628479b5ff490817 --- .../com/android/server/wm/DisplayContent.java | 3 ++- .../server/wm/RecentsAnimationController.java | 22 ++++++++++++++----- .../core/java/com/android/server/wm/Task.java | 18 +++++++++++++++ .../java/com/android/server/wm/TaskStack.java | 14 ++++++++++++ 4 files changed, 50 insertions(+), 7 deletions(-) diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index c9ff9e3ba35b2..d77985c1d9c6e 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -3566,7 +3566,8 @@ class DisplayContent extends WindowContainer= 0; i--) { + if (task == mPendingAnimations.get(i).mTask) { + return true; + } + } + return false; + } + private boolean isAnimatingApp(AppWindowToken appToken) { for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { final Task task = mPendingAnimations.get(i).mTask; @@ -452,18 +461,18 @@ public class RecentsAnimationController implements DeathRecipient { private OnAnimationFinishedCallback mCapturedFinishCallback; private final boolean mIsRecentTaskInvisible; private RemoteAnimationTarget mTarget; + private final Point mPosition = new Point(); + private final Rect mBounds = new Rect(); TaskAnimationAdapter(Task task, boolean isRecentTaskInvisible) { mTask = task; mIsRecentTaskInvisible = isRecentTaskInvisible; + final WindowContainer container = mTask.getParent(); + container.getRelativePosition(mPosition); + container.getBounds(mBounds); } RemoteAnimationTarget createRemoteAnimationApp() { - final Point position = new Point(); - final Rect bounds = new Rect(); - final WindowContainer container = mTask.getParent(); - container.getRelativePosition(position); - container.getBounds(bounds); final WindowState mainWindow = mTask.getTopVisibleAppMainWindow(); if (mainWindow == null) { return null; @@ -472,7 +481,7 @@ public class RecentsAnimationController implements DeathRecipient { InsetUtils.addInsets(insets, mainWindow.mAppToken.getLetterboxInsets()); mTarget = new RemoteAnimationTarget(mTask.mTaskId, MODE_CLOSING, mCapturedLeash, !mTask.fillsParent(), mainWindow.mWinAnimator.mLastClipRect, - insets, mTask.getPrefixOrderIndex(), position, bounds, + insets, mTask.getPrefixOrderIndex(), mPosition, mBounds, mTask.getWindowConfiguration(), mIsRecentTaskInvisible); return mTarget; } @@ -495,6 +504,7 @@ public class RecentsAnimationController implements DeathRecipient { @Override public void startAnimation(SurfaceControl animationLeash, Transaction t, OnAnimationFinishedCallback finishCallback) { + t.setPosition(animationLeash, mPosition.x, mPosition.y); mCapturedLeash = animationLeash; mCapturedFinishCallback = finishCallback; } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index e8d321099f000..95223d84635ad 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -44,6 +44,7 @@ import android.util.Slog; import android.util.proto.ProtoOutputStream; import android.view.Surface; +import android.view.SurfaceControl; import com.android.internal.annotations.VisibleForTesting; import java.io.PrintWriter; @@ -559,6 +560,23 @@ class Task extends WindowContainer { && !mStack.isAnimatingBoundsToFullscreen() && !mPreserveNonFloatingState; } + @Override + public SurfaceControl getAnimationLeashParent() { + // Reparent to the animation layer so that we aren't clipped by the non-minimized + // stack bounds, currently we only animate the task for the recents animation + return getAppAnimationLayer(false /* boosted */); + } + + boolean isTaskAnimating() { + final RecentsAnimationController recentsAnim = mService.getRecentsAnimationController(); + if (recentsAnim != null) { + if (recentsAnim.isAnimatingTask(this)) { + return true; + } + } + return false; + } + WindowState getTopVisibleAppMainWindow() { final AppWindowToken token = getTopVisibleAppToken(); return token != null ? token.findMainWindow() : null; diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index c97f394b39b7a..2175c6be5b5f7 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -1331,6 +1331,20 @@ public class TaskStack extends WindowContainer implements return mMinimizeAmount != 0f; } + /** + * @return {@code true} if we have a {@link Task} that is animating (currently only used for the + * recents animation); {@code false} otherwise. + */ + boolean isTaskAnimating() { + for (int j = mChildren.size() - 1; j >= 0; j--) { + final Task task = mChildren.get(j); + if (task.isTaskAnimating()) { + return true; + } + } + return false; + } + @CallSuper @Override public void writeToProto(ProtoOutputStream proto, long fieldId, boolean trim) {