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