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