From a2eaf83a719d5fff5ee72a488b556e1cbb252fd3 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Wed, 23 Jun 2021 22:05:51 -0700 Subject: [PATCH] Fix flash when canceling the recents animation with a screenshot - When canceling because of starting home, use the screenshot call to allow Launcher to handle the cancel more cleanly - Since the cancel+screenshot path was added, we no longer cancel on root task order change (we just get onTaskAppeared now), it's purely a deferred path for the runner to handle the cancel in a clean way. So instead of having a separate animator, just add a screenshot surface to the existing leash, and clean up everything when the runner finishes the recents animation. This prevents the issue where transferring the animation causes the task to be reparented from the leash and made immediately visible (and fullscreen) - Remove some unused code Fixes: 185643608 Test: atest RecentsAnimationControllerTests Test: Manually enable live-tile for 2/3 button, tap home and ensure there's no flash Change-Id: I57775e5df55ae80931ad02c21f190f29ffa76881 --- .../wm/ActivityTaskManagerInternal.java | 5 - .../server/wm/ActivityTaskManagerService.java | 5 - .../server/wm/RecentsAnimationController.java | 155 +++++++++++------- .../server/wm/RootWindowContainer.java | 4 +- .../server/wm/TaskScreenshotAnimatable.java | 120 -------------- .../wm/RecentsAnimationControllerTest.java | 36 +++- 6 files changed, 124 insertions(+), 201 deletions(-) delete mode 100644 services/core/java/com/android/server/wm/TaskScreenshotAnimatable.java diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java index 01ee3be08f5f3..1759cdeb60d7f 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java @@ -279,11 +279,6 @@ public abstract class ActivityTaskManagerInternal { */ public abstract boolean isRecentsComponentHomeActivity(int userId); - /** - * Cancels any currently running recents animation. - */ - public abstract void cancelRecentsAnimation(boolean restoreHomeRootTaskPosition); - /** * Returns true if the app can close system dialogs. Otherwise it either throws a {@link * SecurityException} or returns false with a logcat message depending on whether the app diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 5e75cebc322df..7bd33741311ae 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -5255,11 +5255,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { return getRecentTasks().isRecentsComponentHomeActivity(userId); } - @Override - public void cancelRecentsAnimation(boolean restoreHomeRootTaskPosition) { - ActivityTaskManagerService.this.cancelRecentsAnimation(restoreHomeRootTaskPosition); - } - @Override public boolean checkCanCloseSystemDialogs(int pid, int uid, @Nullable String packageName) { return ActivityTaskManagerService.this.checkCanCloseSystemDialogs(pid, uid, diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index a7e633527b541..710dd552f72d2 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -36,8 +36,10 @@ import static com.android.server.wm.WindowManagerInternal.AppTransitionListener; import android.annotation.IntDef; import android.annotation.NonNull; import android.app.WindowConfiguration; +import android.graphics.GraphicBuffer; import android.graphics.Point; import android.graphics.Rect; +import android.hardware.HardwareBuffer; import android.os.Binder; import android.os.IBinder.DeathRecipient; import android.os.RemoteException; @@ -54,6 +56,7 @@ import android.view.InputWindowHandle; import android.view.RemoteAnimationTarget; import android.view.SurfaceControl; import android.view.SurfaceControl.Transaction; +import android.view.SurfaceSession; import android.view.WindowInsets.Type; import android.window.PictureInPictureSurfaceTransaction; import android.window.TaskSnapshot; @@ -150,6 +153,8 @@ public class RecentsAnimationController implements DeathRecipient { private boolean mCancelOnNextTransitionStart; // Whether to take a screenshot when handling a deferred cancel private boolean mCancelDeferredWithScreenshot; + // The reorder mode to apply after the cleanupScreenshot() callback + private int mPendingCancelWithScreenshotReorderMode = REORDER_MOVE_TO_ORIGINAL_POSITION; @VisibleForTesting boolean mIsAddingTaskToTargets; @@ -158,12 +163,6 @@ public class RecentsAnimationController implements DeathRecipient { private boolean mNavigationBarAttachedToApp; private ActivityRecord mNavBarAttachedApp; - /** - * Animates the screenshot of task that used to be controlled by RecentsAnimation. - * @see {@link #setCancelOnNextTransitionStart} - */ - SurfaceAnimator mRecentScreenshotAnimator; - /** * An app transition listener to cancel the recents animation only after the app transition * starts or is canceled. @@ -355,12 +354,9 @@ public class RecentsAnimationController implements DeathRecipient { public void cleanupScreenshot() { final long token = Binder.clearCallingIdentity(); try { - synchronized (mService.mGlobalLock) { - if (mRecentScreenshotAnimator != null) { - mRecentScreenshotAnimator.cancelAnimation(); - mRecentScreenshotAnimator = null; - } - } + // Note, the callback will handle its own synchronization, do not lock on WM lock + // prior to calling the callback + continueDeferredCancelAnimation(); } finally { Binder.restoreCallingIdentity(token); } @@ -519,13 +515,13 @@ public class RecentsAnimationController implements DeathRecipient { } @VisibleForTesting - AnimationAdapter addAnimation(Task task, boolean isRecentTaskInvisible) { + TaskAnimationAdapter addAnimation(Task task, boolean isRecentTaskInvisible) { return addAnimation(task, isRecentTaskInvisible, false /* hidden */, null /* finishedCallback */); } @VisibleForTesting - AnimationAdapter addAnimation(Task task, boolean isRecentTaskInvisible, boolean hidden, + TaskAnimationAdapter addAnimation(Task task, boolean isRecentTaskInvisible, boolean hidden, OnAnimationFinishedCallback finishedCallback) { ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "addAnimation(%s)", task.getName()); final TaskAnimationAdapter taskAdapter = new TaskAnimationAdapter(task, @@ -541,9 +537,7 @@ public class RecentsAnimationController implements DeathRecipient { void removeAnimation(TaskAnimationAdapter taskAdapter) { ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "removeAnimation(%d)", taskAdapter.mTask.mTaskId); - taskAdapter.mTask.setCanAffectSystemUiFlags(true); - taskAdapter.mCapturedFinishCallback.onAnimationFinished(taskAdapter.mLastAnimationType, - taskAdapter); + taskAdapter.onRemove(); mPendingAnimations.remove(taskAdapter); } @@ -821,8 +815,22 @@ public class RecentsAnimationController implements DeathRecipient { } /** - * If there is a recents animation running, we need to cancel the animation and snapshot the - * tasks before the change (to ensure they are captured at the right configuration) + * Cancels the running animation when starting home, providing a snapshot for the runner to + * properly handle the cancellation. This call uses the provided hint to determine how to + * finish the animation. + */ + public void cancelAnimationForHomeStart() { + if (mCanceled) { + return; + } + cancelAnimation(mWillFinishToHome ? REORDER_MOVE_TO_TOP : REORDER_KEEP_IN_PLACE, + true /* screenshot */, "cancelAnimationForHomeStart"); + } + + /** + * Cancels the running animation when there is a display change, providing a snapshot for the + * runner to properly handle the cancellation. This call uses the provided hint to determine + * how to finish the animation. */ public void cancelAnimationForDisplayChange() { if (mCanceled) { @@ -843,16 +851,22 @@ public class RecentsAnimationController implements DeathRecipient { mCanceled = true; if (screenshot && !mPendingAnimations.isEmpty()) { + final TaskAnimationAdapter adapter = mPendingAnimations.get(0); + final Task task = adapter.mTask; // Screen shot previous task when next task starts transition and notify the runner. // We will actually finish the animation once the runner calls cleanUpScreenshot(). - final Task task = mPendingAnimations.get(0).mTask; - final TaskSnapshot taskSnapshot = screenshotRecentTask(task, reorderMode); + final TaskSnapshot taskSnapshot = screenshotRecentTask(task); + mPendingCancelWithScreenshotReorderMode = reorderMode; try { mRunner.onAnimationCanceled(taskSnapshot); } catch (RemoteException e) { Slog.e(TAG, "Failed to cancel recents animation", e); } - if (taskSnapshot == null) { + if (taskSnapshot != null) { + // Defer until the runner calls back to cleanupScreenshot() + adapter.setSnapshotOverlay(taskSnapshot); + } else { + // Do a normal cancel since we couldn't screenshot mCallbacks.onAnimationFinished(reorderMode, false /* sendUserLeaveHint */); } } else { @@ -869,6 +883,12 @@ public class RecentsAnimationController implements DeathRecipient { } } + @VisibleForTesting + void continueDeferredCancelAnimation() { + mCallbacks.onAnimationFinished(mPendingCancelWithScreenshotReorderMode, + false /* sendUserLeaveHint */); + } + @VisibleForTesting void setWillFinishToHome(boolean willFinishToHome) { mWillFinishToHome = willFinishToHome; @@ -915,29 +935,13 @@ public class RecentsAnimationController implements DeathRecipient { return mRequestDeferCancelUntilNextTransition && mCancelDeferredWithScreenshot; } - TaskSnapshot screenshotRecentTask(Task task, @ReorderMode int reorderMode) { + TaskSnapshot screenshotRecentTask(Task task) { final TaskSnapshotController snapshotController = mService.mTaskSnapshotController; final ArraySet tasks = Sets.newArraySet(task); snapshotController.snapshotTasks(tasks); snapshotController.addSkipClosingAppSnapshotTasks(tasks); - final TaskSnapshot taskSnapshot = snapshotController.getSnapshot(task.mTaskId, - task.mUserId, false /* restoreFromDisk */, false /* isLowResolution */); - if (taskSnapshot == null) { - return null; - } - - final TaskScreenshotAnimatable animatable = new TaskScreenshotAnimatable( - mService.mSurfaceControlFactory, task, - new SurfaceControl.ScreenshotHardwareBuffer(taskSnapshot.getHardwareBuffer(), - taskSnapshot.getColorSpace(), false /* containsSecureLayers */)); - mRecentScreenshotAnimator = new SurfaceAnimator( - animatable, - (type, anim) -> { - ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "mRecentScreenshotAnimator finish"); - mCallbacks.onAnimationFinished(reorderMode, false /* sendUserLeaveHint */); - }, mService); - mRecentScreenshotAnimator.transferAnimation(task.mSurfaceAnimator); - return taskSnapshot; + return snapshotController.getSnapshot(task.mTaskId, task.mUserId, + false /* restoreFromDisk */, false /* isLowResolution */); } void cleanupAnimation(@ReorderMode int reorderMode) { @@ -976,12 +980,6 @@ public class RecentsAnimationController implements DeathRecipient { mRunner = null; mCanceled = true; - // Make sure previous animator has cleaned-up. - if (mRecentScreenshotAnimator != null) { - mRecentScreenshotAnimator.cancelAnimation(); - mRecentScreenshotAnimator = null; - } - // Restore IME icon only when moving the original app task to front from recents, in case // IME icon may missing if the moving task has already been the current focused task. if (reorderMode == REORDER_MOVE_TO_ORIGINAL_POSITION && !mIsAddingTaskToTargets) { @@ -1028,16 +1026,7 @@ public class RecentsAnimationController implements DeathRecipient { @Override public void binderDied() { - if (!mCanceled) { - cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "binderDied"); - } else { - synchronized (mService.getWindowManagerLock()) { - if (mRecentScreenshotAnimator != null) { - mRecentScreenshotAnimator.cancelAnimation(); - mRecentScreenshotAnimator = null; - } - } - } + cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "binderDied"); synchronized (mService.getWindowManagerLock()) { // Clear associated input consumers on runner death @@ -1171,6 +1160,8 @@ public class RecentsAnimationController implements DeathRecipient { private PictureInPictureSurfaceTransaction mFinishTransaction; // An overlay used to mask the content as an app goes into PIP private SurfaceControl mFinishOverlay; + // An overlay used for canceling the animation with a screenshot + private SurfaceControl mSnapshotOverlay; TaskAnimationAdapter(Task task, boolean isRecentTaskInvisible) { mTask = task; @@ -1205,10 +1196,47 @@ public class RecentsAnimationController implements DeathRecipient { return mTarget; } - void onCleanup() { - if (mFinishTransaction != null) { - final Transaction pendingTransaction = mTask.getPendingTransaction(); + void setSnapshotOverlay(TaskSnapshot snapshot) { + // Create a surface control for the snapshot and reparent it to the leash + final HardwareBuffer buffer = snapshot.getHardwareBuffer(); + if (buffer == null) { + return; + } + final SurfaceSession session = new SurfaceSession(); + mSnapshotOverlay = mService.mSurfaceControlFactory.apply(session) + .setName("RecentTaskScreenshotSurface") + .setCallsite("TaskAnimationAdapter.setSnapshotOverlay") + .setFormat(buffer.getFormat()) + .setParent(mCapturedLeash) + .setBLASTLayer() + .build(); + + final float scale = 1.0f * mTask.getBounds().width() / buffer.getWidth(); + mTask.getPendingTransaction() + .setBuffer(mSnapshotOverlay, GraphicBuffer.createFromHardwareBuffer(buffer)) + .setColorSpace(mSnapshotOverlay, snapshot.getColorSpace()) + .setLayer(mSnapshotOverlay, Integer.MAX_VALUE) + .setMatrix(mSnapshotOverlay, scale, 0, 0, scale) + .show(mSnapshotOverlay) + .apply(); + } + + void onRemove() { + if (mSnapshotOverlay != null) { + // Clean up the snapshot overlay if necessary + mTask.getPendingTransaction() + .remove(mSnapshotOverlay) + .apply(); + mSnapshotOverlay = null; + } + mTask.setCanAffectSystemUiFlags(true); + mCapturedFinishCallback.onAnimationFinished(mLastAnimationType, this); + } + + void onCleanup() { + final Transaction pendingTransaction = mTask.getPendingTransaction(); + if (mFinishTransaction != null) { // Reparent the overlay if (mFinishOverlay != null) { pendingTransaction.reparent(mFinishOverlay, mTask.mSurfaceControl); @@ -1236,10 +1264,15 @@ public class RecentsAnimationController implements DeathRecipient { } else if (!mTask.isAttached()) { // Apply the task's pending transaction in case it is detached and its transaction // is not reachable. - mTask.getPendingTransaction().apply(); + pendingTransaction.apply(); } } + @VisibleForTesting + public SurfaceControl getSnapshotOverlay() { + return mSnapshotOverlay; + } + @Override public boolean getShowWallpaper() { return false; diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index ea80b8bb52869..b75d5009c48ee 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -1525,7 +1525,9 @@ class RootWindowContainer extends WindowContainer // Updates the extra information of the intent. if (fromHomeKey) { homeIntent.putExtra(WindowManagerPolicy.EXTRA_FROM_HOME_KEY, true); - mWindowManager.cancelRecentsAnimation(REORDER_KEEP_IN_PLACE, "startHomeActivity"); + if (mWindowManager.getRecentsAnimationController() != null) { + mWindowManager.getRecentsAnimationController().cancelAnimationForHomeStart(); + } } homeIntent.putExtra(WindowManagerPolicy.EXTRA_START_REASON, reason); diff --git a/services/core/java/com/android/server/wm/TaskScreenshotAnimatable.java b/services/core/java/com/android/server/wm/TaskScreenshotAnimatable.java deleted file mode 100644 index 7e992ac263bc2..0000000000000 --- a/services/core/java/com/android/server/wm/TaskScreenshotAnimatable.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (C) 2019 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.server.wm; - -import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS; - -import android.graphics.GraphicBuffer; -import android.hardware.HardwareBuffer; -import android.view.SurfaceControl; -import android.view.SurfaceSession; - -import com.android.internal.protolog.common.ProtoLog; - -import java.util.function.Function; - -/** - * Class used by {@link RecentsAnimationController} to create a surface control with taking - * screenshot of task when canceling recents animation. - * - * @see {@link RecentsAnimationController#setCancelOnNextTransitionStart} - */ -class TaskScreenshotAnimatable implements SurfaceAnimator.Animatable { - private static final String TAG = "TaskScreenshotAnim"; - private Task mTask; - private SurfaceControl mSurfaceControl; - private int mWidth; - private int mHeight; - - TaskScreenshotAnimatable(Function surfaceControlFactory, - Task task, SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer) { - HardwareBuffer buffer = screenshotBuffer == null - ? null : screenshotBuffer.getHardwareBuffer(); - mTask = task; - mWidth = (buffer != null) ? buffer.getWidth() : 1; - mHeight = (buffer != null) ? buffer.getHeight() : 1; - ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, - "Creating TaskScreenshotAnimatable: task: %s width: %d height: %d", - task, mWidth, mHeight); - mSurfaceControl = surfaceControlFactory.apply(new SurfaceSession()) - .setName("RecentTaskScreenshotSurface") - .setBLASTLayer() - .setCallsite("TaskScreenshotAnimatable") - .build(); - if (buffer != null) { - GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer(buffer); - getPendingTransaction().setBuffer(mSurfaceControl, graphicBuffer); - getPendingTransaction().setColorSpace(mSurfaceControl, - screenshotBuffer.getColorSpace()); - final float scale = 1.0f * mTask.getBounds().width() / mWidth; - getPendingTransaction().setMatrix(mSurfaceControl, scale, 0, 0, scale); - } - getPendingTransaction().show(mSurfaceControl); - } - - @Override - public SurfaceControl.Transaction getPendingTransaction() { - return mTask.getPendingTransaction(); - } - - @Override - public void commitPendingTransaction() { - mTask.commitPendingTransaction(); - } - - @Override - public void onAnimationLeashCreated(SurfaceControl.Transaction t, SurfaceControl leash) { - t.setLayer(leash, 1); - } - - @Override - public void onAnimationLeashLost(SurfaceControl.Transaction t) { - if (mSurfaceControl != null) { - t.remove(mSurfaceControl); - mSurfaceControl = null; - } - } - - @Override - public SurfaceControl.Builder makeAnimationLeash() { - return mTask.makeAnimationLeash(); - } - - @Override - public SurfaceControl getAnimationLeashParent() { - return mTask.getAnimationLeashParent(); - } - - @Override - public SurfaceControl getSurfaceControl() { - return mSurfaceControl; - } - - @Override - public SurfaceControl getParentSurfaceControl() { - return mTask.mSurfaceControl; - } - - @Override - public int getSurfaceWidth() { - return mWidth; - } - - @Override - public int getSurfaceHeight() { - return mHeight; - } -} diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java index afd0ecf0fdee5..03944172db7c9 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java @@ -255,7 +255,6 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { mController.setDeferredCancel(true /* deferred */, false /* screenshot */); mController.cancelAnimationWithScreenshot(false /* screenshot */); verify(mMockRunner).onAnimationCanceled(null /* taskSnapshot */); - assertNull(mController.mRecentScreenshotAnimator); // Simulate the app transition finishing mController.mAppTransitionListener.onAppTransitionStartingLocked(false, 0, 0, 0); @@ -271,7 +270,8 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { assertEquals(activity.getTask().getTopVisibleActivity(), activity); assertEquals(activity.findMainWindow(), win1); - mController.addAnimation(activity.getTask(), false /* isRecentTaskInvisible */); + RecentsAnimationController.TaskAnimationAdapter adapter = mController.addAnimation( + activity.getTask(), false /* isRecentTaskInvisible */); assertTrue(mController.isAnimatingTask(activity.getTask())); spyOn(mWm.mTaskSnapshotController); @@ -282,14 +282,9 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { mController.setDeferredCancel(true /* deferred */, true /* screenshot */); mController.cancelAnimationWithScreenshot(true /* screenshot */); verify(mMockRunner).onAnimationCanceled(mMockTaskSnapshot /* taskSnapshot */); - assertNotNull(mController.mRecentScreenshotAnimator); - assertTrue(mController.mRecentScreenshotAnimator.isAnimating()); - // Assume IRecentsAnimationController#cleanupScreenshot called to finish screenshot - // animation. - spyOn(mController.mRecentScreenshotAnimator.mAnimatable); - mController.mRecentScreenshotAnimator.cancelAnimation(); - verify(mController.mRecentScreenshotAnimator.mAnimatable).onAnimationLeashLost(any()); + // Continue the animation (simulating a call to cleanupScreenshot()) + mController.continueDeferredCancelAnimation(); verify(mAnimationCallbacks).onAnimationFinished(REORDER_KEEP_IN_PLACE, false); } @@ -685,6 +680,29 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { verify(mAnimationCallbacks).onAnimationFinished(REORDER_MOVE_TO_ORIGINAL_POSITION, false); } + @Test + public void testCancelForStartHome() throws Exception { + mWm.setRecentsAnimationController(mController); + final ActivityRecord activity = createActivityRecord(mDefaultDisplay); + final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, activity, "win1"); + activity.addWindow(win1); + + RecentsAnimationController.TaskAnimationAdapter adapter = mController.addAnimation( + activity.getTask(), false /* isRecentTaskInvisible */); + mController.setWillFinishToHome(true); + + // Verify cancel is called with a snapshot and that we've created an overlay + spyOn(mWm.mTaskSnapshotController); + doReturn(mMockTaskSnapshot).when(mWm.mTaskSnapshotController).getSnapshot(anyInt(), + anyInt(), eq(false) /* restoreFromDisk */, eq(false) /* isLowResolution */); + mController.cancelAnimationForHomeStart(); + verify(mMockRunner).onAnimationCanceled(any()); + + // Continue the animation (simulating a call to cleanupScreenshot()) + mController.continueDeferredCancelAnimation(); + verify(mAnimationCallbacks).onAnimationFinished(REORDER_MOVE_TO_TOP, false); + } + private ActivityRecord createHomeActivity() { final ActivityRecord homeActivity = new ActivityBuilder(mWm.mAtmService) .setParentTask(mRootHomeTask)