1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package com.android.server.wm; 18 19 import static android.app.ActivityTaskManager.INVALID_TASK_ID; 20 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; 21 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; 22 import static android.view.RemoteAnimationTarget.MODE_CLOSING; 23 import static android.view.RemoteAnimationTarget.MODE_OPENING; 24 import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION; 25 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; 26 27 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS; 28 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; 29 import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_RECENTS_ANIM; 30 import static com.android.server.wm.AnimationAdapterProto.REMOTE; 31 import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET; 32 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS; 33 import static com.android.server.wm.WindowManagerInternal.AppTransitionListener; 34 35 import android.annotation.IntDef; 36 import android.annotation.NonNull; 37 import android.app.WindowConfiguration; 38 import android.graphics.GraphicBuffer; 39 import android.graphics.Point; 40 import android.graphics.Rect; 41 import android.hardware.HardwareBuffer; 42 import android.os.Binder; 43 import android.os.IBinder.DeathRecipient; 44 import android.os.RemoteException; 45 import android.os.SystemClock; 46 import android.util.ArrayMap; 47 import android.util.ArraySet; 48 import android.util.IntArray; 49 import android.util.Slog; 50 import android.util.SparseBooleanArray; 51 import android.util.proto.ProtoOutputStream; 52 import android.view.IRecentsAnimationController; 53 import android.view.IRecentsAnimationRunner; 54 import android.view.InputWindowHandle; 55 import android.view.RemoteAnimationTarget; 56 import android.view.SurfaceControl; 57 import android.view.SurfaceControl.Transaction; 58 import android.view.SurfaceSession; 59 import android.view.WindowInsets.Type; 60 import android.window.PictureInPictureSurfaceTransaction; 61 import android.window.TaskSnapshot; 62 63 import com.android.internal.annotations.VisibleForTesting; 64 import com.android.internal.protolog.common.ProtoLog; 65 import com.android.internal.util.function.pooled.PooledConsumer; 66 import com.android.internal.util.function.pooled.PooledLambda; 67 import com.android.server.LocalServices; 68 import com.android.server.inputmethod.InputMethodManagerInternal; 69 import com.android.server.statusbar.StatusBarManagerInternal; 70 import com.android.server.wm.SurfaceAnimator.AnimationType; 71 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback; 72 import com.android.server.wm.utils.InsetUtils; 73 74 import com.google.android.collect.Sets; 75 76 import java.io.PrintWriter; 77 import java.util.ArrayList; 78 import java.util.stream.Collectors; 79 80 /** 81 * Controls a single instance of the remote driven recents animation. In particular, this allows 82 * the calling SystemUI to animate the visible task windows as a part of the transition. The remote 83 * runner is provided an animation controller which allows it to take screenshots and to notify 84 * window manager when the animation is completed. In addition, window manager may also notify the 85 * app if it requires the animation to be canceled at any time (ie. due to timeout, etc.) 86 */ 87 public class RecentsAnimationController implements DeathRecipient { 88 private static final String TAG = RecentsAnimationController.class.getSimpleName(); 89 private static final long FAILSAFE_DELAY = 1000; 90 91 // Constant for a yet-to-be-calculated {@link RemoteAnimationTarget#Mode} state 92 private static final int MODE_UNKNOWN = -1; 93 94 public static final int REORDER_KEEP_IN_PLACE = 0; 95 public static final int REORDER_MOVE_TO_TOP = 1; 96 public static final int REORDER_MOVE_TO_ORIGINAL_POSITION = 2; 97 98 @IntDef(prefix = { "REORDER_MODE_" }, value = { 99 REORDER_KEEP_IN_PLACE, 100 REORDER_MOVE_TO_TOP, 101 REORDER_MOVE_TO_ORIGINAL_POSITION 102 }) 103 public @interface ReorderMode {} 104 105 private final WindowManagerService mService; 106 @VisibleForTesting 107 final StatusBarManagerInternal mStatusBar; 108 private IRecentsAnimationRunner mRunner; 109 private final RecentsAnimationCallbacks mCallbacks; 110 private final ArrayList<TaskAnimationAdapter> mPendingAnimations = new ArrayList<>(); 111 private final IntArray mPendingNewTaskTargets = new IntArray(0); 112 113 private final ArrayList<WallpaperAnimationAdapter> mPendingWallpaperAnimations = 114 new ArrayList<>(); 115 private final int mDisplayId; 116 private boolean mWillFinishToHome = false; 117 private final Runnable mFailsafeRunnable = this::onFailsafe; 118 119 // The recents component app token that is shown behind the visible tasks 120 private ActivityRecord mTargetActivityRecord; 121 private DisplayContent mDisplayContent; 122 private int mTargetActivityType; 123 124 // We start the RecentsAnimationController in a pending-start state since we need to wait for 125 // the wallpaper/activity to draw before we can give control to the handler to start animating 126 // the visible task surfaces 127 private boolean mPendingStart = true; 128 129 // Set when the animation has been canceled 130 private boolean mCanceled; 131 132 // Whether or not the input consumer is enabled. The input consumer must be both registered and 133 // enabled for it to start intercepting touch events. 134 private boolean mInputConsumerEnabled; 135 136 private final Rect mTmpRect = new Rect(); 137 138 private boolean mLinkedToDeathOfRunner; 139 140 // Whether to try to defer canceling from a root task order change until the next transition 141 private boolean mRequestDeferCancelUntilNextTransition; 142 // Whether to actually defer canceling until the next transition 143 private boolean mCancelOnNextTransitionStart; 144 // Whether to take a screenshot when handling a deferred cancel 145 private boolean mCancelDeferredWithScreenshot; 146 // The reorder mode to apply after the cleanupScreenshot() callback 147 private int mPendingCancelWithScreenshotReorderMode = REORDER_MOVE_TO_ORIGINAL_POSITION; 148 149 @VisibleForTesting 150 boolean mIsAddingTaskToTargets; 151 @VisibleForTesting 152 boolean mShouldAttachNavBarToAppDuringTransition; 153 private boolean mNavigationBarAttachedToApp; 154 private ActivityRecord mNavBarAttachedApp; 155 156 private final ArrayList<RemoteAnimationTarget> mPendingTaskAppears = new ArrayList<>(); 157 158 /** 159 * An app transition listener to cancel the recents animation only after the app transition 160 * starts or is canceled. 161 */ 162 final AppTransitionListener mAppTransitionListener = new AppTransitionListener() { 163 @Override 164 public int onAppTransitionStartingLocked(boolean keyguardGoingAway, 165 boolean keyguardOccluding, long duration, long statusBarAnimationStartTime, 166 long statusBarAnimationDuration) { 167 continueDeferredCancel(); 168 return 0; 169 } 170 171 @Override 172 public void onAppTransitionCancelledLocked(boolean keyguardGoingAway) { 173 continueDeferredCancel(); 174 } 175 176 private void continueDeferredCancel() { 177 mDisplayContent.mAppTransition.unregisterListener(this); 178 if (mCanceled) { 179 return; 180 } 181 182 if (mCancelOnNextTransitionStart) { 183 mCancelOnNextTransitionStart = false; 184 cancelAnimationWithScreenshot(mCancelDeferredWithScreenshot); 185 } 186 } 187 }; 188 189 public interface RecentsAnimationCallbacks { 190 /** Callback when recents animation is finished. */ onAnimationFinished(@eorderMode int reorderMode, boolean sendUserLeaveHint)191 void onAnimationFinished(@ReorderMode int reorderMode, boolean sendUserLeaveHint); 192 } 193 194 private final IRecentsAnimationController mController = 195 new IRecentsAnimationController.Stub() { 196 197 @Override 198 public TaskSnapshot screenshotTask(int taskId) { 199 ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, 200 "screenshotTask(%d): mCanceled=%b", taskId, mCanceled); 201 final long token = Binder.clearCallingIdentity(); 202 try { 203 synchronized (mService.getWindowManagerLock()) { 204 if (mCanceled) { 205 return null; 206 } 207 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { 208 final TaskAnimationAdapter adapter = mPendingAnimations.get(i); 209 final Task task = adapter.mTask; 210 if (task.mTaskId == taskId) { 211 final TaskSnapshotController snapshotController = 212 mService.mTaskSnapshotController; 213 final ArraySet<Task> tasks = Sets.newArraySet(task); 214 snapshotController.snapshotTasks(tasks); 215 snapshotController.addSkipClosingAppSnapshotTasks(tasks); 216 return snapshotController.getSnapshot(taskId, task.mUserId, 217 false /* restoreFromDisk */, false /* isLowResolution */); 218 } 219 } 220 return null; 221 } 222 } finally { 223 Binder.restoreCallingIdentity(token); 224 } 225 } 226 227 @Override 228 public void setFinishTaskTransaction(int taskId, 229 PictureInPictureSurfaceTransaction finishTransaction, 230 SurfaceControl overlay) { 231 ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, 232 "setFinishTaskTransaction(%d): transaction=%s", taskId, finishTransaction); 233 final long token = Binder.clearCallingIdentity(); 234 try { 235 synchronized (mService.getWindowManagerLock()) { 236 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { 237 final TaskAnimationAdapter taskAdapter = mPendingAnimations.get(i); 238 if (taskAdapter.mTask.mTaskId == taskId) { 239 taskAdapter.mFinishTransaction = finishTransaction; 240 taskAdapter.mFinishOverlay = overlay; 241 break; 242 } 243 } 244 } 245 } finally { 246 Binder.restoreCallingIdentity(token); 247 } 248 } 249 250 @Override 251 public void finish(boolean moveHomeToTop, boolean sendUserLeaveHint) { 252 ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, 253 "finish(%b): mCanceled=%b", moveHomeToTop, mCanceled); 254 final long token = Binder.clearCallingIdentity(); 255 try { 256 // Note, the callback will handle its own synchronization, do not lock on WM lock 257 // prior to calling the callback 258 mCallbacks.onAnimationFinished(moveHomeToTop 259 ? REORDER_MOVE_TO_TOP 260 : REORDER_MOVE_TO_ORIGINAL_POSITION, sendUserLeaveHint); 261 } finally { 262 Binder.restoreCallingIdentity(token); 263 } 264 } 265 266 @Override 267 public void setAnimationTargetsBehindSystemBars(boolean behindSystemBars) 268 throws RemoteException { 269 final long token = Binder.clearCallingIdentity(); 270 try { 271 synchronized (mService.getWindowManagerLock()) { 272 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { 273 final Task task = mPendingAnimations.get(i).mTask; 274 if (task.getActivityType() != mTargetActivityType) { 275 task.setCanAffectSystemUiFlags(behindSystemBars); 276 } 277 } 278 InputMethodManagerInternal.get().maybeFinishStylusHandwriting(); 279 mService.mWindowPlacerLocked.requestTraversal(); 280 } 281 } finally { 282 Binder.restoreCallingIdentity(token); 283 } 284 } 285 286 @Override 287 public void setInputConsumerEnabled(boolean enabled) { 288 ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, 289 "setInputConsumerEnabled(%s): mCanceled=%b", enabled, mCanceled); 290 final long token = Binder.clearCallingIdentity(); 291 try { 292 synchronized (mService.getWindowManagerLock()) { 293 if (mCanceled) { 294 return; 295 } 296 mInputConsumerEnabled = enabled; 297 final InputMonitor inputMonitor = mDisplayContent.getInputMonitor(); 298 inputMonitor.updateInputWindowsLw(true /*force*/); 299 mService.scheduleAnimationLocked(); 300 } 301 } finally { 302 Binder.restoreCallingIdentity(token); 303 } 304 } 305 306 @Override 307 public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) { 308 synchronized (mService.mGlobalLock) { 309 setDeferredCancel(defer, screenshot); 310 } 311 } 312 313 @Override 314 public void cleanupScreenshot() { 315 final long token = Binder.clearCallingIdentity(); 316 try { 317 // Note, the callback will handle its own synchronization, do not lock on WM lock 318 // prior to calling the callback 319 continueDeferredCancelAnimation(); 320 } finally { 321 Binder.restoreCallingIdentity(token); 322 } 323 } 324 325 @Override 326 public void setWillFinishToHome(boolean willFinishToHome) { 327 synchronized (mService.getWindowManagerLock()) { 328 RecentsAnimationController.this.setWillFinishToHome(willFinishToHome); 329 } 330 } 331 332 @Override 333 public boolean removeTask(int taskId) { 334 final long token = Binder.clearCallingIdentity(); 335 try { 336 synchronized (mService.getWindowManagerLock()) { 337 return removeTaskInternal(taskId); 338 } 339 } finally { 340 Binder.restoreCallingIdentity(token); 341 } 342 } 343 344 @Override 345 public void detachNavigationBarFromApp(boolean moveHomeToTop) { 346 final long token = Binder.clearCallingIdentity(); 347 try { 348 synchronized (mService.getWindowManagerLock()) { 349 restoreNavigationBarFromApp( 350 moveHomeToTop || mIsAddingTaskToTargets /* animate */); 351 mService.mWindowPlacerLocked.requestTraversal(); 352 } 353 } finally { 354 Binder.restoreCallingIdentity(token); 355 } 356 } 357 358 @Override 359 public void animateNavigationBarToApp(long duration) { 360 final long token = Binder.clearCallingIdentity(); 361 try { 362 synchronized (mService.getWindowManagerLock()) { 363 animateNavigationBarForAppLaunch(duration); 364 } 365 } finally { 366 Binder.restoreCallingIdentity(token); 367 } 368 } 369 }; 370 371 /** 372 * @param remoteAnimationRunner The remote runner which should be notified when the animation is 373 * ready to start or has been canceled 374 * @param callbacks Callbacks to be made when the animation finishes 375 */ RecentsAnimationController(WindowManagerService service, IRecentsAnimationRunner remoteAnimationRunner, RecentsAnimationCallbacks callbacks, int displayId)376 RecentsAnimationController(WindowManagerService service, 377 IRecentsAnimationRunner remoteAnimationRunner, RecentsAnimationCallbacks callbacks, 378 int displayId) { 379 mService = service; 380 mRunner = remoteAnimationRunner; 381 mCallbacks = callbacks; 382 mDisplayId = displayId; 383 mStatusBar = LocalServices.getService(StatusBarManagerInternal.class); 384 mDisplayContent = service.mRoot.getDisplayContent(displayId); 385 mShouldAttachNavBarToAppDuringTransition = 386 mDisplayContent.getDisplayPolicy().shouldAttachNavBarToAppDuringTransition(); 387 } 388 389 /** 390 * Initializes the recents animation controller. This is a separate call from the constructor 391 * because it may call cancelAnimation() which needs to properly clean up the controller 392 * in the window manager. 393 */ initialize(int targetActivityType, SparseBooleanArray recentTaskIds, ActivityRecord targetActivity)394 public void initialize(int targetActivityType, SparseBooleanArray recentTaskIds, 395 ActivityRecord targetActivity) { 396 mTargetActivityType = targetActivityType; 397 mDisplayContent.mAppTransition.registerListenerLocked(mAppTransitionListener); 398 399 // Make leashes for each of the visible/target tasks and add it to the recents animation to 400 // be started 401 // TODO(b/153090560): Support Recents on multiple task display areas 402 final ArrayList<Task> visibleTasks = mDisplayContent.getDefaultTaskDisplayArea() 403 .getVisibleTasks(); 404 final Task targetRootTask = mDisplayContent.getDefaultTaskDisplayArea() 405 .getRootTask(WINDOWING_MODE_UNDEFINED, targetActivityType); 406 if (targetRootTask != null) { 407 final PooledConsumer c = PooledLambda.obtainConsumer((t, outList) -> 408 { if (!outList.contains(t)) outList.add(t); }, PooledLambda.__(Task.class), 409 visibleTasks); 410 targetRootTask.forAllLeafTasks(c, true /* traverseTopToBottom */); 411 c.recycle(); 412 } 413 414 final int taskCount = visibleTasks.size(); 415 for (int i = taskCount - 1; i >= 0; i--) { 416 final Task task = visibleTasks.get(i); 417 if (skipAnimation(task)) { 418 continue; 419 } 420 addAnimation(task, !recentTaskIds.get(task.mTaskId), false /* hidden */, 421 (type, anim) -> task.forAllWindows(win -> { 422 win.onAnimationFinished(type, anim); 423 }, true /* traverseTopToBottom */)); 424 } 425 426 // Skip the animation if there is nothing to animate 427 if (mPendingAnimations.isEmpty()) { 428 cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "initialize-noVisibleTasks"); 429 return; 430 } 431 432 try { 433 linkToDeathOfRunner(); 434 } catch (RemoteException e) { 435 cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "initialize-failedToLinkToDeath"); 436 return; 437 } 438 439 attachNavigationBarToApp(); 440 441 // Adjust the wallpaper visibility for the showing target activity 442 ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, 443 "setHomeApp(%s)", targetActivity.getName()); 444 mTargetActivityRecord = targetActivity; 445 if (targetActivity.windowsCanBeWallpaperTarget()) { 446 mDisplayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; 447 mDisplayContent.setLayoutNeeded(); 448 } 449 450 mService.mWindowPlacerLocked.performSurfacePlacement(); 451 452 mDisplayContent.mFixedRotationTransitionListener.onStartRecentsAnimation(targetActivity); 453 454 // Notify that the animation has started 455 if (mStatusBar != null) { 456 mStatusBar.onRecentsAnimationStateChanged(true /* running */); 457 } 458 } 459 460 /** 461 * Return whether the given window should still be considered interesting for the all-drawn 462 * state. This is only interesting for the target app, which may have child windows that are 463 * not actually visible and should not be considered interesting and waited upon. 464 */ isInterestingForAllDrawn(WindowState window)465 protected boolean isInterestingForAllDrawn(WindowState window) { 466 if (isTargetApp(window.getActivityRecord())) { 467 if (window.getWindowType() != TYPE_BASE_APPLICATION 468 && window.getAttrs().alpha == 0f) { 469 // If there is a cihld window that is alpha 0, then ignore that window 470 return false; 471 } 472 } 473 // By default all windows are still interesting for all drawn purposes 474 return true; 475 } 476 477 /** 478 * Whether a task should be filtered from the recents animation. This can be true for tasks 479 * being displayed outside of recents. 480 */ skipAnimation(Task task)481 private boolean skipAnimation(Task task) { 482 final WindowConfiguration config = task.getWindowConfiguration(); 483 return task.isAlwaysOnTop() || config.tasksAreFloating(); 484 } 485 486 @VisibleForTesting addAnimation(Task task, boolean isRecentTaskInvisible)487 TaskAnimationAdapter addAnimation(Task task, boolean isRecentTaskInvisible) { 488 return addAnimation(task, isRecentTaskInvisible, false /* hidden */, 489 null /* finishedCallback */); 490 } 491 492 @VisibleForTesting addAnimation(Task task, boolean isRecentTaskInvisible, boolean hidden, OnAnimationFinishedCallback finishedCallback)493 TaskAnimationAdapter addAnimation(Task task, boolean isRecentTaskInvisible, boolean hidden, 494 OnAnimationFinishedCallback finishedCallback) { 495 ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "addAnimation(%s)", task.getName()); 496 final TaskAnimationAdapter taskAdapter = new TaskAnimationAdapter(task, 497 isRecentTaskInvisible); 498 task.startAnimation(task.getPendingTransaction(), taskAdapter, hidden, 499 ANIMATION_TYPE_RECENTS, finishedCallback); 500 task.commitPendingTransaction(); 501 mPendingAnimations.add(taskAdapter); 502 return taskAdapter; 503 } 504 505 @VisibleForTesting removeAnimation(TaskAnimationAdapter taskAdapter)506 void removeAnimation(TaskAnimationAdapter taskAdapter) { 507 ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, 508 "removeAnimation(%d)", taskAdapter.mTask.mTaskId); 509 taskAdapter.onRemove(); 510 mPendingAnimations.remove(taskAdapter); 511 } 512 513 @VisibleForTesting removeWallpaperAnimation(WallpaperAnimationAdapter wallpaperAdapter)514 void removeWallpaperAnimation(WallpaperAnimationAdapter wallpaperAdapter) { 515 ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "removeWallpaperAnimation()"); 516 wallpaperAdapter.getLeashFinishedCallback().onAnimationFinished( 517 wallpaperAdapter.getLastAnimationType(), wallpaperAdapter); 518 mPendingWallpaperAnimations.remove(wallpaperAdapter); 519 } 520 startAnimation()521 void startAnimation() { 522 ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, 523 "startAnimation(): mPendingStart=%b mCanceled=%b", mPendingStart, mCanceled); 524 if (!mPendingStart || mCanceled) { 525 // Skip starting if we've already started or canceled the animation 526 return; 527 } 528 try { 529 // Create the app targets 530 final RemoteAnimationTarget[] appTargets = createAppAnimations(); 531 532 // Skip the animation if there is nothing to animate 533 if (appTargets.length == 0) { 534 cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "startAnimation-noAppWindows"); 535 return; 536 } 537 538 // Create the wallpaper targets 539 final RemoteAnimationTarget[] wallpaperTargets = createWallpaperAnimations(); 540 541 mPendingStart = false; 542 543 final Rect contentInsets; 544 final WindowState targetAppMainWindow = getTargetAppMainWindow(); 545 if (targetAppMainWindow != null) { 546 contentInsets = targetAppMainWindow 547 .getInsetsStateWithVisibilityOverride() 548 .calculateInsets(mTargetActivityRecord.getBounds(), Type.systemBars(), 549 false /* ignoreVisibility */).toRect(); 550 } else { 551 // If the window for the activity had not yet been created, use the display insets. 552 mService.getStableInsets(mDisplayId, mTmpRect); 553 contentInsets = mTmpRect; 554 } 555 mRunner.onAnimationStart(mController, appTargets, wallpaperTargets, contentInsets, 556 null); 557 ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, 558 "startAnimation(): Notify animation start: %s", 559 mPendingAnimations.stream() 560 .map(anim->anim.mTask.mTaskId).collect(Collectors.toList())); 561 } catch (RemoteException e) { 562 Slog.e(TAG, "Failed to start recents animation", e); 563 } 564 565 if (mTargetActivityRecord != null) { 566 final ArrayMap<WindowContainer, Integer> reasons = new ArrayMap<>(1); 567 reasons.put(mTargetActivityRecord, APP_TRANSITION_RECENTS_ANIM); 568 mService.mAtmService.mTaskSupervisor.getActivityMetricsLogger() 569 .notifyTransitionStarting(reasons); 570 } 571 } 572 isNavigationBarAttachedToApp()573 boolean isNavigationBarAttachedToApp() { 574 return mNavigationBarAttachedToApp; 575 } 576 577 @VisibleForTesting getNavigationBarWindow()578 WindowState getNavigationBarWindow() { 579 return mDisplayContent.getDisplayPolicy().getNavigationBar(); 580 } 581 attachNavigationBarToApp()582 private void attachNavigationBarToApp() { 583 if (!mShouldAttachNavBarToAppDuringTransition 584 // Skip the case where the nav bar is controlled by fade rotation. 585 || mDisplayContent.getAsyncRotationController() != null) { 586 return; 587 } 588 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { 589 final TaskAnimationAdapter adapter = mPendingAnimations.get(i); 590 final Task task = adapter.mTask; 591 if (task.isActivityTypeHomeOrRecents()) { 592 continue; 593 } 594 mNavBarAttachedApp = task.getTopVisibleActivity(); 595 break; 596 } 597 598 final WindowState navWindow = getNavigationBarWindow(); 599 if (mNavBarAttachedApp == null || navWindow == null || navWindow.mToken == null) { 600 return; 601 } 602 mNavigationBarAttachedToApp = true; 603 navWindow.mToken.cancelAnimation(); 604 final SurfaceControl.Transaction t = navWindow.mToken.getPendingTransaction(); 605 final SurfaceControl navSurfaceControl = navWindow.mToken.getSurfaceControl(); 606 navWindow.setSurfaceTranslationY(-mNavBarAttachedApp.getBounds().top); 607 t.reparent(navSurfaceControl, mNavBarAttachedApp.getSurfaceControl()); 608 t.show(navSurfaceControl); 609 610 final WindowContainer imeContainer = mDisplayContent.getImeContainer(); 611 if (imeContainer.isVisible()) { 612 t.setRelativeLayer(navSurfaceControl, imeContainer.getSurfaceControl(), 1); 613 } else { 614 // Place the nav bar on top of anything else in the top activity. 615 t.setLayer(navSurfaceControl, Integer.MAX_VALUE); 616 } 617 if (mStatusBar != null) { 618 mStatusBar.setNavigationBarLumaSamplingEnabled(mDisplayId, false); 619 } 620 } 621 622 @VisibleForTesting restoreNavigationBarFromApp(boolean animate)623 void restoreNavigationBarFromApp(boolean animate) { 624 if (!mNavigationBarAttachedToApp) { 625 return; 626 } 627 mNavigationBarAttachedToApp = false; 628 629 if (mStatusBar != null) { 630 mStatusBar.setNavigationBarLumaSamplingEnabled(mDisplayId, true); 631 } 632 633 final WindowState navWindow = getNavigationBarWindow(); 634 if (navWindow == null) { 635 return; 636 } 637 navWindow.setSurfaceTranslationY(0); 638 639 final WindowToken navToken = navWindow.mToken; 640 if (navToken == null) { 641 return; 642 } 643 final SurfaceControl.Transaction t = mDisplayContent.getPendingTransaction(); 644 final WindowContainer parent = navToken.getParent(); 645 t.setLayer(navToken.getSurfaceControl(), navToken.getLastLayer()); 646 647 if (animate) { 648 final NavBarFadeAnimationController controller = 649 new NavBarFadeAnimationController(mDisplayContent); 650 controller.fadeWindowToken(true); 651 } else { 652 // Reparent the SurfaceControl of nav bar token back. 653 t.reparent(navToken.getSurfaceControl(), parent.getSurfaceControl()); 654 } 655 } 656 animateNavigationBarForAppLaunch(long duration)657 void animateNavigationBarForAppLaunch(long duration) { 658 if (!mShouldAttachNavBarToAppDuringTransition 659 // Skip the case where the nav bar is controlled by fade rotation. 660 || mDisplayContent.getAsyncRotationController() != null 661 || mNavigationBarAttachedToApp 662 || mNavBarAttachedApp == null) { 663 return; 664 } 665 666 final NavBarFadeAnimationController controller = 667 new NavBarFadeAnimationController(mDisplayContent); 668 controller.fadeOutAndInSequentially(duration, null /* fadeOutParent */, 669 mNavBarAttachedApp.getSurfaceControl()); 670 } 671 addTaskToTargets(Task task, OnAnimationFinishedCallback finishedCallback)672 void addTaskToTargets(Task task, OnAnimationFinishedCallback finishedCallback) { 673 if (mRunner != null) { 674 mIsAddingTaskToTargets = task != null; 675 mNavBarAttachedApp = task == null ? null : task.getTopVisibleActivity(); 676 // No need to send task appeared when the task target already exists, or when the 677 // task is being managed as a multi-window mode outside of recents (e.g. bubbles). 678 if (isAnimatingTask(task) || skipAnimation(task)) { 679 return; 680 } 681 collectTaskRemoteAnimations(task, MODE_OPENING, finishedCallback); 682 } 683 } 684 sendTasksAppeared()685 void sendTasksAppeared() { 686 if (mPendingTaskAppears.isEmpty() || mRunner == null) return; 687 try { 688 final RemoteAnimationTarget[] targets = mPendingTaskAppears.toArray( 689 new RemoteAnimationTarget[0]); 690 mRunner.onTasksAppeared(targets); 691 mPendingTaskAppears.clear(); 692 } catch (RemoteException e) { 693 Slog.e(TAG, "Failed to report task appeared", e); 694 } 695 } 696 collectTaskRemoteAnimations(Task task, int mode, OnAnimationFinishedCallback finishedCallback)697 private void collectTaskRemoteAnimations(Task task, int mode, 698 OnAnimationFinishedCallback finishedCallback) { 699 final SparseBooleanArray recentTaskIds = 700 mService.mAtmService.getRecentTasks().getRecentTaskIds(); 701 702 // The target must be built off the root task (the leaf task surface would be cropped 703 // within the root surface). However, recents only tracks leaf task ids, so we'll traverse 704 // and create animation target for all visible leaf tasks. 705 task.forAllLeafTasks(leafTask -> { 706 if (!leafTask.shouldBeVisible(null /* starting */)) { 707 return; 708 } 709 final int taskId = leafTask.mTaskId; 710 TaskAnimationAdapter adapter = addAnimation(leafTask, 711 !recentTaskIds.get(taskId), true /* hidden */, finishedCallback); 712 mPendingNewTaskTargets.add(taskId); 713 final RemoteAnimationTarget target = 714 adapter.createRemoteAnimationTarget(taskId, mode); 715 if (target != null) { 716 mPendingTaskAppears.add(target); 717 ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, 718 "collectTaskRemoteAnimations, target: %s", target); 719 } 720 }, false /* traverseTopToBottom */); 721 } 722 removeTaskInternal(int taskId)723 private boolean removeTaskInternal(int taskId) { 724 boolean result = false; 725 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { 726 // Only allows when task target has became visible to user, to prevent 727 // the flickering during remove animation and task visible. 728 final TaskAnimationAdapter target = mPendingAnimations.get(i); 729 if (target.mTask.mTaskId == taskId && target.mTask.isOnTop()) { 730 removeAnimation(target); 731 final int taskIndex = mPendingNewTaskTargets.indexOf(taskId); 732 if (taskIndex != -1) { 733 mPendingNewTaskTargets.remove(taskIndex); 734 } 735 result = true; 736 break; 737 } 738 } 739 return result; 740 } 741 createAppAnimations()742 private RemoteAnimationTarget[] createAppAnimations() { 743 final ArrayList<RemoteAnimationTarget> targets = new ArrayList<>(); 744 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { 745 final TaskAnimationAdapter taskAdapter = mPendingAnimations.get(i); 746 final RemoteAnimationTarget target = 747 taskAdapter.createRemoteAnimationTarget(INVALID_TASK_ID, MODE_UNKNOWN); 748 if (target != null) { 749 targets.add(target); 750 } else { 751 removeAnimation(taskAdapter); 752 } 753 } 754 return targets.toArray(new RemoteAnimationTarget[targets.size()]); 755 } 756 createWallpaperAnimations()757 private RemoteAnimationTarget[] createWallpaperAnimations() { 758 ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "createWallpaperAnimations()"); 759 return WallpaperAnimationAdapter.startWallpaperAnimations(mDisplayContent, 0L, 0L, 760 adapter -> { 761 synchronized (mService.mGlobalLock) { 762 // If the wallpaper animation is canceled, continue with the recents 763 // animation 764 mPendingWallpaperAnimations.remove(adapter); 765 } 766 }, mPendingWallpaperAnimations); 767 } 768 769 void forceCancelAnimation(@ReorderMode int reorderMode, String reason) { 770 if (!mCanceled) { 771 cancelAnimation(reorderMode, reason); 772 } else { 773 continueDeferredCancelAnimation(); 774 } 775 } 776 777 void cancelAnimation(@ReorderMode int reorderMode, String reason) { 778 cancelAnimation(reorderMode, false /*screenshot */, reason); 779 } 780 781 void cancelAnimationWithScreenshot(boolean screenshot) { 782 cancelAnimation(REORDER_KEEP_IN_PLACE, screenshot, "rootTaskOrderChanged"); 783 } 784 785 /** 786 * Cancels the running animation when starting home, providing a snapshot for the runner to 787 * properly handle the cancellation. This call uses the provided hint to determine how to 788 * finish the animation. 789 */ 790 public void cancelAnimationForHomeStart() { 791 final int reorderMode = mTargetActivityType == ACTIVITY_TYPE_HOME && mWillFinishToHome 792 ? REORDER_MOVE_TO_TOP 793 : REORDER_KEEP_IN_PLACE; 794 cancelAnimation(reorderMode, true /* screenshot */, "cancelAnimationForHomeStart"); 795 } 796 797 /** 798 * Cancels the running animation when there is a display change, providing a snapshot for the 799 * runner to properly handle the cancellation. This call uses the provided hint to determine 800 * how to finish the animation. 801 */ 802 public void cancelAnimationForDisplayChange() { 803 cancelAnimation(mWillFinishToHome ? REORDER_MOVE_TO_TOP : REORDER_MOVE_TO_ORIGINAL_POSITION, 804 true /* screenshot */, "cancelAnimationForDisplayChange"); 805 } 806 807 private void cancelAnimation(@ReorderMode int reorderMode, boolean screenshot, String reason) { 808 ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "cancelAnimation(): reason=%s", reason); 809 synchronized (mService.getWindowManagerLock()) { 810 if (mCanceled) { 811 // We've already canceled the animation 812 return; 813 } 814 mService.mH.removeCallbacks(mFailsafeRunnable); 815 mCanceled = true; 816 817 if (screenshot && !mPendingAnimations.isEmpty()) { 818 final ArrayMap<Task, TaskSnapshot> snapshotMap = screenshotRecentTasks(); 819 mPendingCancelWithScreenshotReorderMode = reorderMode; 820 821 if (!snapshotMap.isEmpty()) { 822 try { 823 int[] taskIds = new int[snapshotMap.size()]; 824 TaskSnapshot[] snapshots = new TaskSnapshot[snapshotMap.size()]; 825 for (int i = snapshotMap.size() - 1; i >= 0; i--) { 826 taskIds[i] = snapshotMap.keyAt(i).mTaskId; 827 snapshots[i] = snapshotMap.valueAt(i); 828 } 829 mRunner.onAnimationCanceled(taskIds, snapshots); 830 } catch (RemoteException e) { 831 Slog.e(TAG, "Failed to cancel recents animation", e); 832 } 833 // Schedule a new failsafe for if the runner doesn't clean up the screenshot 834 scheduleFailsafe(); 835 return; 836 } 837 // Fallback to a normal cancel since we couldn't screenshot 838 } 839 840 // Notify the runner and clean up the animation immediately 841 // Note: In the fallback case, this can trigger multiple onAnimationCancel() calls 842 // to the runner if we this actually triggers cancel twice on the caller 843 try { 844 mRunner.onAnimationCanceled(null /* taskIds */, null /* taskSnapshots */); 845 } catch (RemoteException e) { 846 Slog.e(TAG, "Failed to cancel recents animation", e); 847 } 848 mCallbacks.onAnimationFinished(reorderMode, false /* sendUserLeaveHint */); 849 } 850 } 851 852 @VisibleForTesting 853 void continueDeferredCancelAnimation() { 854 mCallbacks.onAnimationFinished(mPendingCancelWithScreenshotReorderMode, 855 false /* sendUserLeaveHint */); 856 } 857 858 @VisibleForTesting 859 void setWillFinishToHome(boolean willFinishToHome) { 860 mWillFinishToHome = willFinishToHome; 861 } 862 863 /** 864 * Cancel recents animation when the next app transition starts. 865 * <p> 866 * When we cancel the recents animation due to a root task order change, we can't just cancel it 867 * immediately as it would lead to a flicker in Launcher if we just remove the task from the 868 * leash. Instead we screenshot the previous task and replace the child of the leash with the 869 * screenshot, so that Launcher can still control the leash lifecycle & make the next app 870 * transition animate smoothly without flickering. 871 */ 872 void setCancelOnNextTransitionStart() { 873 mCancelOnNextTransitionStart = true; 874 } 875 876 /** 877 * Requests that we attempt to defer the cancel until the next app transition if we are 878 * canceling from a root task order change. If {@param screenshot} is specified, then the 879 * system will replace the contents of the leash with a screenshot, which must be cleaned up 880 * when the runner calls cleanUpScreenshot(). 881 */ 882 void setDeferredCancel(boolean defer, boolean screenshot) { 883 mRequestDeferCancelUntilNextTransition = defer; 884 mCancelDeferredWithScreenshot = screenshot; 885 } 886 887 /** 888 * @return Whether we should defer the cancel from a root task order change until the next app 889 * transition. 890 */ 891 boolean shouldDeferCancelUntilNextTransition() { 892 return mRequestDeferCancelUntilNextTransition; 893 } 894 895 /** 896 * @return Whether we should both defer the cancel from a root task order change until the next 897 * app transition, and also that the deferred cancel should replace the contents of the leash 898 * with a screenshot. 899 */ 900 boolean shouldDeferCancelWithScreenshot() { 901 return mRequestDeferCancelUntilNextTransition && mCancelDeferredWithScreenshot; 902 } 903 904 private ArrayMap<Task, TaskSnapshot> screenshotRecentTasks() { 905 final TaskSnapshotController snapshotController = mService.mTaskSnapshotController; 906 final ArrayMap<Task, TaskSnapshot> snapshotMap = new ArrayMap<>(); 907 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { 908 final TaskAnimationAdapter adapter = mPendingAnimations.get(i); 909 final Task task = adapter.mTask; 910 snapshotController.recordTaskSnapshot(task, false /* allowSnapshotHome */); 911 final TaskSnapshot snapshot = snapshotController.getSnapshot(task.mTaskId, task.mUserId, 912 false /* restoreFromDisk */, false /* isLowResolution */); 913 if (snapshot != null) { 914 snapshotMap.put(task, snapshot); 915 // Defer until the runner calls back to cleanupScreenshot() 916 adapter.setSnapshotOverlay(snapshot); 917 } 918 } 919 snapshotController.addSkipClosingAppSnapshotTasks(snapshotMap.keySet()); 920 return snapshotMap; 921 } 922 923 void cleanupAnimation(@ReorderMode int reorderMode) { 924 ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, 925 "cleanupAnimation(): Notify animation finished mPendingAnimations=%d " 926 + "reorderMode=%d", 927 mPendingAnimations.size(), reorderMode); 928 if (reorderMode != REORDER_MOVE_TO_ORIGINAL_POSITION 929 && mTargetActivityRecord != mDisplayContent.topRunningActivity()) { 930 // Notify the state at the beginning because the removeAnimation may notify the 931 // transition is finished. This is a signal that there will be a next transition. 932 mDisplayContent.mFixedRotationTransitionListener.notifyRecentsWillBeTop(); 933 } 934 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { 935 final TaskAnimationAdapter taskAdapter = mPendingAnimations.get(i); 936 if (reorderMode == REORDER_MOVE_TO_TOP || reorderMode == REORDER_KEEP_IN_PLACE) { 937 taskAdapter.mTask.dontAnimateDimExit(); 938 } 939 removeAnimation(taskAdapter); 940 taskAdapter.onCleanup(); 941 } 942 // Should already be empty, but clean-up pending task-appears in-case they weren't sent. 943 mPendingNewTaskTargets.clear(); 944 mPendingTaskAppears.clear(); 945 946 for (int i = mPendingWallpaperAnimations.size() - 1; i >= 0; i--) { 947 final WallpaperAnimationAdapter wallpaperAdapter = mPendingWallpaperAnimations.get(i); 948 removeWallpaperAnimation(wallpaperAdapter); 949 } 950 951 restoreNavigationBarFromApp( 952 reorderMode == REORDER_MOVE_TO_TOP || mIsAddingTaskToTargets /* animate */); 953 954 // Clear any pending failsafe runnables 955 mService.mH.removeCallbacks(mFailsafeRunnable); 956 mDisplayContent.mAppTransition.unregisterListener(mAppTransitionListener); 957 958 // Clear references to the runner 959 unlinkToDeathOfRunner(); 960 mRunner = null; 961 mCanceled = true; 962 963 // Restore IME icon only when moving the original app task to front from recents, in case 964 // IME icon may missing if the moving task has already been the current focused task. 965 if (reorderMode == REORDER_MOVE_TO_ORIGINAL_POSITION && !mIsAddingTaskToTargets) { 966 InputMethodManagerInternal.get().updateImeWindowStatus(false /* disableImeIcon */); 967 } 968 969 // Update the input windows after the animation is complete 970 final InputMonitor inputMonitor = mDisplayContent.getInputMonitor(); 971 inputMonitor.updateInputWindowsLw(true /*force*/); 972 973 // We have deferred all notifications to the target app as a part of the recents animation, 974 // so if we are actually transitioning there, notify again here 975 if (mTargetActivityRecord != null) { 976 if (reorderMode == REORDER_MOVE_TO_TOP || reorderMode == REORDER_KEEP_IN_PLACE) { 977 mDisplayContent.mAppTransition.notifyAppTransitionFinishedLocked( 978 mTargetActivityRecord.token); 979 } 980 } 981 mDisplayContent.mFixedRotationTransitionListener.onFinishRecentsAnimation(); 982 983 // Notify that the animation has ended 984 if (mStatusBar != null) { 985 mStatusBar.onRecentsAnimationStateChanged(false /* running */); 986 } 987 } 988 989 void scheduleFailsafe() { 990 mService.mH.postDelayed(mFailsafeRunnable, FAILSAFE_DELAY); 991 } 992 993 void onFailsafe() { 994 forceCancelAnimation( 995 mWillFinishToHome ? REORDER_MOVE_TO_TOP : REORDER_MOVE_TO_ORIGINAL_POSITION, 996 "onFailsafe"); 997 } 998 999 private void linkToDeathOfRunner() throws RemoteException { 1000 if (!mLinkedToDeathOfRunner) { 1001 mRunner.asBinder().linkToDeath(this, 0); 1002 mLinkedToDeathOfRunner = true; 1003 } 1004 } 1005 1006 private void unlinkToDeathOfRunner() { 1007 if (mLinkedToDeathOfRunner) { 1008 mRunner.asBinder().unlinkToDeath(this, 0); 1009 mLinkedToDeathOfRunner = false; 1010 } 1011 } 1012 1013 @Override 1014 public void binderDied() { 1015 forceCancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "binderDied"); 1016 1017 synchronized (mService.getWindowManagerLock()) { 1018 // Clear associated input consumers on runner death 1019 final InputMonitor inputMonitor = mDisplayContent.getInputMonitor(); 1020 inputMonitor.destroyInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION); 1021 } 1022 } 1023 1024 void checkAnimationReady(WallpaperController wallpaperController) { 1025 if (mPendingStart) { 1026 final boolean wallpaperReady = !isTargetOverWallpaper() 1027 || (wallpaperController.getWallpaperTarget() != null 1028 && wallpaperController.wallpaperTransitionReady()); 1029 if (wallpaperReady) { 1030 mService.getRecentsAnimationController().startAnimation(); 1031 } 1032 } 1033 } 1034 1035 boolean isWallpaperVisible(WindowState w) { 1036 return w != null && w.mAttrs.type == TYPE_BASE_APPLICATION && 1037 ((w.mActivityRecord != null && mTargetActivityRecord == w.mActivityRecord) 1038 || isAnimatingTask(w.getTask())) 1039 && isTargetOverWallpaper() && w.isOnScreen(); 1040 } 1041 1042 /** 1043 * @return Whether to use the input consumer to override app input to route home/recents. 1044 */ 1045 boolean shouldApplyInputConsumer(ActivityRecord activity) { 1046 // Only apply the input consumer if it is enabled, it is not the target (home/recents) 1047 // being revealed with the transition, and we are actively animating the app as a part of 1048 // the animation 1049 return mInputConsumerEnabled && activity != null 1050 && !isTargetApp(activity) && isAnimatingApp(activity); 1051 } 1052 1053 boolean updateInputConsumerForApp(InputWindowHandle inputWindowHandle) { 1054 // Update the input consumer touchable region to match the target app main window 1055 final WindowState targetAppMainWindow = getTargetAppMainWindow(); 1056 if (targetAppMainWindow != null) { 1057 targetAppMainWindow.getBounds(mTmpRect); 1058 inputWindowHandle.touchableRegion.set(mTmpRect); 1059 return true; 1060 } 1061 return false; 1062 } 1063 1064 boolean isTargetApp(ActivityRecord activity) { 1065 return mTargetActivityRecord != null && activity == mTargetActivityRecord; 1066 } 1067 1068 private boolean isTargetOverWallpaper() { 1069 if (mTargetActivityRecord == null) { 1070 return false; 1071 } 1072 return mTargetActivityRecord.windowsCanBeWallpaperTarget(); 1073 } 1074 1075 WindowState getTargetAppMainWindow() { 1076 if (mTargetActivityRecord == null) { 1077 return null; 1078 } 1079 return mTargetActivityRecord.findMainWindow(); 1080 } 1081 1082 DisplayArea getTargetAppDisplayArea() { 1083 if (mTargetActivityRecord == null) { 1084 return null; 1085 } 1086 return mTargetActivityRecord.getDisplayArea(); 1087 } 1088 1089 boolean isAnimatingTask(Task task) { 1090 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { 1091 if (task == mPendingAnimations.get(i).mTask) { 1092 return true; 1093 } 1094 } 1095 return false; 1096 } 1097 1098 boolean isAnimatingWallpaper(WallpaperWindowToken token) { 1099 for (int i = mPendingWallpaperAnimations.size() - 1; i >= 0; i--) { 1100 if (token == mPendingWallpaperAnimations.get(i).getToken()) { 1101 return true; 1102 } 1103 } 1104 return false; 1105 } 1106 1107 private boolean isAnimatingApp(ActivityRecord activity) { 1108 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { 1109 if (activity.isDescendantOf(mPendingAnimations.get(i).mTask)) { 1110 return true; 1111 } 1112 } 1113 return false; 1114 } 1115 1116 boolean shouldIgnoreForAccessibility(WindowState windowState) { 1117 final Task task = windowState.getTask(); 1118 return task != null && isAnimatingTask(task) && !isTargetApp(windowState.mActivityRecord); 1119 } 1120 1121 /** 1122 * If the animation target ActivityRecord has a fixed rotation ({@link 1123 * WindowToken#hasFixedRotationTransform()}, the provided wallpaper will be rotated accordingly. 1124 * 1125 * This avoids any screen rotation animation when animating to the Recents view. 1126 */ 1127 void linkFixedRotationTransformIfNeeded(@NonNull WindowToken wallpaper) { 1128 if (mTargetActivityRecord == null) { 1129 return; 1130 } 1131 wallpaper.linkFixedRotationTransform(mTargetActivityRecord); 1132 } 1133 1134 @VisibleForTesting 1135 class TaskAnimationAdapter implements AnimationAdapter { 1136 1137 private final Task mTask; 1138 private SurfaceControl mCapturedLeash; 1139 private OnAnimationFinishedCallback mCapturedFinishCallback; 1140 private @AnimationType int mLastAnimationType; 1141 private final boolean mIsRecentTaskInvisible; 1142 private RemoteAnimationTarget mTarget; 1143 private final Rect mBounds = new Rect(); 1144 // The bounds of the target relative to its parent. 1145 private final Rect mLocalBounds = new Rect(); 1146 // The final surface transaction when animation is finished. 1147 private PictureInPictureSurfaceTransaction mFinishTransaction; 1148 // An overlay used to mask the content as an app goes into PIP 1149 private SurfaceControl mFinishOverlay; 1150 // An overlay used for canceling the animation with a screenshot 1151 private SurfaceControl mSnapshotOverlay; 1152 1153 TaskAnimationAdapter(Task task, boolean isRecentTaskInvisible) { 1154 mTask = task; 1155 mIsRecentTaskInvisible = isRecentTaskInvisible; 1156 mBounds.set(mTask.getBounds()); 1157 1158 mLocalBounds.set(mBounds); 1159 Point tmpPos = new Point(); 1160 mTask.getRelativePosition(tmpPos); 1161 mLocalBounds.offsetTo(tmpPos.x, tmpPos.y); 1162 } 1163 1164 /** 1165 * @param overrideTaskId overrides the target's taskId. It may differ from mTaskId and thus 1166 * can differ from taskInfo. This mismatch is needed, however, in 1167 * some cases where we are animating root tasks but need need leaf 1168 * ids for identification. If this is INVALID (-1), then mTaskId 1169 * will be used. 1170 * @param overrideMode overrides the target's mode. If this is -1, the mode will be 1171 * calculated relative to going to the target activity (ie. OPENING if 1172 * this is the target task, CLOSING otherwise). 1173 */ 1174 RemoteAnimationTarget createRemoteAnimationTarget(int overrideTaskId, int overrideMode) { 1175 ActivityRecord topApp = mTask.getTopRealVisibleActivity(); 1176 if (topApp == null) { 1177 topApp = mTask.getTopVisibleActivity(); 1178 } 1179 final WindowState mainWindow = topApp != null 1180 ? topApp.findMainWindow() 1181 : null; 1182 if (mainWindow == null) { 1183 return null; 1184 } 1185 final Rect insets = mainWindow.getInsetsStateWithVisibilityOverride().calculateInsets( 1186 mBounds, Type.systemBars(), false /* ignoreVisibility */).toRect(); 1187 InsetUtils.addInsets(insets, mainWindow.mActivityRecord.getLetterboxInsets()); 1188 final int mode = overrideMode != MODE_UNKNOWN 1189 ? overrideMode 1190 : topApp.getActivityType() == mTargetActivityType 1191 ? MODE_OPENING 1192 : MODE_CLOSING; 1193 if (overrideTaskId < 0) { 1194 overrideTaskId = mTask.mTaskId; 1195 } 1196 mTarget = new RemoteAnimationTarget(overrideTaskId, mode, mCapturedLeash, 1197 !topApp.fillsParent(), new Rect(), 1198 insets, mTask.getPrefixOrderIndex(), new Point(mBounds.left, mBounds.top), 1199 mLocalBounds, mBounds, mTask.getWindowConfiguration(), 1200 mIsRecentTaskInvisible, null, null, mTask.getTaskInfo(), 1201 topApp.checkEnterPictureInPictureAppOpsState()); 1202 1203 final ActivityRecord topActivity = mTask.getTopNonFinishingActivity(); 1204 if (topActivity != null && topActivity.mStartingData != null 1205 && topActivity.mStartingData.hasImeSurface()) { 1206 mTarget.setWillShowImeOnTarget(true); 1207 } 1208 return mTarget; 1209 } 1210 1211 void setSnapshotOverlay(TaskSnapshot snapshot) { 1212 // Create a surface control for the snapshot and reparent it to the leash 1213 final HardwareBuffer buffer = snapshot.getHardwareBuffer(); 1214 if (buffer == null) { 1215 return; 1216 } 1217 1218 final SurfaceSession session = new SurfaceSession(); 1219 mSnapshotOverlay = mService.mSurfaceControlFactory.apply(session) 1220 .setName("RecentTaskScreenshotSurface") 1221 .setCallsite("TaskAnimationAdapter.setSnapshotOverlay") 1222 .setFormat(buffer.getFormat()) 1223 .setParent(mCapturedLeash) 1224 .setBLASTLayer() 1225 .build(); 1226 1227 final float scale = 1.0f * mTask.getBounds().width() / buffer.getWidth(); 1228 mTask.getPendingTransaction() 1229 .setBuffer(mSnapshotOverlay, GraphicBuffer.createFromHardwareBuffer(buffer)) 1230 .setColorSpace(mSnapshotOverlay, snapshot.getColorSpace()) 1231 .setLayer(mSnapshotOverlay, Integer.MAX_VALUE) 1232 .setMatrix(mSnapshotOverlay, scale, 0, 0, scale) 1233 .show(mSnapshotOverlay) 1234 .apply(); 1235 } 1236 1237 void onRemove() { 1238 if (mSnapshotOverlay != null) { 1239 // Clean up the snapshot overlay if necessary 1240 mTask.getPendingTransaction() 1241 .remove(mSnapshotOverlay) 1242 .apply(); 1243 mSnapshotOverlay = null; 1244 } 1245 mTask.setCanAffectSystemUiFlags(true); 1246 mCapturedFinishCallback.onAnimationFinished(mLastAnimationType, this); 1247 } 1248 1249 void onCleanup() { 1250 final Transaction pendingTransaction = mTask.getPendingTransaction(); 1251 if (mFinishTransaction != null) { 1252 // Reparent the overlay 1253 if (mFinishOverlay != null) { 1254 pendingTransaction.reparent(mFinishOverlay, mTask.mSurfaceControl); 1255 } 1256 1257 // Transfer the transform from the leash to the task 1258 PictureInPictureSurfaceTransaction.apply(mFinishTransaction, 1259 mTask.mSurfaceControl, pendingTransaction); 1260 mTask.setLastRecentsAnimationTransaction(mFinishTransaction, mFinishOverlay); 1261 if (mDisplayContent.isFixedRotationLaunchingApp(mTargetActivityRecord)) { 1262 // The transaction is needed for position when rotating the display. 1263 mDisplayContent.mPinnedTaskController.setEnterPipTransaction( 1264 mFinishTransaction); 1265 } 1266 // In the case where we are transferring the transform to the task in preparation 1267 // for entering PIP, we disable the task being able to affect sysui flags otherwise 1268 // it may cause a flash 1269 if (mTask.getActivityType() != mTargetActivityType 1270 && mFinishTransaction.getShouldDisableCanAffectSystemUiFlags()) { 1271 mTask.setCanAffectSystemUiFlags(false); 1272 } 1273 mFinishTransaction = null; 1274 mFinishOverlay = null; 1275 pendingTransaction.apply(); 1276 } else if (!mTask.isAttached()) { 1277 // Apply the task's pending transaction in case it is detached and its transaction 1278 // is not reachable. 1279 pendingTransaction.apply(); 1280 } 1281 } 1282 1283 @VisibleForTesting 1284 public SurfaceControl getSnapshotOverlay() { 1285 return mSnapshotOverlay; 1286 } 1287 1288 @Override 1289 public boolean getShowWallpaper() { 1290 return false; 1291 } 1292 1293 @Override 1294 public void startAnimation(SurfaceControl animationLeash, Transaction t, 1295 @AnimationType int type, @NonNull OnAnimationFinishedCallback finishCallback) { 1296 // Restore position and root task crop until client has a chance to modify it. 1297 t.setPosition(animationLeash, mLocalBounds.left, mLocalBounds.top); 1298 mTmpRect.set(mLocalBounds); 1299 mTmpRect.offsetTo(0, 0); 1300 t.setWindowCrop(animationLeash, mTmpRect); 1301 mCapturedLeash = animationLeash; 1302 mCapturedFinishCallback = finishCallback; 1303 mLastAnimationType = type; 1304 } 1305 1306 @Override 1307 public void onAnimationCancelled(SurfaceControl animationLeash) { 1308 // Cancel the animation immediately if any single task animator is canceled 1309 cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "taskAnimationAdapterCanceled"); 1310 } 1311 1312 @Override 1313 public long getDurationHint() { 1314 return 0; 1315 } 1316 1317 @Override 1318 public long getStatusBarTransitionsStartTime() { 1319 return SystemClock.uptimeMillis(); 1320 } 1321 1322 @Override 1323 public void dump(PrintWriter pw, String prefix) { 1324 pw.print(prefix); pw.println("task=" + mTask); 1325 if (mTarget != null) { 1326 pw.print(prefix); pw.println("Target:"); 1327 mTarget.dump(pw, prefix + " "); 1328 } else { 1329 pw.print(prefix); pw.println("Target: null"); 1330 } 1331 pw.println("mIsRecentTaskInvisible=" + mIsRecentTaskInvisible); 1332 pw.println("mLocalBounds=" + mLocalBounds); 1333 pw.println("mFinishTransaction=" + mFinishTransaction); 1334 pw.println("mBounds=" + mBounds); 1335 pw.println("mIsRecentTaskInvisible=" + mIsRecentTaskInvisible); 1336 } 1337 1338 @Override 1339 public void dumpDebug(ProtoOutputStream proto) { 1340 final long token = proto.start(REMOTE); 1341 if (mTarget != null) { 1342 mTarget.dumpDebug(proto, TARGET); 1343 } 1344 proto.end(token); 1345 } 1346 } 1347 1348 public void dump(PrintWriter pw, String prefix) { 1349 final String innerPrefix = prefix + " "; 1350 pw.print(prefix); pw.println(RecentsAnimationController.class.getSimpleName() + ":"); 1351 pw.print(innerPrefix); pw.println("mPendingStart=" + mPendingStart); 1352 pw.print(innerPrefix); pw.println("mPendingAnimations=" + mPendingAnimations.size()); 1353 pw.print(innerPrefix); pw.println("mCanceled=" + mCanceled); 1354 pw.print(innerPrefix); pw.println("mInputConsumerEnabled=" + mInputConsumerEnabled); 1355 pw.print(innerPrefix); pw.println("mTargetActivityRecord=" + mTargetActivityRecord); 1356 pw.print(innerPrefix); pw.println("isTargetOverWallpaper=" + isTargetOverWallpaper()); 1357 pw.print(innerPrefix); pw.println("mRequestDeferCancelUntilNextTransition=" 1358 + mRequestDeferCancelUntilNextTransition); 1359 pw.print(innerPrefix); pw.println("mCancelOnNextTransitionStart=" 1360 + mCancelOnNextTransitionStart); 1361 pw.print(innerPrefix); pw.println("mCancelDeferredWithScreenshot=" 1362 + mCancelDeferredWithScreenshot); 1363 pw.print(innerPrefix); pw.println("mPendingCancelWithScreenshotReorderMode=" 1364 + mPendingCancelWithScreenshotReorderMode); 1365 } 1366 } 1367