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