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