1 /* 2 * Copyright (C) 2021 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.view.RemoteAnimationTarget.MODE_CLOSING; 21 import static android.view.RemoteAnimationTarget.MODE_OPENING; 22 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; 23 import static android.view.WindowManager.TRANSIT_CLOSE; 24 import static android.view.WindowManager.TRANSIT_OLD_NONE; 25 import static android.view.WindowManager.TRANSIT_TO_BACK; 26 27 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_BACK_PREVIEW; 28 import static com.android.server.wm.BackNavigationProto.ANIMATION_IN_PROGRESS; 29 import static com.android.server.wm.BackNavigationProto.ANIMATION_RUNNING; 30 import static com.android.server.wm.BackNavigationProto.LAST_BACK_TYPE; 31 import static com.android.server.wm.BackNavigationProto.MAIN_OPEN_ACTIVITY; 32 import static com.android.server.wm.BackNavigationProto.SHOW_WALLPAPER; 33 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_PREDICT_BACK; 34 35 import android.annotation.NonNull; 36 import android.annotation.Nullable; 37 import android.content.Context; 38 import android.content.res.ResourceId; 39 import android.graphics.Point; 40 import android.graphics.Rect; 41 import android.os.Bundle; 42 import android.os.IBinder; 43 import android.os.RemoteCallback; 44 import android.os.RemoteException; 45 import android.os.SystemProperties; 46 import android.util.ArraySet; 47 import android.util.Slog; 48 import android.util.proto.ProtoOutputStream; 49 import android.view.RemoteAnimationTarget; 50 import android.view.SurfaceControl; 51 import android.view.WindowInsets; 52 import android.window.BackAnimationAdapter; 53 import android.window.BackNavigationInfo; 54 import android.window.IBackAnimationFinishedCallback; 55 import android.window.IWindowlessStartingSurfaceCallback; 56 import android.window.OnBackInvokedCallbackInfo; 57 import android.window.TaskSnapshot; 58 59 import com.android.internal.annotations.VisibleForTesting; 60 import com.android.internal.policy.TransitionAnimation; 61 import com.android.internal.protolog.common.ProtoLog; 62 import com.android.server.LocalServices; 63 import com.android.server.wm.utils.InsetUtils; 64 65 import java.io.PrintWriter; 66 import java.util.ArrayList; 67 import java.util.Objects; 68 69 /** 70 * Controller to handle actions related to the back gesture on the server side. 71 */ 72 class BackNavigationController { 73 private static final String TAG = "CoreBackPreview"; 74 private WindowManagerService mWindowManagerService; 75 private boolean mBackAnimationInProgress; 76 private @BackNavigationInfo.BackTargetType int mLastBackType; 77 private boolean mShowWallpaper; 78 private Runnable mPendingAnimation; 79 80 private boolean mBackAnimationRunning; 81 private final NavigationMonitor mNavigationMonitor = new NavigationMonitor(); 82 83 private AnimationHandler mAnimationHandler; 84 85 /** 86 * The transition who match the back navigation targets, 87 * release animation after this transition finish. 88 */ 89 private Transition mWaitTransitionFinish; 90 private final ArrayList<WindowContainer> mTmpOpenApps = new ArrayList<>(); 91 private final ArrayList<WindowContainer> mTmpCloseApps = new ArrayList<>(); 92 93 // This will be set if the back navigation is in progress and the current transition is still 94 // running. The pending animation builder will do the animation stuff includes creating leashes, 95 // re-parenting leashes and set launch behind, etc. Will be handled when transition finished. 96 private AnimationHandler.ScheduleAnimationBuilder mPendingAnimationBuilder; 97 98 private static int sDefaultAnimationResId; 99 100 /** 101 * true if the back predictability feature is enabled 102 */ 103 static final boolean sPredictBackEnable = 104 SystemProperties.getBoolean("persist.wm.debug.predictive_back", true); 105 isScreenshotEnabled()106 static boolean isScreenshotEnabled() { 107 return SystemProperties.getInt("persist.wm.debug.predictive_back_screenshot", 0) != 0; 108 } 109 110 // Notify focus window changed onFocusChanged(WindowState newFocus)111 void onFocusChanged(WindowState newFocus) { 112 mNavigationMonitor.onFocusWindowChanged(newFocus); 113 } 114 115 /** 116 * Set up the necessary leashes and build a {@link BackNavigationInfo} instance for an upcoming 117 * back gesture animation. 118 * 119 * @return a {@link BackNavigationInfo} instance containing the required leashes and metadata 120 * for the animation, or null if we don't know how to animate the current window and need to 121 * fallback on dispatching the key event. 122 */ 123 @VisibleForTesting 124 @Nullable startBackNavigation(@onNull RemoteCallback navigationObserver, BackAnimationAdapter adapter)125 BackNavigationInfo startBackNavigation(@NonNull RemoteCallback navigationObserver, 126 BackAnimationAdapter adapter) { 127 if (!sPredictBackEnable) { 128 return null; 129 } 130 final WindowManagerService wmService = mWindowManagerService; 131 132 int backType = BackNavigationInfo.TYPE_UNDEFINED; 133 134 // The currently visible activity (if any). 135 ActivityRecord currentActivity = null; 136 137 // The currently visible task (if any). 138 Task currentTask = null; 139 140 // The previous task we're going back to. Can be the same as currentTask, if there are 141 // multiple Activities in the Stack. 142 Task prevTask = null; 143 144 // The previous activity we're going back to. This can be either a child of currentTask 145 // if there are more than one Activity in currentTask, or a child of prevTask, if 146 // currentActivity is the last child of currentTask. 147 ActivityRecord prevActivity; 148 WindowContainer<?> removedWindowContainer = null; 149 WindowState window; 150 151 BackNavigationInfo.Builder infoBuilder = new BackNavigationInfo.Builder(); 152 synchronized (wmService.mGlobalLock) { 153 if (isMonitoringTransition()) { 154 Slog.w(TAG, "Previous animation hasn't finish, status: " + mAnimationHandler); 155 // Don't start any animation for it. 156 return null; 157 } 158 WindowManagerInternal windowManagerInternal = 159 LocalServices.getService(WindowManagerInternal.class); 160 IBinder focusedWindowToken = windowManagerInternal.getFocusedWindowToken(); 161 162 window = wmService.getFocusedWindowLocked(); 163 164 if (window == null) { 165 EmbeddedWindowController.EmbeddedWindow embeddedWindow = 166 wmService.mEmbeddedWindowController.getByFocusToken(focusedWindowToken); 167 if (embeddedWindow != null) { 168 ProtoLog.d(WM_DEBUG_BACK_PREVIEW, 169 "Current focused window is embeddedWindow. Dispatch KEYCODE_BACK."); 170 return null; 171 } 172 } 173 174 // Lets first gather the states of things 175 // - What is our current window ? 176 // - Does it has an Activity and a Task ? 177 // TODO Temp workaround for Sysui until b/221071505 is fixed 178 if (window != null) { 179 ProtoLog.d(WM_DEBUG_BACK_PREVIEW, 180 "Focused window found using getFocusedWindowToken"); 181 } 182 183 if (window != null) { 184 // This is needed to bridge the old and new back behavior with recents. While in 185 // Overview with live tile enabled, the previous app is technically focused but we 186 // add an input consumer to capture all input that would otherwise go to the apps 187 // being controlled by the animation. This means that the window resolved is not 188 // the right window to consume back while in overview, so we need to route it to 189 // launcher and use the legacy behavior of injecting KEYCODE_BACK since the existing 190 // compat callback in VRI only works when the window is focused. 191 // This symptom also happen while shell transition enabled, we can check that by 192 // isTransientLaunch to know whether the focus window is point to live tile. 193 final RecentsAnimationController recentsAnimationController = 194 wmService.getRecentsAnimationController(); 195 final ActivityRecord ar = window.mActivityRecord; 196 if ((ar != null && ar.isActivityTypeHomeOrRecents() 197 && ar.mTransitionController.isTransientLaunch(ar)) 198 || (recentsAnimationController != null 199 && recentsAnimationController.shouldApplyInputConsumer(ar))) { 200 ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Current focused window being animated by " 201 + "recents. Overriding back callback to recents controller callback."); 202 return null; 203 } 204 205 if (!window.isDrawn()) { 206 ProtoLog.d(WM_DEBUG_BACK_PREVIEW, 207 "Focused window didn't have a valid surface drawn."); 208 return null; 209 } 210 } 211 212 if (window == null) { 213 // We don't have any focused window, fallback ont the top currentTask of the focused 214 // display. 215 ProtoLog.w(WM_DEBUG_BACK_PREVIEW, 216 "No focused window, defaulting to top current task's window"); 217 currentTask = wmService.mAtmService.getTopDisplayFocusedRootTask(); 218 window = currentTask.getWindow(WindowState::isFocused); 219 } 220 221 // Now let's find if this window has a callback from the client side. 222 OnBackInvokedCallbackInfo callbackInfo = null; 223 if (window != null) { 224 currentActivity = window.mActivityRecord; 225 currentTask = window.getTask(); 226 callbackInfo = window.getOnBackInvokedCallbackInfo(); 227 if (callbackInfo == null) { 228 Slog.e(TAG, "No callback registered, returning null."); 229 return null; 230 } 231 if (!callbackInfo.isSystemCallback()) { 232 backType = BackNavigationInfo.TYPE_CALLBACK; 233 } 234 infoBuilder.setOnBackInvokedCallback(callbackInfo.getCallback()); 235 infoBuilder.setAnimationCallback(callbackInfo.isAnimationCallback()); 236 mNavigationMonitor.startMonitor(window, navigationObserver); 237 } 238 239 ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "startBackNavigation currentTask=%s, " 240 + "topRunningActivity=%s, callbackInfo=%s, currentFocus=%s", 241 currentTask, currentActivity, callbackInfo, window); 242 243 if (window == null) { 244 Slog.e(TAG, "Window is null, returning null."); 245 return null; 246 } 247 248 // If we don't need to set up the animation, we return early. This is the case when 249 // - We have an application callback. 250 // - We don't have any ActivityRecord or Task to animate. 251 // - The IME is opened, and we just need to close it. 252 // - The home activity is the focused activity. 253 // - The current activity will do shared element transition when exiting. 254 if (backType == BackNavigationInfo.TYPE_CALLBACK 255 || currentActivity == null 256 || currentTask == null 257 || currentActivity.isActivityTypeHome() 258 || currentActivity.mHasSceneTransition) { 259 infoBuilder.setType(BackNavigationInfo.TYPE_CALLBACK); 260 infoBuilder.setOnBackNavigationDone(new RemoteCallback(result -> 261 onBackNavigationDone(result, BackNavigationInfo.TYPE_CALLBACK))); 262 mLastBackType = BackNavigationInfo.TYPE_CALLBACK; 263 return infoBuilder.build(); 264 } 265 266 // We don't have an application callback, let's find the destination of the back gesture 267 // The search logic should align with ActivityClientController#finishActivity 268 prevActivity = currentTask.topRunningActivity(currentActivity.token, INVALID_TASK_ID); 269 final boolean isOccluded = isKeyguardOccluded(window); 270 // TODO Dialog window does not need to attach on activity, check 271 // window.mAttrs.type != TYPE_BASE_APPLICATION 272 if ((window.getParent().getChildCount() > 1 273 && window.getParent().getChildAt(0) != window)) { 274 // Are we the top window of our parent? If not, we are a window on top of the 275 // activity, we won't close the activity. 276 backType = BackNavigationInfo.TYPE_DIALOG_CLOSE; 277 removedWindowContainer = window; 278 } else if (prevActivity != null) { 279 if (!isOccluded || prevActivity.canShowWhenLocked()) { 280 // We have another Activity in the same currentTask to go to 281 final WindowContainer parent = currentActivity.getParent(); 282 final boolean canCustomize = parent != null 283 && (parent.asTask() != null 284 || (parent.asTaskFragment() != null 285 && parent.canCustomizeAppTransition())); 286 if (canCustomize) { 287 if (isCustomizeExitAnimation(window)) { 288 infoBuilder.setWindowAnimations( 289 window.mAttrs.packageName, window.mAttrs.windowAnimations); 290 } 291 final ActivityRecord.CustomAppTransition customAppTransition = 292 currentActivity.getCustomAnimation(false/* open */); 293 if (customAppTransition != null) { 294 infoBuilder.setCustomAnimation(currentActivity.packageName, 295 customAppTransition.mEnterAnim, 296 customAppTransition.mExitAnim, 297 customAppTransition.mBackgroundColor); 298 } 299 } 300 removedWindowContainer = currentActivity; 301 prevTask = prevActivity.getTask(); 302 backType = BackNavigationInfo.TYPE_CROSS_ACTIVITY; 303 } else { 304 backType = BackNavigationInfo.TYPE_CALLBACK; 305 } 306 } else if (currentTask.returnsToHomeRootTask()) { 307 if (isOccluded) { 308 backType = BackNavigationInfo.TYPE_CALLBACK; 309 } else { 310 // Our Task should bring back to home 311 removedWindowContainer = currentTask; 312 prevTask = currentTask.getDisplayArea().getRootHomeTask(); 313 backType = BackNavigationInfo.TYPE_RETURN_TO_HOME; 314 mShowWallpaper = true; 315 } 316 } else if (currentActivity.isRootOfTask()) { 317 // TODO(208789724): Create single source of truth for this, maybe in 318 // RootWindowContainer 319 prevTask = currentTask.mRootWindowContainer.getTask(Task::showToCurrentUser, 320 currentTask, false /*includeBoundary*/, true /*traverseTopToBottom*/); 321 removedWindowContainer = currentTask; 322 // If it reaches the top activity, we will check the below task from parent. 323 // If it's null or multi-window, fallback the type to TYPE_CALLBACK. 324 // or set the type to proper value when it's return to home or another task. 325 if (prevTask == null || prevTask.inMultiWindowMode()) { 326 backType = BackNavigationInfo.TYPE_CALLBACK; 327 } else { 328 prevActivity = prevTask.getTopNonFinishingActivity(); 329 if (prevActivity == null || (isOccluded && !prevActivity.canShowWhenLocked())) { 330 backType = BackNavigationInfo.TYPE_CALLBACK; 331 } else if (prevTask.isActivityTypeHome()) { 332 backType = BackNavigationInfo.TYPE_RETURN_TO_HOME; 333 mShowWallpaper = true; 334 } else { 335 backType = BackNavigationInfo.TYPE_CROSS_TASK; 336 } 337 } 338 } 339 infoBuilder.setType(backType); 340 341 ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Previous Destination is Activity:%s Task:%s " 342 + "removedContainer:%s, backType=%s", 343 prevActivity != null ? prevActivity.mActivityComponent : null, 344 prevTask != null ? prevTask.getName() : null, 345 removedWindowContainer, 346 BackNavigationInfo.typeToString(backType)); 347 348 // For now, we only animate when going home, cross task or cross-activity. 349 boolean prepareAnimation = 350 (backType == BackNavigationInfo.TYPE_RETURN_TO_HOME 351 || backType == BackNavigationInfo.TYPE_CROSS_TASK 352 || backType == BackNavigationInfo.TYPE_CROSS_ACTIVITY) 353 && adapter != null; 354 355 if (prepareAnimation) { 356 final AnimationHandler.ScheduleAnimationBuilder builder = 357 mAnimationHandler.prepareAnimation(backType, adapter, 358 currentTask, prevTask, currentActivity, prevActivity); 359 mBackAnimationInProgress = builder != null; 360 if (mBackAnimationInProgress) { 361 if (removedWindowContainer.hasCommittedReparentToAnimationLeash() 362 || removedWindowContainer.mTransitionController.inTransition() 363 || mWindowManagerService.mSyncEngine.hasPendingSyncSets()) { 364 ProtoLog.w(WM_DEBUG_BACK_PREVIEW, 365 "Pending back animation due to another animation is running"); 366 mPendingAnimationBuilder = builder; 367 // Current transition is still running, we have to defer the hiding to the 368 // client process to prevent the unexpected relayout when handling the back 369 // animation. 370 if (prevActivity != null) { 371 prevActivity.setDeferHidingClient(true); 372 } 373 } else { 374 scheduleAnimation(builder); 375 } 376 } 377 } 378 infoBuilder.setPrepareRemoteAnimation(prepareAnimation); 379 } // Release wm Lock 380 381 WindowContainer<?> finalRemovedWindowContainer = removedWindowContainer; 382 if (finalRemovedWindowContainer != null) { 383 final int finalBackType = backType; 384 RemoteCallback onBackNavigationDone = new RemoteCallback(result -> onBackNavigationDone( 385 result, finalBackType)); 386 infoBuilder.setOnBackNavigationDone(onBackNavigationDone); 387 } 388 mLastBackType = backType; 389 return infoBuilder.build(); 390 } 391 isMonitoringTransition()392 boolean isMonitoringTransition() { 393 return mAnimationHandler.mComposed || mNavigationMonitor.isMonitorForRemote(); 394 } 395 scheduleAnimation(@onNull AnimationHandler.ScheduleAnimationBuilder builder)396 private void scheduleAnimation(@NonNull AnimationHandler.ScheduleAnimationBuilder builder) { 397 mPendingAnimation = builder.build(); 398 mWindowManagerService.mWindowPlacerLocked.requestTraversal(); 399 if (mShowWallpaper) { 400 mWindowManagerService.getDefaultDisplayContentLocked().mWallpaperController 401 .adjustWallpaperWindows(); 402 } 403 } 404 isWaitBackTransition()405 private boolean isWaitBackTransition() { 406 return mAnimationHandler.mComposed && mAnimationHandler.mWaitTransition; 407 } 408 isKeyguardOccluded(WindowState focusWindow)409 boolean isKeyguardOccluded(WindowState focusWindow) { 410 final KeyguardController kc = mWindowManagerService.mAtmService.mKeyguardController; 411 final int displayId = focusWindow.getDisplayId(); 412 return kc.isKeyguardLocked(displayId) && kc.isDisplayOccluded(displayId); 413 } 414 415 /** 416 * There are two ways to customize activity exit animation, one is to provide the 417 * windowAnimationStyle by Activity#setTheme, another one is to set resId by 418 * Window#setWindowAnimations. 419 * Not all run-time customization methods can be checked from here, such as 420 * overridePendingTransition, which the animation resource will be set just before the 421 * transition is about to happen. 422 */ isCustomizeExitAnimation(WindowState window)423 private static boolean isCustomizeExitAnimation(WindowState window) { 424 // The default animation ResId is loaded from system package, so the result must match. 425 if (Objects.equals(window.mAttrs.packageName, "android")) { 426 return false; 427 } 428 if (window.mAttrs.windowAnimations != 0) { 429 final TransitionAnimation transitionAnimation = window.getDisplayContent() 430 .mAppTransition.mTransitionAnimation; 431 final int attr = com.android.internal.R.styleable 432 .WindowAnimation_activityCloseExitAnimation; 433 final int appResId = transitionAnimation.getAnimationResId( 434 window.mAttrs, attr, TRANSIT_OLD_NONE); 435 if (ResourceId.isValid(appResId)) { 436 if (sDefaultAnimationResId == 0) { 437 sDefaultAnimationResId = transitionAnimation.getDefaultAnimationResId(attr, 438 TRANSIT_OLD_NONE); 439 } 440 return sDefaultAnimationResId != appResId; 441 } 442 } 443 return false; 444 } 445 446 // For legacy transition. 447 /** 448 * Once we find the transition targets match back animation targets, remove the target from 449 * list, so that transition won't count them in since the close animation was finished. 450 * 451 * @return {@code true} if the participants of this transition was animated by back gesture 452 * animations, and shouldn't join next transition. 453 */ removeIfContainsBackAnimationTargets(ArraySet<ActivityRecord> openApps, ArraySet<ActivityRecord> closeApps)454 boolean removeIfContainsBackAnimationTargets(ArraySet<ActivityRecord> openApps, 455 ArraySet<ActivityRecord> closeApps) { 456 if (!isMonitoringTransition()) { 457 return false; 458 } 459 mTmpCloseApps.addAll(closeApps); 460 final boolean matchAnimationTargets = removeIfWaitForBackTransition(openApps, closeApps); 461 if (!matchAnimationTargets) { 462 mNavigationMonitor.onTransitionReadyWhileNavigate(mTmpOpenApps, mTmpCloseApps); 463 } 464 mTmpCloseApps.clear(); 465 return matchAnimationTargets; 466 } 467 removeIfWaitForBackTransition(ArraySet<ActivityRecord> openApps, ArraySet<ActivityRecord> closeApps)468 boolean removeIfWaitForBackTransition(ArraySet<ActivityRecord> openApps, 469 ArraySet<ActivityRecord> closeApps) { 470 if (!isWaitBackTransition()) { 471 return false; 472 } 473 // Note: TmpOpenApps is empty. Unlike shell transition, the open apps will be removed from 474 // mOpeningApps if there is no visibility change. 475 if (mAnimationHandler.containsBackAnimationTargets(mTmpOpenApps, mTmpCloseApps)) { 476 // remove close target from close list, open target from open list; 477 // but the open target can be in close list. 478 for (int i = openApps.size() - 1; i >= 0; --i) { 479 final ActivityRecord ar = openApps.valueAt(i); 480 if (mAnimationHandler.isTarget(ar, true /* open */)) { 481 openApps.removeAt(i); 482 mAnimationHandler.markStartingSurfaceMatch(); 483 } 484 } 485 for (int i = closeApps.size() - 1; i >= 0; --i) { 486 final ActivityRecord ar = closeApps.valueAt(i); 487 if (mAnimationHandler.isTarget(ar, false /* open */)) { 488 closeApps.removeAt(i); 489 } 490 } 491 return true; 492 } 493 return false; 494 } 495 496 private class NavigationMonitor { 497 // The window which triggering the back navigation. 498 private WindowState mNavigatingWindow; 499 private RemoteCallback mObserver; 500 startMonitor(@onNull WindowState window, @NonNull RemoteCallback observer)501 void startMonitor(@NonNull WindowState window, @NonNull RemoteCallback observer) { 502 mNavigatingWindow = window; 503 mObserver = observer; 504 } 505 stopMonitorForRemote()506 void stopMonitorForRemote() { 507 mObserver = null; 508 } 509 stopMonitorTransition()510 void stopMonitorTransition() { 511 mNavigatingWindow = null; 512 } 513 isMonitorForRemote()514 boolean isMonitorForRemote() { 515 return mNavigatingWindow != null && mObserver != null; 516 } 517 isMonitorAnimationOrTransition()518 boolean isMonitorAnimationOrTransition() { 519 return mNavigatingWindow != null 520 && (mAnimationHandler.mComposed || mAnimationHandler.mWaitTransition); 521 } 522 523 /** 524 * Notify focus window changed during back navigation. This will cancel the gesture for 525 * scenarios like: a system window popup, or when an activity add a new window. 526 * 527 * This method should only be used to check window-level change, otherwise it may cause 528 * misjudgment in multi-window mode. For example: in split-screen, when user is 529 * navigating on the top task, bottom task can start a new task, which will gain focus for 530 * a short time, but we should not cancel the navigation. 531 */ onFocusWindowChanged(WindowState newFocus)532 private void onFocusWindowChanged(WindowState newFocus) { 533 if (!atSameDisplay(newFocus) 534 || !(isMonitorForRemote() || isMonitorAnimationOrTransition())) { 535 return; 536 } 537 // Keep navigating if either new focus == navigating window or null. 538 if (newFocus != null && newFocus != mNavigatingWindow 539 && (newFocus.mActivityRecord == null 540 || (newFocus.mActivityRecord == mNavigatingWindow.mActivityRecord))) { 541 cancelBackNavigating("focusWindowChanged"); 542 } 543 } 544 545 /** 546 * Notify an unexpected transition has happened during back navigation. 547 */ onTransitionReadyWhileNavigate(ArrayList<WindowContainer> opening, ArrayList<WindowContainer> closing)548 private void onTransitionReadyWhileNavigate(ArrayList<WindowContainer> opening, 549 ArrayList<WindowContainer> closing) { 550 if (!isMonitorForRemote() && !isMonitorAnimationOrTransition()) { 551 return; 552 } 553 final ArrayList<WindowContainer> all = new ArrayList<>(opening); 554 all.addAll(closing); 555 for (int i = all.size() - 1; i >= 0; --i) { 556 if (all.get(i).hasChild(mNavigatingWindow)) { 557 cancelBackNavigating("transitionHappens"); 558 break; 559 } 560 } 561 } 562 atSameDisplay(WindowState newFocus)563 private boolean atSameDisplay(WindowState newFocus) { 564 if (mNavigatingWindow == null) { 565 return false; 566 } 567 final int navigatingDisplayId = mNavigatingWindow.getDisplayId(); 568 return newFocus == null || newFocus.getDisplayId() == navigatingDisplayId; 569 } 570 cancelBackNavigating(String reason)571 private void cancelBackNavigating(String reason) { 572 EventLogTags.writeWmBackNaviCanceled(reason); 573 if (isMonitorForRemote()) { 574 mObserver.sendResult(null /* result */); 575 } 576 if (isMonitorAnimationOrTransition()) { 577 clearBackAnimations(); 578 } 579 cancelPendingAnimation(); 580 } 581 } 582 583 // For shell transition 584 /** 585 * Check whether the transition targets was animated by back gesture animation. 586 * Because the opening target could request to do other stuff at onResume, so it could become 587 * close target for a transition. So the condition here is 588 * The closing target should only exist in close list, but the opening target can be either in 589 * open or close list. 590 */ onTransactionReady(Transition transition, ArrayList<Transition.ChangeInfo> targets, SurfaceControl.Transaction startTransaction)591 void onTransactionReady(Transition transition, ArrayList<Transition.ChangeInfo> targets, 592 SurfaceControl.Transaction startTransaction) { 593 if (!isMonitoringTransition() || targets.isEmpty()) { 594 return; 595 } 596 for (int i = targets.size() - 1; i >= 0; --i) { 597 final WindowContainer wc = targets.get(i).mContainer; 598 if (wc.asActivityRecord() == null && wc.asTask() == null 599 && wc.asTaskFragment() == null) { 600 continue; 601 } 602 // WC can be visible due to setLaunchBehind 603 if (wc.isVisibleRequested()) { 604 mTmpOpenApps.add(wc); 605 } else { 606 mTmpCloseApps.add(wc); 607 } 608 } 609 final boolean matchAnimationTargets = isWaitBackTransition() 610 && (transition.mType == TRANSIT_CLOSE || transition.mType == TRANSIT_TO_BACK) 611 && mAnimationHandler.containsBackAnimationTargets(mTmpOpenApps, mTmpCloseApps); 612 ProtoLog.d(WM_DEBUG_BACK_PREVIEW, 613 "onTransactionReady, opening: %s, closing: %s, animating: %s, match: %b", 614 mTmpOpenApps, mTmpCloseApps, mAnimationHandler, matchAnimationTargets); 615 if (!matchAnimationTargets) { 616 mNavigationMonitor.onTransitionReadyWhileNavigate(mTmpOpenApps, mTmpCloseApps); 617 } else { 618 if (mWaitTransitionFinish != null) { 619 Slog.e(TAG, "Gesture animation is applied on another transition?"); 620 } 621 mWaitTransitionFinish = transition; 622 // Flag target matches to defer remove the splash screen. 623 for (int i = mTmpOpenApps.size() - 1; i >= 0; --i) { 624 final WindowContainer wc = mTmpOpenApps.get(i); 625 if (mAnimationHandler.isTarget(wc, true /* open */)) { 626 mAnimationHandler.markStartingSurfaceMatch(); 627 break; 628 } 629 } 630 // Because the target will reparent to transition root, so it cannot be controlled by 631 // animation leash. Hide the close target when transition starts. 632 startTransaction.hide(mAnimationHandler.mCloseAdaptor.mTarget.getSurfaceControl()); 633 } 634 mTmpOpenApps.clear(); 635 mTmpCloseApps.clear(); 636 } 637 isMonitorTransitionTarget(WindowContainer wc)638 boolean isMonitorTransitionTarget(WindowContainer wc) { 639 if (!isWaitBackTransition() || mWaitTransitionFinish == null) { 640 return false; 641 } 642 return mAnimationHandler.isTarget(wc, wc.isVisibleRequested() /* open */); 643 } 644 645 /** 646 * Cleanup animation, this can either happen when legacy transition ready, or when the Shell 647 * transition finish. 648 */ clearBackAnimations()649 void clearBackAnimations() { 650 mAnimationHandler.clearBackAnimateTarget(); 651 mNavigationMonitor.stopMonitorTransition(); 652 mWaitTransitionFinish = null; 653 mBackAnimationRunning = false; 654 } 655 656 /** 657 * Called when a transition finished. 658 * Handle the pending animation when the running transition finished. 659 * @param targets The final animation targets derived in transition. 660 * @param finishedTransition The finished transition target. 661 */ onTransitionFinish(ArrayList<Transition.ChangeInfo> targets, @NonNull Transition finishedTransition)662 boolean onTransitionFinish(ArrayList<Transition.ChangeInfo> targets, 663 @NonNull Transition finishedTransition) { 664 if (finishedTransition == mWaitTransitionFinish) { 665 clearBackAnimations(); 666 } 667 668 if (!mBackAnimationInProgress || mPendingAnimationBuilder == null) { 669 return false; 670 } 671 ProtoLog.d(WM_DEBUG_BACK_PREVIEW, 672 "Handling the deferred animation after transition finished"); 673 674 // Find the participated container collected by transition when : 675 // Open transition -> the open target in back navigation, the close target in transition. 676 // Close transition -> the close target in back navigation, the open target in transition. 677 boolean hasTarget = false; 678 for (int i = 0; i < finishedTransition.mParticipants.size(); i++) { 679 final WindowContainer wc = finishedTransition.mParticipants.valueAt(i); 680 if (wc.asActivityRecord() == null && wc.asTask() == null 681 && wc.asTaskFragment() == null) { 682 continue; 683 } 684 685 if (mPendingAnimationBuilder.containTarget(wc)) { 686 hasTarget = true; 687 break; 688 } 689 } 690 691 if (!hasTarget) { 692 // Skip if no target participated in current finished transition. 693 Slog.w(TAG, "Finished transition didn't include the targets" 694 + " open: " + mPendingAnimationBuilder.mOpenTarget 695 + " close: " + mPendingAnimationBuilder.mCloseTarget); 696 cancelPendingAnimation(); 697 return false; 698 } 699 700 // Ensure the final animation targets which hidden by transition could be visible. 701 for (int i = 0; i < targets.size(); i++) { 702 final WindowContainer wc = targets.get(i).mContainer; 703 wc.prepareSurfaces(); 704 } 705 706 scheduleAnimation(mPendingAnimationBuilder); 707 mPendingAnimationBuilder = null; 708 return true; 709 } 710 cancelPendingAnimation()711 private void cancelPendingAnimation() { 712 if (mPendingAnimationBuilder == null) { 713 return; 714 } 715 try { 716 mPendingAnimationBuilder.mBackAnimationAdapter.getRunner().onAnimationCancelled(); 717 } catch (RemoteException e) { 718 Slog.e(TAG, "Remote animation gone", e); 719 } 720 mPendingAnimationBuilder = null; 721 } 722 723 /** 724 * Create and handling animations status for an open/close animation targets. 725 */ 726 static class AnimationHandler { 727 private final boolean mShowWindowlessSurface; 728 private final WindowManagerService mWindowManagerService; 729 private BackWindowAnimationAdaptor mCloseAdaptor; 730 private BackWindowAnimationAdaptor mOpenAdaptor; 731 private boolean mComposed; 732 private boolean mWaitTransition; 733 private int mSwitchType = UNKNOWN; 734 735 // This will be set before transition happen, to know whether the real opening target 736 // exactly match animating target. When target match, reparent the starting surface to 737 // the opening target like starting window do. 738 private boolean mStartingSurfaceTargetMatch; 739 private ActivityRecord mOpenActivity; 740 AnimationHandler(WindowManagerService wms)741 AnimationHandler(WindowManagerService wms) { 742 mWindowManagerService = wms; 743 final Context context = wms.mContext; 744 mShowWindowlessSurface = context.getResources().getBoolean( 745 com.android.internal.R.bool.config_predictShowStartingSurface); 746 } 747 private static final int UNKNOWN = 0; 748 private static final int TASK_SWITCH = 1; 749 private static final int ACTIVITY_SWITCH = 2; 750 isActivitySwitch(WindowContainer close, WindowContainer open)751 private static boolean isActivitySwitch(WindowContainer close, WindowContainer open) { 752 if (close.asActivityRecord() == null || open.asActivityRecord() == null 753 || (close.asActivityRecord().getTask() 754 != open.asActivityRecord().getTask())) { 755 return false; 756 } 757 return true; 758 } 759 isTaskSwitch(WindowContainer close, WindowContainer open)760 private static boolean isTaskSwitch(WindowContainer close, WindowContainer open) { 761 if (close.asTask() == null || open.asTask() == null 762 || (close.asTask() == open.asTask())) { 763 return false; 764 } 765 return true; 766 } 767 initiate(WindowContainer close, WindowContainer open, ActivityRecord openActivity)768 private void initiate(WindowContainer close, WindowContainer open, 769 ActivityRecord openActivity) { 770 WindowContainer closeTarget; 771 if (isActivitySwitch(close, open)) { 772 mSwitchType = ACTIVITY_SWITCH; 773 closeTarget = close.asActivityRecord(); 774 } else if (isTaskSwitch(close, open)) { 775 mSwitchType = TASK_SWITCH; 776 closeTarget = close.asTask().getTopNonFinishingActivity(); 777 } else { 778 mSwitchType = UNKNOWN; 779 return; 780 } 781 782 mCloseAdaptor = createAdaptor(closeTarget, false, mSwitchType); 783 mOpenAdaptor = createAdaptor(open, true, mSwitchType); 784 mOpenActivity = openActivity; 785 if (mCloseAdaptor.mAnimationTarget == null || mOpenAdaptor.mAnimationTarget == null) { 786 Slog.w(TAG, "composeNewAnimations fail, skip"); 787 clearBackAnimateTarget(); 788 } 789 } 790 composeAnimations(@onNull WindowContainer close, @NonNull WindowContainer open, ActivityRecord openActivity)791 private boolean composeAnimations(@NonNull WindowContainer close, 792 @NonNull WindowContainer open, ActivityRecord openActivity) { 793 if (mComposed || mWaitTransition) { 794 Slog.e(TAG, "Previous animation is running " + this); 795 return false; 796 } 797 clearBackAnimateTarget(); 798 if (close == null || open == null || openActivity == null) { 799 Slog.e(TAG, "reset animation with null target close: " 800 + close + " open: " + open); 801 return false; 802 } 803 initiate(close, open, openActivity); 804 if (mSwitchType == UNKNOWN) { 805 return false; 806 } 807 mComposed = true; 808 mWaitTransition = false; 809 return true; 810 } 811 getAnimationTargets()812 RemoteAnimationTarget[] getAnimationTargets() { 813 return mComposed ? new RemoteAnimationTarget[] { 814 mCloseAdaptor.mAnimationTarget, mOpenAdaptor.mAnimationTarget} : null; 815 } 816 isSupportWindowlessSurface()817 boolean isSupportWindowlessSurface() { 818 return mWindowManagerService.mAtmService.mTaskOrganizerController 819 .isSupportWindowlessStartingSurface(); 820 } 821 containTarget(ArrayList<WindowContainer> wcs, boolean open)822 boolean containTarget(ArrayList<WindowContainer> wcs, boolean open) { 823 for (int i = wcs.size() - 1; i >= 0; --i) { 824 if (isTarget(wcs.get(i), open)) { 825 return true; 826 } 827 } 828 return wcs.isEmpty(); 829 } 830 isTarget(WindowContainer wc, boolean open)831 boolean isTarget(WindowContainer wc, boolean open) { 832 if (!mComposed) { 833 return false; 834 } 835 836 // WC must be ActivityRecord in legacy transition, but it also can be Task or 837 // TaskFragment when using Shell transition. 838 // Open target: Can be Task or ActivityRecord or TaskFragment 839 // Close target: Limit to the top activity for now, to reduce the chance of misjudgment. 840 final WindowContainer target = open ? mOpenAdaptor.mTarget : mCloseAdaptor.mTarget; 841 if (mSwitchType == TASK_SWITCH) { 842 return wc == target 843 || (wc.asTask() != null && wc.hasChild(target)) 844 || (wc.asActivityRecord() != null && target.hasChild(wc)); 845 } else if (mSwitchType == ACTIVITY_SWITCH) { 846 return wc == target || (wc.asTaskFragment() != null && wc.hasChild(target)); 847 } 848 return false; 849 } 850 finishPresentAnimations()851 void finishPresentAnimations() { 852 if (!mComposed) { 853 return; 854 } 855 856 if (mCloseAdaptor != null) { 857 mCloseAdaptor.mTarget.cancelAnimation(); 858 mCloseAdaptor = null; 859 } 860 if (mOpenAdaptor != null) { 861 mOpenAdaptor.cleanUpWindowlessSurface(mStartingSurfaceTargetMatch); 862 mOpenAdaptor.mTarget.cancelAnimation(); 863 mOpenAdaptor = null; 864 } 865 if (mOpenActivity != null && mOpenActivity.mLaunchTaskBehind) { 866 restoreLaunchBehind(mOpenActivity); 867 } 868 } 869 markStartingSurfaceMatch()870 void markStartingSurfaceMatch() { 871 mStartingSurfaceTargetMatch = true; 872 mOpenAdaptor.reparentWindowlessSurfaceToTarget(); 873 } 874 clearBackAnimateTarget()875 void clearBackAnimateTarget() { 876 finishPresentAnimations(); 877 mComposed = false; 878 mWaitTransition = false; 879 mStartingSurfaceTargetMatch = false; 880 mSwitchType = UNKNOWN; 881 mOpenActivity = null; 882 } 883 884 // The close target must in close list 885 // The open target can either in close or open list containsBackAnimationTargets(ArrayList<WindowContainer> openApps, ArrayList<WindowContainer> closeApps)886 boolean containsBackAnimationTargets(ArrayList<WindowContainer> openApps, 887 ArrayList<WindowContainer> closeApps) { 888 return containTarget(closeApps, false /* open */) 889 && (containTarget(openApps, true /* open */) 890 || containTarget(openApps, false /* open */)); 891 } 892 893 @Override toString()894 public String toString() { 895 return "AnimationTargets{" 896 + " openTarget= " 897 + (mOpenAdaptor != null ? mOpenAdaptor.mTarget : "null") 898 + " closeTarget= " 899 + (mCloseAdaptor != null ? mCloseAdaptor.mTarget : "null") 900 + " mSwitchType= " 901 + mSwitchType 902 + " mComposed= " 903 + mComposed 904 + " mWaitTransition= " 905 + mWaitTransition 906 + '}'; 907 } 908 createAdaptor( WindowContainer target, boolean isOpen, int switchType)909 private static BackWindowAnimationAdaptor createAdaptor( 910 WindowContainer target, boolean isOpen, int switchType) { 911 final BackWindowAnimationAdaptor adaptor = 912 new BackWindowAnimationAdaptor(target, isOpen, switchType); 913 final SurfaceControl.Transaction pt = target.getPendingTransaction(); 914 target.startAnimation(pt, adaptor, false /* hidden */, ANIMATION_TYPE_PREDICT_BACK); 915 // Workaround to show TaskFragment which can be hide in Transitions and won't show 916 // during isAnimating. 917 if (isOpen && target.asActivityRecord() != null) { 918 final TaskFragment fragment = target.asActivityRecord().getTaskFragment(); 919 if (fragment != null) { 920 pt.show(fragment.mSurfaceControl); 921 } 922 } 923 return adaptor; 924 } 925 926 private static class BackWindowAnimationAdaptor implements AnimationAdapter { 927 SurfaceControl mCapturedLeash; 928 private final Rect mBounds = new Rect(); 929 private final WindowContainer mTarget; 930 private final boolean mIsOpen; 931 private RemoteAnimationTarget mAnimationTarget; 932 private final int mSwitchType; 933 934 // The starting surface task Id. Used to clear the starting surface if the animation has 935 // requested one during animating. 936 private int mRequestedStartingSurfaceId = INVALID_TASK_ID; 937 private SurfaceControl mStartingSurface; 938 BackWindowAnimationAdaptor(WindowContainer target, boolean isOpen, int switchType)939 BackWindowAnimationAdaptor(WindowContainer target, boolean isOpen, 940 int switchType) { 941 mBounds.set(target.getBounds()); 942 mTarget = target; 943 mIsOpen = isOpen; 944 mSwitchType = switchType; 945 } 946 @Override getShowWallpaper()947 public boolean getShowWallpaper() { 948 return false; 949 } 950 951 @Override startAnimation(SurfaceControl animationLeash, SurfaceControl.Transaction t, int type, SurfaceAnimator.OnAnimationFinishedCallback finishCallback)952 public void startAnimation(SurfaceControl animationLeash, SurfaceControl.Transaction t, 953 int type, SurfaceAnimator.OnAnimationFinishedCallback finishCallback) { 954 mCapturedLeash = animationLeash; 955 createRemoteAnimationTarget(mIsOpen); 956 } 957 958 @Override onAnimationCancelled(SurfaceControl animationLeash)959 public void onAnimationCancelled(SurfaceControl animationLeash) { 960 if (mCapturedLeash == animationLeash) { 961 mCapturedLeash = null; 962 mRequestedStartingSurfaceId = INVALID_TASK_ID; 963 mStartingSurface = null; 964 } 965 } 966 967 @Override getDurationHint()968 public long getDurationHint() { 969 return 0; 970 } 971 972 @Override getStatusBarTransitionsStartTime()973 public long getStatusBarTransitionsStartTime() { 974 return 0; 975 } 976 977 @Override dump(PrintWriter pw, String prefix)978 public void dump(PrintWriter pw, String prefix) { 979 pw.print(prefix + "BackWindowAnimationAdaptor mCapturedLeash="); 980 pw.print(mCapturedLeash); 981 pw.println(); 982 } 983 984 @Override dumpDebug(ProtoOutputStream proto)985 public void dumpDebug(ProtoOutputStream proto) { 986 987 } 988 createRemoteAnimationTarget(boolean isOpen)989 RemoteAnimationTarget createRemoteAnimationTarget(boolean isOpen) { 990 if (mAnimationTarget != null) { 991 return mAnimationTarget; 992 } 993 Task t = mTarget.asTask(); 994 ActivityRecord r = null; 995 if (t == null && mTarget.asTaskFragment() != null) { 996 t = mTarget.asTaskFragment().getTask(); 997 r = mTarget.asTaskFragment().getTopNonFinishingActivity(); 998 } 999 if (r == null) { 1000 r = t != null ? t.getTopNonFinishingActivity() 1001 : mTarget.asActivityRecord(); 1002 } 1003 if (t == null && r != null) { 1004 t = r.getTask(); 1005 } 1006 if (t == null || r == null) { 1007 Slog.e(TAG, "createRemoteAnimationTarget fail " + mTarget); 1008 return null; 1009 } 1010 final WindowState mainWindow = r.findMainWindow(); 1011 Rect insets; 1012 if (mainWindow != null) { 1013 insets = mainWindow.getInsetsStateWithVisibilityOverride().calculateInsets( 1014 mBounds, WindowInsets.Type.systemBars(), 1015 false /* ignoreVisibility */).toRect(); 1016 InsetUtils.addInsets(insets, mainWindow.mActivityRecord.getLetterboxInsets()); 1017 } else { 1018 insets = new Rect(); 1019 } 1020 final int mode = isOpen ? MODE_OPENING : MODE_CLOSING; 1021 mAnimationTarget = new RemoteAnimationTarget(t.mTaskId, mode, mCapturedLeash, 1022 !r.fillsParent(), new Rect(), 1023 insets, r.getPrefixOrderIndex(), new Point(mBounds.left, mBounds.top), 1024 mBounds, mBounds, t.getWindowConfiguration(), 1025 true /* isNotInRecents */, null, null, t.getTaskInfo(), 1026 r.checkEnterPictureInPictureAppOpsState()); 1027 return mAnimationTarget; 1028 } 1029 createStartingSurface()1030 void createStartingSurface() { 1031 if (!mIsOpen) { 1032 return; 1033 } 1034 final Task openTask = mSwitchType == TASK_SWITCH 1035 ? mTarget.asTask() : mSwitchType == ACTIVITY_SWITCH 1036 ? mTarget.asActivityRecord().getTask() : null; 1037 if (openTask == null) { 1038 return; 1039 } 1040 final ActivityRecord mainActivity = mSwitchType == ACTIVITY_SWITCH 1041 ? mTarget.asActivityRecord() 1042 : openTask.getTopNonFinishingActivity(); 1043 if (mainActivity == null) { 1044 return; 1045 } 1046 final TaskSnapshot snapshot = getSnapshot(mTarget); 1047 mRequestedStartingSurfaceId = openTask.mAtmService.mTaskOrganizerController 1048 .addWindowlessStartingSurface(openTask, mainActivity, 1049 mAnimationTarget.leash, snapshot, 1050 new IWindowlessStartingSurfaceCallback.Stub() { 1051 // Once the starting surface has been created in shell, it will call 1052 // onSurfaceAdded to pass the created surface to core, so if a 1053 // transition is triggered by the back gesture, there doesn't need to 1054 // create another starting surface for the opening target, just reparent 1055 // the starting surface to the opening target. 1056 // Note if cleanUpWindowlessSurface happen prior than onSurfaceAdded 1057 // called, there won't be able to reparent the starting surface on 1058 // opening target. But if that happens and transition target is matched, 1059 // the app window should already draw. 1060 @Override 1061 public void onSurfaceAdded(SurfaceControl sc) { 1062 synchronized (mTarget.mWmService.mGlobalLock) { 1063 if (mRequestedStartingSurfaceId != INVALID_TASK_ID) { 1064 mStartingSurface = sc; 1065 } 1066 } 1067 } 1068 }); 1069 } 1070 1071 // When back gesture has triggered and transition target matches navigation target, 1072 // reparent the starting surface to the opening target as it's starting window. reparentWindowlessSurfaceToTarget()1073 void reparentWindowlessSurfaceToTarget() { 1074 if (mRequestedStartingSurfaceId == INVALID_TASK_ID) { 1075 return; 1076 } 1077 // If open target matches, reparent to open activity or task 1078 if (mStartingSurface != null && mStartingSurface.isValid()) { 1079 mTarget.getPendingTransaction() 1080 .reparent(mStartingSurface, mTarget.getSurfaceControl()); 1081 // remove starting surface. 1082 mStartingSurface = null; 1083 } 1084 } 1085 1086 /** 1087 * Ask shell to clear the starting surface. 1088 * @param openTransitionMatch if true, shell will play the remove starting window 1089 * animation, otherwise remove it directly. 1090 */ cleanUpWindowlessSurface(boolean openTransitionMatch)1091 void cleanUpWindowlessSurface(boolean openTransitionMatch) { 1092 if (mRequestedStartingSurfaceId == INVALID_TASK_ID) { 1093 return; 1094 } 1095 mTarget.mWmService.mAtmService.mTaskOrganizerController 1096 .removeWindowlessStartingSurface(mRequestedStartingSurfaceId, 1097 !openTransitionMatch); 1098 mRequestedStartingSurfaceId = INVALID_TASK_ID; 1099 } 1100 } 1101 prepareAnimation(int backType, BackAnimationAdapter adapter, Task currentTask, Task previousTask, ActivityRecord currentActivity, ActivityRecord previousActivity)1102 ScheduleAnimationBuilder prepareAnimation(int backType, BackAnimationAdapter adapter, 1103 Task currentTask, Task previousTask, ActivityRecord currentActivity, 1104 ActivityRecord previousActivity) { 1105 switch (backType) { 1106 case BackNavigationInfo.TYPE_RETURN_TO_HOME: 1107 return new ScheduleAnimationBuilder(backType, adapter) 1108 .setIsLaunchBehind(true) 1109 .setComposeTarget(currentTask, previousTask); 1110 case BackNavigationInfo.TYPE_CROSS_ACTIVITY: 1111 return new ScheduleAnimationBuilder(backType, adapter) 1112 .setComposeTarget(currentActivity, previousActivity) 1113 .setIsLaunchBehind(false); 1114 case BackNavigationInfo.TYPE_CROSS_TASK: 1115 return new ScheduleAnimationBuilder(backType, adapter) 1116 .setComposeTarget(currentTask, previousTask) 1117 .setIsLaunchBehind(false); 1118 } 1119 return null; 1120 } 1121 1122 class ScheduleAnimationBuilder { 1123 final int mType; 1124 final BackAnimationAdapter mBackAnimationAdapter; 1125 WindowContainer mCloseTarget; 1126 WindowContainer mOpenTarget; 1127 boolean mIsLaunchBehind; 1128 ScheduleAnimationBuilder(int type, BackAnimationAdapter backAnimationAdapter)1129 ScheduleAnimationBuilder(int type, BackAnimationAdapter backAnimationAdapter) { 1130 mType = type; 1131 mBackAnimationAdapter = backAnimationAdapter; 1132 } 1133 setComposeTarget(WindowContainer close, WindowContainer open)1134 ScheduleAnimationBuilder setComposeTarget(WindowContainer close, WindowContainer open) { 1135 mCloseTarget = close; 1136 mOpenTarget = open; 1137 return this; 1138 } 1139 setIsLaunchBehind(boolean launchBehind)1140 ScheduleAnimationBuilder setIsLaunchBehind(boolean launchBehind) { 1141 mIsLaunchBehind = launchBehind; 1142 return this; 1143 } 1144 containTarget(@onNull WindowContainer wc)1145 boolean containTarget(@NonNull WindowContainer wc) { 1146 return wc == mOpenTarget || wc == mCloseTarget 1147 || mOpenTarget.hasChild(wc) || mCloseTarget.hasChild(wc); 1148 } 1149 1150 /** 1151 * Apply preview strategy on the opening target 1152 * @param openAnimationAdaptor The animator who can create starting surface. 1153 * @param visibleOpenActivity The visible activity in opening target. 1154 */ applyPreviewStrategy(BackWindowAnimationAdaptor openAnimationAdaptor, ActivityRecord visibleOpenActivity)1155 private void applyPreviewStrategy(BackWindowAnimationAdaptor openAnimationAdaptor, 1156 ActivityRecord visibleOpenActivity) { 1157 if (isSupportWindowlessSurface() && mShowWindowlessSurface && !mIsLaunchBehind) { 1158 openAnimationAdaptor.createStartingSurface(); 1159 return; 1160 } 1161 setLaunchBehind(visibleOpenActivity); 1162 } 1163 build()1164 Runnable build() { 1165 if (mOpenTarget == null || mCloseTarget == null) { 1166 return null; 1167 } 1168 final ActivityRecord openActivity = mOpenTarget.asTask() != null 1169 ? mOpenTarget.asTask().getTopNonFinishingActivity() 1170 : mOpenTarget.asActivityRecord() != null 1171 ? mOpenTarget.asActivityRecord() : null; 1172 if (openActivity == null) { 1173 Slog.e(TAG, "No opening activity"); 1174 return null; 1175 } 1176 1177 if (!composeAnimations(mCloseTarget, mOpenTarget, openActivity)) { 1178 return null; 1179 } 1180 mCloseTarget.mTransitionController.mSnapshotController 1181 .mActivitySnapshotController.clearOnBackPressedActivities(); 1182 applyPreviewStrategy(mOpenAdaptor, openActivity); 1183 1184 final IBackAnimationFinishedCallback callback = makeAnimationFinishedCallback(); 1185 final RemoteAnimationTarget[] targets = getAnimationTargets(); 1186 1187 return () -> { 1188 try { 1189 mBackAnimationAdapter.getRunner().onAnimationStart( 1190 targets, null, null, callback); 1191 } catch (RemoteException e) { 1192 e.printStackTrace(); 1193 } 1194 }; 1195 } 1196 makeAnimationFinishedCallback()1197 private IBackAnimationFinishedCallback makeAnimationFinishedCallback() { 1198 return new IBackAnimationFinishedCallback.Stub() { 1199 @Override 1200 public void onAnimationFinished(boolean triggerBack) { 1201 synchronized (mWindowManagerService.mGlobalLock) { 1202 if (!mComposed) { 1203 // animation was canceled 1204 return; 1205 } 1206 if (!triggerBack) { 1207 clearBackAnimateTarget(); 1208 } else { 1209 mWaitTransition = true; 1210 } 1211 } 1212 // TODO Add timeout monitor if transition didn't happen 1213 } 1214 }; 1215 } 1216 } 1217 } 1218 1219 private static void setLaunchBehind(@NonNull ActivityRecord activity) { 1220 if (!activity.isVisibleRequested()) { 1221 activity.setVisibility(true); 1222 // The transition could commit the visibility and in the finishing state, that could 1223 // skip commitVisibility call in setVisibility cause the activity won't visible here. 1224 // Call it again to make sure the activity could be visible while handling the pending 1225 // animation. 1226 activity.commitVisibility(true, true); 1227 activity.mTransitionController.mSnapshotController 1228 .mActivitySnapshotController.addOnBackPressedActivity(activity); 1229 } 1230 activity.mLaunchTaskBehind = true; 1231 1232 // Handle fixed rotation launching app. 1233 final DisplayContent dc = activity.mDisplayContent; 1234 dc.rotateInDifferentOrientationIfNeeded(activity); 1235 if (activity.hasFixedRotationTransform()) { 1236 // Set the record so we can recognize it to continue to update display 1237 // orientation if the previous activity becomes the top later. 1238 dc.setFixedRotationLaunchingApp(activity, 1239 activity.getWindowConfiguration().getRotation()); 1240 } 1241 1242 ProtoLog.d(WM_DEBUG_BACK_PREVIEW, 1243 "Setting Activity.mLauncherTaskBehind to true. Activity=%s", activity); 1244 activity.mTaskSupervisor.mStoppingActivities.remove(activity); 1245 activity.getDisplayContent().ensureActivitiesVisible(null /* starting */, 1246 0 /* configChanges */, false /* preserveWindows */, true); 1247 } 1248 1249 private static void restoreLaunchBehind(@NonNull ActivityRecord activity) { 1250 activity.mDisplayContent.continueUpdateOrientationForDiffOrienLaunchingApp(); 1251 1252 // Restore the launch-behind state. 1253 activity.mTaskSupervisor.scheduleLaunchTaskBehindComplete(activity.token); 1254 activity.mLaunchTaskBehind = false; 1255 // Ignore all change 1256 activity.mTransitionController.mSnapshotController 1257 .mActivitySnapshotController.clearOnBackPressedActivities(); 1258 ProtoLog.d(WM_DEBUG_BACK_PREVIEW, 1259 "Setting Activity.mLauncherTaskBehind to false. Activity=%s", 1260 activity); 1261 } 1262 1263 void checkAnimationReady(WallpaperController wallpaperController) { 1264 if (!mBackAnimationInProgress) { 1265 return; 1266 } 1267 1268 final boolean wallpaperReady = !mShowWallpaper 1269 || (wallpaperController.getWallpaperTarget() != null 1270 && wallpaperController.wallpaperTransitionReady()); 1271 if (wallpaperReady && mPendingAnimation != null) { 1272 startAnimation(); 1273 } 1274 } 1275 1276 void startAnimation() { 1277 if (!mBackAnimationInProgress) { 1278 // gesture is already finished, do not start animation 1279 if (mPendingAnimation != null) { 1280 clearBackAnimations(); 1281 mPendingAnimation = null; 1282 } 1283 return; 1284 } 1285 if (mPendingAnimation != null) { 1286 mPendingAnimation.run(); 1287 mPendingAnimation = null; 1288 mBackAnimationRunning = true; 1289 } 1290 } 1291 1292 private void onBackNavigationDone(Bundle result, int backType) { 1293 boolean triggerBack = result != null && result.getBoolean( 1294 BackNavigationInfo.KEY_TRIGGER_BACK); 1295 ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "onBackNavigationDone backType=%s, " 1296 + "triggerBack=%b", backType, triggerBack); 1297 1298 mNavigationMonitor.stopMonitorForRemote(); 1299 mBackAnimationInProgress = false; 1300 mShowWallpaper = false; 1301 mPendingAnimationBuilder = null; 1302 } 1303 1304 static TaskSnapshot getSnapshot(@NonNull WindowContainer w) { 1305 if (w.asTask() != null) { 1306 final Task task = w.asTask(); 1307 return task.mRootWindowContainer.mWindowManager.mTaskSnapshotController.getSnapshot( 1308 task.mTaskId, task.mUserId, false /* restoreFromDisk */, 1309 false /* isLowResolution */); 1310 } 1311 1312 if (w.asActivityRecord() != null) { 1313 final ActivityRecord ar = w.asActivityRecord(); 1314 return ar.mWmService.mSnapshotController.mActivitySnapshotController.getSnapshot(ar); 1315 } 1316 return null; 1317 } 1318 1319 void setWindowManager(WindowManagerService wm) { 1320 mWindowManagerService = wm; 1321 mAnimationHandler = new AnimationHandler(wm); 1322 } 1323 1324 boolean isWallpaperVisible(WindowState w) { 1325 return mAnimationHandler.mComposed && mShowWallpaper 1326 && w.mAttrs.type == TYPE_BASE_APPLICATION && w.mActivityRecord != null 1327 && mAnimationHandler.isTarget(w.mActivityRecord, true /* open */); 1328 } 1329 1330 // Called from WindowManagerService to write to a protocol buffer output stream. 1331 void dumpDebug(ProtoOutputStream proto, long fieldId) { 1332 final long token = proto.start(fieldId); 1333 proto.write(ANIMATION_IN_PROGRESS, mBackAnimationInProgress); 1334 proto.write(LAST_BACK_TYPE, mLastBackType); 1335 proto.write(SHOW_WALLPAPER, mShowWallpaper); 1336 if (mAnimationHandler.mOpenActivity != null) { 1337 mAnimationHandler.mOpenActivity.writeNameToProto(proto, MAIN_OPEN_ACTIVITY); 1338 } else { 1339 proto.write(MAIN_OPEN_ACTIVITY, ""); 1340 } 1341 proto.write(ANIMATION_RUNNING, mBackAnimationRunning); 1342 proto.end(token); 1343 } 1344 } 1345