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.WINDOWING_MODE_FULLSCREEN; 20 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; 21 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; 22 import static android.view.RemoteAnimationTarget.MODE_CLOSING; 23 import static android.view.RemoteAnimationTarget.MODE_OPENING; 24 import static android.view.WindowManager.DOCKED_INVALID; 25 import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION; 26 27 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; 28 import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_RECENTS_ANIM; 29 import static com.android.server.wm.AnimationAdapterProto.REMOTE; 30 import static com.android.server.wm.BoundsAnimationController.FADE_IN; 31 import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET; 32 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_RECENTS_ANIMATIONS; 33 import static com.android.server.wm.WindowManagerInternal.AppTransitionListener; 34 35 import android.annotation.IntDef; 36 import android.app.ActivityManager.TaskSnapshot; 37 import android.app.WindowConfiguration; 38 import android.graphics.Point; 39 import android.graphics.Rect; 40 import android.os.Binder; 41 import android.os.IBinder.DeathRecipient; 42 import android.os.RemoteException; 43 import android.os.SystemClock; 44 import android.util.ArraySet; 45 import android.util.Slog; 46 import android.util.SparseBooleanArray; 47 import android.util.SparseIntArray; 48 import android.util.proto.ProtoOutputStream; 49 import android.view.IRecentsAnimationController; 50 import android.view.IRecentsAnimationRunner; 51 import android.view.InputWindowHandle; 52 import android.view.RemoteAnimationTarget; 53 import android.view.SurfaceControl; 54 import android.view.SurfaceControl.Transaction; 55 56 import com.android.internal.annotations.VisibleForTesting; 57 import com.android.server.LocalServices; 58 import com.android.server.inputmethod.InputMethodManagerInternal; 59 import com.android.server.statusbar.StatusBarManagerInternal; 60 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback; 61 import com.android.server.wm.utils.InsetUtils; 62 63 import com.google.android.collect.Sets; 64 65 import java.io.PrintWriter; 66 import java.util.ArrayList; 67 68 /** 69 * Controls a single instance of the remote driven recents animation. In particular, this allows 70 * the calling SystemUI to animate the visible task windows as a part of the transition. The remote 71 * runner is provided an animation controller which allows it to take screenshots and to notify 72 * window manager when the animation is completed. In addition, window manager may also notify the 73 * app if it requires the animation to be canceled at any time (ie. due to timeout, etc.) 74 */ 75 public class RecentsAnimationController implements DeathRecipient { 76 private static final String TAG = RecentsAnimationController.class.getSimpleName(); 77 private static final long FAILSAFE_DELAY = 1000; 78 79 public static final int REORDER_KEEP_IN_PLACE = 0; 80 public static final int REORDER_MOVE_TO_TOP = 1; 81 public static final int REORDER_MOVE_TO_ORIGINAL_POSITION = 2; 82 83 @IntDef(prefix = { "REORDER_MODE_" }, value = { 84 REORDER_KEEP_IN_PLACE, 85 REORDER_MOVE_TO_TOP, 86 REORDER_MOVE_TO_ORIGINAL_POSITION 87 }) 88 public @interface ReorderMode {} 89 90 private final WindowManagerService mService; 91 private final StatusBarManagerInternal mStatusBar; 92 private IRecentsAnimationRunner mRunner; 93 private final RecentsAnimationCallbacks mCallbacks; 94 private final ArrayList<TaskAnimationAdapter> mPendingAnimations = new ArrayList<>(); 95 private final int mDisplayId; 96 private final Runnable mFailsafeRunnable = () -> 97 cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "failSafeRunnable"); 98 99 final Object mLock = new Object(); 100 101 // The recents component app token that is shown behind the visibile tasks 102 private AppWindowToken mTargetAppToken; 103 private int mTargetActivityType; 104 private Rect mMinimizedHomeBounds = new Rect(); 105 106 // We start the RecentsAnimationController in a pending-start state since we need to wait for 107 // the wallpaper/activity to draw before we can give control to the handler to start animating 108 // the visible task surfaces 109 private boolean mPendingStart = true; 110 111 // Set when the animation has been canceled 112 private boolean mCanceled; 113 114 // Whether or not the input consumer is enabled. The input consumer must be both registered and 115 // enabled for it to start intercepting touch events. 116 private boolean mInputConsumerEnabled; 117 118 // Whether or not the recents animation should cause the primary split-screen stack to be 119 // minimized 120 private boolean mSplitScreenMinimized; 121 122 private final Rect mTmpRect = new Rect(); 123 124 private boolean mLinkedToDeathOfRunner; 125 126 private boolean mCancelWithDeferredScreenshot; 127 128 private boolean mCancelOnNextTransitionStart; 129 130 /** 131 * Animates the screenshot of task that used to be controlled by RecentsAnimation. 132 * @see {@link #cancelOnNextTransitionStart} 133 */ 134 SurfaceAnimator mRecentScreenshotAnimator; 135 136 final AppTransitionListener mAppTransitionListener = new AppTransitionListener() { 137 @Override 138 public int onAppTransitionStartingLocked(int transit, long duration, 139 long statusBarAnimationStartTime, long statusBarAnimationDuration) { 140 onTransitionStart(); 141 mService.mRoot.getDisplayContent(mDisplayId).mAppTransition 142 .unregisterListener(this); 143 return 0; 144 } 145 }; 146 147 public interface RecentsAnimationCallbacks { 148 /** Callback when recents animation is finished. */ onAnimationFinished(@eorderMode int reorderMode, boolean runSychronously, boolean sendUserLeaveHint)149 void onAnimationFinished(@ReorderMode int reorderMode, boolean runSychronously, 150 boolean sendUserLeaveHint); 151 } 152 153 private final IRecentsAnimationController mController = 154 new IRecentsAnimationController.Stub() { 155 156 @Override 157 public TaskSnapshot screenshotTask(int taskId) { 158 if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "screenshotTask(" + taskId + "):" 159 + " mCanceled=" + mCanceled); 160 final long token = Binder.clearCallingIdentity(); 161 try { 162 synchronized (mService.getWindowManagerLock()) { 163 if (mCanceled) { 164 return null; 165 } 166 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { 167 final TaskAnimationAdapter adapter = mPendingAnimations.get(i); 168 final Task task = adapter.mTask; 169 if (task.mTaskId == taskId) { 170 final TaskSnapshotController snapshotController = 171 mService.mTaskSnapshotController; 172 final ArraySet<Task> tasks = Sets.newArraySet(task); 173 snapshotController.snapshotTasks(tasks); 174 snapshotController.addSkipClosingAppSnapshotTasks(tasks); 175 return snapshotController.getSnapshot(taskId, 0 /* userId */, 176 false /* restoreFromDisk */, false /* reducedResolution */); 177 } 178 } 179 return null; 180 } 181 } finally { 182 Binder.restoreCallingIdentity(token); 183 } 184 } 185 186 @Override 187 public void finish(boolean moveHomeToTop, boolean sendUserLeaveHint) { 188 if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "finish(" + moveHomeToTop + "):" 189 + " mCanceled=" + mCanceled); 190 final long token = Binder.clearCallingIdentity(); 191 try { 192 synchronized (mService.getWindowManagerLock()) { 193 if (mCanceled) { 194 return; 195 } 196 } 197 198 // Note, the callback will handle its own synchronization, do not lock on WM lock 199 // prior to calling the callback 200 mCallbacks.onAnimationFinished(moveHomeToTop 201 ? REORDER_MOVE_TO_TOP 202 : REORDER_MOVE_TO_ORIGINAL_POSITION, 203 true /* runSynchronously */, sendUserLeaveHint); 204 final DisplayContent dc = mService.mRoot.getDisplayContent(mDisplayId); 205 dc.mBoundsAnimationController.setAnimationType(FADE_IN); 206 } finally { 207 Binder.restoreCallingIdentity(token); 208 } 209 } 210 211 @Override 212 public void setAnimationTargetsBehindSystemBars(boolean behindSystemBars) 213 throws RemoteException { 214 final long token = Binder.clearCallingIdentity(); 215 try { 216 synchronized (mService.getWindowManagerLock()) { 217 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { 218 final Task task = mPendingAnimations.get(i).mTask; 219 if (task.getActivityType() != mTargetActivityType) { 220 task.setCanAffectSystemUiFlags(behindSystemBars); 221 } 222 } 223 mService.mWindowPlacerLocked.requestTraversal(); 224 } 225 } finally { 226 Binder.restoreCallingIdentity(token); 227 } 228 } 229 230 @Override 231 public void setInputConsumerEnabled(boolean enabled) { 232 if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "setInputConsumerEnabled(" + enabled + "):" 233 + " mCanceled=" + mCanceled); 234 final long token = Binder.clearCallingIdentity(); 235 try { 236 synchronized (mService.getWindowManagerLock()) { 237 if (mCanceled) { 238 return; 239 } 240 241 mInputConsumerEnabled = enabled; 242 final InputMonitor inputMonitor = 243 mService.mRoot.getDisplayContent(mDisplayId).getInputMonitor(); 244 inputMonitor.updateInputWindowsLw(true /*force*/); 245 mService.scheduleAnimationLocked(); 246 } 247 } finally { 248 Binder.restoreCallingIdentity(token); 249 } 250 } 251 252 @Override 253 public void setSplitScreenMinimized(boolean minimized) { 254 final long token = Binder.clearCallingIdentity(); 255 try { 256 synchronized (mService.getWindowManagerLock()) { 257 if (mCanceled) { 258 return; 259 } 260 261 mSplitScreenMinimized = minimized; 262 mService.checkSplitScreenMinimizedChanged(true /* animate */); 263 } 264 } finally { 265 Binder.restoreCallingIdentity(token); 266 } 267 } 268 269 @Override 270 public void hideCurrentInputMethod() { 271 final long token = Binder.clearCallingIdentity(); 272 try { 273 final InputMethodManagerInternal inputMethodManagerInternal = 274 LocalServices.getService(InputMethodManagerInternal.class); 275 if (inputMethodManagerInternal != null) { 276 inputMethodManagerInternal.hideCurrentInputMethod(); 277 } 278 } finally { 279 Binder.restoreCallingIdentity(token); 280 } 281 } 282 283 @Override 284 public void setCancelWithDeferredScreenshot(boolean screenshot) { 285 synchronized (mLock) { 286 setCancelWithDeferredScreenshotLocked(screenshot); 287 } 288 } 289 290 @Override 291 public void cleanupScreenshot() { 292 synchronized (mLock) { 293 if (mRecentScreenshotAnimator != null) { 294 mRecentScreenshotAnimator.cancelAnimation(); 295 mRecentScreenshotAnimator = null; 296 } 297 } 298 } 299 }; 300 301 /** 302 * @param remoteAnimationRunner The remote runner which should be notified when the animation is 303 * ready to start or has been canceled 304 * @param callbacks Callbacks to be made when the animation finishes 305 */ RecentsAnimationController(WindowManagerService service, IRecentsAnimationRunner remoteAnimationRunner, RecentsAnimationCallbacks callbacks, int displayId)306 RecentsAnimationController(WindowManagerService service, 307 IRecentsAnimationRunner remoteAnimationRunner, RecentsAnimationCallbacks callbacks, 308 int displayId) { 309 mService = service; 310 mRunner = remoteAnimationRunner; 311 mCallbacks = callbacks; 312 mDisplayId = displayId; 313 mStatusBar = LocalServices.getService(StatusBarManagerInternal.class); 314 } 315 initialize(int targetActivityType, SparseBooleanArray recentTaskIds)316 public void initialize(int targetActivityType, SparseBooleanArray recentTaskIds) { 317 initialize(mService.mRoot.getDisplayContent(mDisplayId), targetActivityType, recentTaskIds); 318 } 319 320 /** 321 * Initializes the recents animation controller. This is a separate call from the constructor 322 * because it may call cancelAnimation() which needs to properly clean up the controller 323 * in the window manager. 324 */ 325 @VisibleForTesting initialize(DisplayContent dc, int targetActivityType, SparseBooleanArray recentTaskIds)326 void initialize(DisplayContent dc, int targetActivityType, SparseBooleanArray recentTaskIds) { 327 mTargetActivityType = targetActivityType; 328 dc.mAppTransition.registerListenerLocked(mAppTransitionListener); 329 330 // Make leashes for each of the visible/target tasks and add it to the recents animation to 331 // be started 332 final ArrayList<Task> visibleTasks = dc.getVisibleTasks(); 333 final TaskStack targetStack = dc.getStack(WINDOWING_MODE_UNDEFINED, targetActivityType); 334 if (targetStack != null) { 335 for (int i = targetStack.getChildCount() - 1; i >= 0; i--) { 336 final Task t = targetStack.getChildAt(i); 337 if (!visibleTasks.contains(t)) { 338 visibleTasks.add(t); 339 } 340 } 341 } 342 final int taskCount = visibleTasks.size(); 343 for (int i = 0; i < taskCount; i++) { 344 final Task task = visibleTasks.get(i); 345 final WindowConfiguration config = task.getWindowConfiguration(); 346 if (config.tasksAreFloating() 347 || config.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) { 348 continue; 349 } 350 addAnimation(task, !recentTaskIds.get(task.mTaskId)); 351 } 352 353 // Skip the animation if there is nothing to animate 354 if (mPendingAnimations.isEmpty()) { 355 cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "initialize-noVisibleTasks"); 356 return; 357 } 358 359 try { 360 linkToDeathOfRunner(); 361 } catch (RemoteException e) { 362 cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "initialize-failedToLinkToDeath"); 363 return; 364 } 365 366 // Adjust the wallpaper visibility for the showing target activity 367 final AppWindowToken recentsComponentAppToken = dc.getStack(WINDOWING_MODE_UNDEFINED, 368 targetActivityType).getTopChild().getTopFullscreenAppToken(); 369 if (recentsComponentAppToken != null) { 370 if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "setHomeApp(" 371 + recentsComponentAppToken.getName() + ")"); 372 mTargetAppToken = recentsComponentAppToken; 373 if (recentsComponentAppToken.windowsCanBeWallpaperTarget()) { 374 dc.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER; 375 dc.setLayoutNeeded(); 376 } 377 } 378 379 // Save the minimized home height 380 final TaskStack dockedStack = dc.getSplitScreenPrimaryStackIgnoringVisibility(); 381 dc.getDockedDividerController().getHomeStackBoundsInDockedMode( 382 dc.getConfiguration(), 383 dockedStack == null ? DOCKED_INVALID : dockedStack.getDockSide(), 384 mMinimizedHomeBounds); 385 386 mService.mWindowPlacerLocked.performSurfacePlacement(); 387 388 // Notify that the animation has started 389 mStatusBar.onRecentsAnimationStateChanged(true /* running */); 390 } 391 392 @VisibleForTesting addAnimation(Task task, boolean isRecentTaskInvisible)393 AnimationAdapter addAnimation(Task task, boolean isRecentTaskInvisible) { 394 if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "addAnimation(" + task.getName() + ")"); 395 final TaskAnimationAdapter taskAdapter = new TaskAnimationAdapter(task, 396 isRecentTaskInvisible); 397 task.startAnimation(task.getPendingTransaction(), taskAdapter, false /* hidden */); 398 task.commitPendingTransaction(); 399 mPendingAnimations.add(taskAdapter); 400 return taskAdapter; 401 } 402 403 @VisibleForTesting removeAnimation(TaskAnimationAdapter taskAdapter)404 void removeAnimation(TaskAnimationAdapter taskAdapter) { 405 if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "removeAnimation(" 406 + taskAdapter.mTask.mTaskId + ")"); 407 taskAdapter.mTask.setCanAffectSystemUiFlags(true); 408 taskAdapter.mCapturedFinishCallback.onAnimationFinished(taskAdapter); 409 mPendingAnimations.remove(taskAdapter); 410 } 411 startAnimation()412 void startAnimation() { 413 if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "startAnimation(): mPendingStart=" + mPendingStart 414 + " mCanceled=" + mCanceled); 415 if (!mPendingStart || mCanceled) { 416 // Skip starting if we've already started or canceled the animation 417 return; 418 } 419 try { 420 final ArrayList<RemoteAnimationTarget> appAnimations = new ArrayList<>(); 421 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { 422 final TaskAnimationAdapter taskAdapter = mPendingAnimations.get(i); 423 final RemoteAnimationTarget target = taskAdapter.createRemoteAnimationApp(); 424 if (target != null) { 425 appAnimations.add(target); 426 } else { 427 removeAnimation(taskAdapter); 428 } 429 } 430 431 // Skip the animation if there is nothing to animate 432 if (appAnimations.isEmpty()) { 433 cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "startAnimation-noAppWindows"); 434 return; 435 } 436 437 final RemoteAnimationTarget[] appTargets = appAnimations.toArray( 438 new RemoteAnimationTarget[appAnimations.size()]); 439 mPendingStart = false; 440 441 // Perform layout if it was scheduled before to make sure that we get correct content 442 // insets for the target app window after a rotation 443 final DisplayContent displayContent = mService.mRoot.getDisplayContent(mDisplayId); 444 displayContent.performLayout(false /* initial */, false /* updateInputWindows */); 445 446 final Rect minimizedHomeBounds = mTargetAppToken != null 447 && mTargetAppToken.inSplitScreenSecondaryWindowingMode() 448 ? mMinimizedHomeBounds 449 : null; 450 final Rect contentInsets; 451 if (mTargetAppToken != null && mTargetAppToken.findMainWindow() != null) { 452 contentInsets = mTargetAppToken.findMainWindow().getContentInsets(); 453 } else { 454 // If the window for the activity had not yet been created, use the display insets. 455 mService.getStableInsets(mDisplayId, mTmpRect); 456 contentInsets = mTmpRect; 457 } 458 mRunner.onAnimationStart(mController, appTargets, contentInsets, minimizedHomeBounds); 459 if (DEBUG_RECENTS_ANIMATIONS) { 460 Slog.d(TAG, "startAnimation(): Notify animation start:"); 461 for (int i = 0; i < mPendingAnimations.size(); i++) { 462 final Task task = mPendingAnimations.get(i).mTask; 463 Slog.d(TAG, "\t" + task.mTaskId); 464 } 465 } 466 } catch (RemoteException e) { 467 Slog.e(TAG, "Failed to start recents animation", e); 468 } 469 final SparseIntArray reasons = new SparseIntArray(); 470 reasons.put(WINDOWING_MODE_FULLSCREEN, APP_TRANSITION_RECENTS_ANIM); 471 mService.mAtmInternal.notifyAppTransitionStarting(reasons, SystemClock.uptimeMillis()); 472 } 473 cancelAnimation(@eorderMode int reorderMode, String reason)474 void cancelAnimation(@ReorderMode int reorderMode, String reason) { 475 cancelAnimation(reorderMode, false /* runSynchronously */, false /*screenshot */, reason); 476 } 477 cancelAnimationSynchronously(@eorderMode int reorderMode, String reason)478 void cancelAnimationSynchronously(@ReorderMode int reorderMode, String reason) { 479 cancelAnimation(reorderMode, true /* runSynchronously */, false /* screenshot */, reason); 480 } 481 cancelAnimationWithScreenShot()482 void cancelAnimationWithScreenShot() { 483 cancelAnimation(REORDER_KEEP_IN_PLACE, true /* sync */, true /* screenshot */, 484 "stackOrderChanged"); 485 } 486 cancelAnimation(@eorderMode int reorderMode, boolean runSynchronously, boolean screenshot, String reason)487 private void cancelAnimation(@ReorderMode int reorderMode, boolean runSynchronously, 488 boolean screenshot, String reason) { 489 if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "cancelAnimation(): reason=" + reason 490 + " runSynchronously=" + runSynchronously); 491 synchronized (mService.getWindowManagerLock()) { 492 if (mCanceled) { 493 // We've already canceled the animation 494 return; 495 } 496 mService.mH.removeCallbacks(mFailsafeRunnable); 497 mCanceled = true; 498 try { 499 if (screenshot) { 500 // Screen shot previous task when next task starts transition. 501 final Task task = mPendingAnimations.get(0).mTask; 502 screenshotRecentTask(task, reorderMode, runSynchronously); 503 mRunner.onAnimationCanceled(true /* deferredWithScreenshot */); 504 return; 505 } 506 mRunner.onAnimationCanceled(false /* deferredWithScreenshot */); 507 } catch (RemoteException e) { 508 Slog.e(TAG, "Failed to cancel recents animation", e); 509 } 510 // Clean up and return to the previous app 511 mCallbacks.onAnimationFinished(reorderMode, runSynchronously, 512 false /* sendUserLeaveHint */); 513 } 514 } 515 516 /** 517 * Cancel recents animation when the next app transition starts. 518 * <p> 519 * When we cancel the recents animation due to a stack order change, we can't just cancel it 520 * immediately as it would lead to a flicker in Launcher if we just remove the task from the 521 * leash. Instead we screenshot the previous task and replace the child of the leash with the 522 * screenshot, so that Launcher can still control the leash lifecycle & make the next app 523 * transition animate smoothly without flickering. 524 */ cancelOnNextTransitionStart()525 void cancelOnNextTransitionStart() { 526 mCancelOnNextTransitionStart = true; 527 } 528 setCancelWithDeferredScreenshotLocked(boolean screenshot)529 void setCancelWithDeferredScreenshotLocked(boolean screenshot) { 530 mCancelWithDeferredScreenshot = screenshot; 531 } 532 shouldCancelWithDeferredScreenshot()533 boolean shouldCancelWithDeferredScreenshot() { 534 return mCancelWithDeferredScreenshot; 535 } 536 onTransitionStart()537 void onTransitionStart() { 538 if (mCanceled) { 539 return; 540 } 541 542 if (mCancelOnNextTransitionStart) { 543 mCancelOnNextTransitionStart = false; 544 cancelAnimationWithScreenShot(); 545 } 546 } 547 screenshotRecentTask(Task task, @ReorderMode int reorderMode, boolean runSynchronously)548 void screenshotRecentTask(Task task, @ReorderMode int reorderMode, boolean runSynchronously) { 549 final TaskScreenshotAnimatable animatable = TaskScreenshotAnimatable.create(task); 550 if (animatable != null) { 551 mRecentScreenshotAnimator = new SurfaceAnimator( 552 animatable, 553 () -> { 554 if (DEBUG_RECENTS_ANIMATIONS) { 555 Slog.d(TAG, "mRecentScreenshotAnimator finish"); 556 } 557 mCallbacks.onAnimationFinished(reorderMode, runSynchronously, 558 false /* sendUserLeaveHint */); 559 }, mService); 560 mRecentScreenshotAnimator.transferAnimation(task.mSurfaceAnimator); 561 } 562 } 563 cleanupAnimation(@eorderMode int reorderMode)564 void cleanupAnimation(@ReorderMode int reorderMode) { 565 if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, 566 "cleanupAnimation(): Notify animation finished mPendingAnimations=" 567 + mPendingAnimations.size() + " reorderMode=" + reorderMode); 568 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { 569 final TaskAnimationAdapter taskAdapter = mPendingAnimations.get(i); 570 if (reorderMode == REORDER_MOVE_TO_TOP || reorderMode == REORDER_KEEP_IN_PLACE) { 571 taskAdapter.mTask.dontAnimateDimExit(); 572 } 573 removeAnimation(taskAdapter); 574 } 575 576 // Clear any pending failsafe runnables 577 mService.mH.removeCallbacks(mFailsafeRunnable); 578 579 // Clear references to the runner 580 unlinkToDeathOfRunner(); 581 mRunner = null; 582 mCanceled = true; 583 584 // Make sure previous animator has cleaned-up. 585 if (mRecentScreenshotAnimator != null) { 586 mRecentScreenshotAnimator.cancelAnimation(); 587 mRecentScreenshotAnimator = null; 588 } 589 590 // Update the input windows after the animation is complete 591 final InputMonitor inputMonitor = 592 mService.mRoot.getDisplayContent(mDisplayId).getInputMonitor(); 593 inputMonitor.updateInputWindowsLw(true /*force*/); 594 595 // We have deferred all notifications to the target app as a part of the recents animation, 596 // so if we are actually transitioning there, notify again here 597 if (mTargetAppToken != null) { 598 if (reorderMode == REORDER_MOVE_TO_TOP || reorderMode == REORDER_KEEP_IN_PLACE) { 599 mService.mRoot.getDisplayContent(mDisplayId) 600 .mAppTransition.notifyAppTransitionFinishedLocked(mTargetAppToken.token); 601 } 602 } 603 604 // Notify that the animation has ended 605 mStatusBar.onRecentsAnimationStateChanged(false /* running */); 606 } 607 scheduleFailsafe()608 void scheduleFailsafe() { 609 mService.mH.postDelayed(mFailsafeRunnable, FAILSAFE_DELAY); 610 } 611 linkToDeathOfRunner()612 private void linkToDeathOfRunner() throws RemoteException { 613 if (!mLinkedToDeathOfRunner) { 614 mRunner.asBinder().linkToDeath(this, 0); 615 mLinkedToDeathOfRunner = true; 616 } 617 } 618 unlinkToDeathOfRunner()619 private void unlinkToDeathOfRunner() { 620 if (mLinkedToDeathOfRunner) { 621 mRunner.asBinder().unlinkToDeath(this, 0); 622 mLinkedToDeathOfRunner = false; 623 } 624 } 625 626 @Override binderDied()627 public void binderDied() { 628 cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "binderDied"); 629 630 synchronized (mService.getWindowManagerLock()) { 631 // Clear associated input consumers on runner death 632 final InputMonitor inputMonitor = 633 mService.mRoot.getDisplayContent(mDisplayId).getInputMonitor(); 634 inputMonitor.destroyInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION); 635 } 636 } 637 checkAnimationReady(WallpaperController wallpaperController)638 void checkAnimationReady(WallpaperController wallpaperController) { 639 if (mPendingStart) { 640 final boolean wallpaperReady = !isTargetOverWallpaper() 641 || (wallpaperController.getWallpaperTarget() != null 642 && wallpaperController.wallpaperTransitionReady()); 643 if (wallpaperReady) { 644 mService.getRecentsAnimationController().startAnimation(); 645 } 646 } 647 } 648 isSplitScreenMinimized()649 boolean isSplitScreenMinimized() { 650 return mSplitScreenMinimized; 651 } 652 isWallpaperVisible(WindowState w)653 boolean isWallpaperVisible(WindowState w) { 654 return w != null && w.mAppToken != null && mTargetAppToken == w.mAppToken 655 && isTargetOverWallpaper(); 656 } 657 658 /** 659 * @return Whether to use the input consumer to override app input to route home/recents. 660 */ shouldApplyInputConsumer(AppWindowToken appToken)661 boolean shouldApplyInputConsumer(AppWindowToken appToken) { 662 // Only apply the input consumer if it is enabled, it is not the target (home/recents) 663 // being revealed with the transition, and we are actively animating the app as a part of 664 // the animation 665 return mInputConsumerEnabled && mTargetAppToken != appToken && isAnimatingApp(appToken); 666 } 667 updateInputConsumerForApp(InputWindowHandle inputWindowHandle, boolean hasFocus)668 boolean updateInputConsumerForApp(InputWindowHandle inputWindowHandle, 669 boolean hasFocus) { 670 // Update the input consumer touchable region to match the target app main window 671 final WindowState targetAppMainWindow = mTargetAppToken != null 672 ? mTargetAppToken.findMainWindow() 673 : null; 674 if (targetAppMainWindow != null) { 675 targetAppMainWindow.getBounds(mTmpRect); 676 inputWindowHandle.hasFocus = hasFocus; 677 inputWindowHandle.touchableRegion.set(mTmpRect); 678 return true; 679 } 680 return false; 681 } 682 isTargetApp(AppWindowToken token)683 boolean isTargetApp(AppWindowToken token) { 684 return mTargetAppToken != null && token == mTargetAppToken; 685 } 686 isTargetOverWallpaper()687 private boolean isTargetOverWallpaper() { 688 if (mTargetAppToken == null) { 689 return false; 690 } 691 return mTargetAppToken.windowsCanBeWallpaperTarget(); 692 } 693 isAnimatingTask(Task task)694 boolean isAnimatingTask(Task task) { 695 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { 696 if (task == mPendingAnimations.get(i).mTask) { 697 return true; 698 } 699 } 700 return false; 701 } 702 isAnimatingApp(AppWindowToken appToken)703 private boolean isAnimatingApp(AppWindowToken appToken) { 704 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { 705 final Task task = mPendingAnimations.get(i).mTask; 706 for (int j = task.getChildCount() - 1; j >= 0; j--) { 707 final AppWindowToken app = task.getChildAt(j); 708 if (app == appToken) { 709 return true; 710 } 711 } 712 } 713 return false; 714 } 715 716 @VisibleForTesting 717 class TaskAnimationAdapter implements AnimationAdapter { 718 719 private final Task mTask; 720 private SurfaceControl mCapturedLeash; 721 private OnAnimationFinishedCallback mCapturedFinishCallback; 722 private final boolean mIsRecentTaskInvisible; 723 private RemoteAnimationTarget mTarget; 724 private final Point mPosition = new Point(); 725 private final Rect mBounds = new Rect(); 726 TaskAnimationAdapter(Task task, boolean isRecentTaskInvisible)727 TaskAnimationAdapter(Task task, boolean isRecentTaskInvisible) { 728 mTask = task; 729 mIsRecentTaskInvisible = isRecentTaskInvisible; 730 final WindowContainer container = mTask.getParent(); 731 container.getRelativeDisplayedPosition(mPosition); 732 mBounds.set(container.getDisplayedBounds()); 733 } 734 createRemoteAnimationApp()735 RemoteAnimationTarget createRemoteAnimationApp() { 736 final AppWindowToken topApp = mTask.getTopVisibleAppToken(); 737 final WindowState mainWindow = topApp != null 738 ? topApp.findMainWindow() 739 : null; 740 if (mainWindow == null) { 741 return null; 742 } 743 final Rect insets = new Rect(); 744 mainWindow.getContentInsets(insets); 745 InsetUtils.addInsets(insets, mainWindow.mAppToken.getLetterboxInsets()); 746 final int mode = topApp.getActivityType() == mTargetActivityType 747 ? MODE_OPENING 748 : MODE_CLOSING; 749 mTarget = new RemoteAnimationTarget(mTask.mTaskId, mode, mCapturedLeash, 750 !topApp.fillsParent(), mainWindow.mWinAnimator.mLastClipRect, 751 insets, mTask.getPrefixOrderIndex(), mPosition, mBounds, 752 mTask.getWindowConfiguration(), mIsRecentTaskInvisible, null, null); 753 return mTarget; 754 } 755 756 @Override getShowWallpaper()757 public boolean getShowWallpaper() { 758 return false; 759 } 760 761 @Override getBackgroundColor()762 public int getBackgroundColor() { 763 return 0; 764 } 765 766 @Override startAnimation(SurfaceControl animationLeash, Transaction t, OnAnimationFinishedCallback finishCallback)767 public void startAnimation(SurfaceControl animationLeash, Transaction t, 768 OnAnimationFinishedCallback finishCallback) { 769 // Restore z-layering, position and stack crop until client has a chance to modify it. 770 t.setLayer(animationLeash, mTask.getPrefixOrderIndex()); 771 t.setPosition(animationLeash, mPosition.x, mPosition.y); 772 mTmpRect.set(mBounds); 773 mTmpRect.offsetTo(0, 0); 774 t.setWindowCrop(animationLeash, mTmpRect); 775 mCapturedLeash = animationLeash; 776 mCapturedFinishCallback = finishCallback; 777 } 778 779 @Override onAnimationCancelled(SurfaceControl animationLeash)780 public void onAnimationCancelled(SurfaceControl animationLeash) { 781 cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "taskAnimationAdapterCanceled"); 782 } 783 784 @Override getDurationHint()785 public long getDurationHint() { 786 return 0; 787 } 788 789 @Override getStatusBarTransitionsStartTime()790 public long getStatusBarTransitionsStartTime() { 791 return SystemClock.uptimeMillis(); 792 } 793 794 @Override dump(PrintWriter pw, String prefix)795 public void dump(PrintWriter pw, String prefix) { 796 pw.print(prefix); pw.println("task=" + mTask); 797 if (mTarget != null) { 798 pw.print(prefix); pw.println("Target:"); 799 mTarget.dump(pw, prefix + " "); 800 } else { 801 pw.print(prefix); pw.println("Target: null"); 802 } 803 pw.println("mIsRecentTaskInvisible=" + mIsRecentTaskInvisible); 804 pw.println("mPosition=" + mPosition); 805 pw.println("mBounds=" + mBounds); 806 pw.println("mIsRecentTaskInvisible=" + mIsRecentTaskInvisible); 807 } 808 809 @Override writeToProto(ProtoOutputStream proto)810 public void writeToProto(ProtoOutputStream proto) { 811 final long token = proto.start(REMOTE); 812 if (mTarget != null) { 813 mTarget.writeToProto(proto, TARGET); 814 } 815 proto.end(token); 816 } 817 } 818 dump(PrintWriter pw, String prefix)819 public void dump(PrintWriter pw, String prefix) { 820 final String innerPrefix = prefix + " "; 821 pw.print(prefix); pw.println(RecentsAnimationController.class.getSimpleName() + ":"); 822 pw.print(innerPrefix); pw.println("mPendingStart=" + mPendingStart); 823 pw.print(innerPrefix); pw.println("mPendingAnimations=" + mPendingAnimations.size()); 824 pw.print(innerPrefix); pw.println("mCanceled=" + mCanceled); 825 pw.print(innerPrefix); pw.println("mInputConsumerEnabled=" + mInputConsumerEnabled); 826 pw.print(innerPrefix); pw.println("mSplitScreenMinimized=" + mSplitScreenMinimized); 827 pw.print(innerPrefix); pw.println("mTargetAppToken=" + mTargetAppToken); 828 pw.print(innerPrefix); pw.println("isTargetOverWallpaper=" + isTargetOverWallpaper()); 829 } 830 } 831