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.PRIVATE_FLAG_APP_PROGRESS_GENERATION_ALLOWED; 23 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; 24 import static android.view.WindowManager.TRANSIT_CHANGE; 25 import static android.view.WindowManager.TRANSIT_CLOSE; 26 import static android.view.WindowManager.TRANSIT_OLD_NONE; 27 import static android.view.WindowManager.TRANSIT_TO_BACK; 28 29 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_BACK_PREVIEW; 30 import static com.android.server.wm.BackNavigationProto.ANIMATION_IN_PROGRESS; 31 import static com.android.server.wm.BackNavigationProto.ANIMATION_RUNNING; 32 import static com.android.server.wm.BackNavigationProto.LAST_BACK_TYPE; 33 import static com.android.server.wm.BackNavigationProto.MAIN_OPEN_ACTIVITY; 34 import static com.android.server.wm.BackNavigationProto.SHOW_WALLPAPER; 35 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_PREDICT_BACK; 36 37 import android.annotation.NonNull; 38 import android.annotation.Nullable; 39 import android.content.Context; 40 import android.content.res.Configuration; 41 import android.content.res.ResourceId; 42 import android.graphics.Point; 43 import android.graphics.Rect; 44 import android.os.Bundle; 45 import android.os.RemoteCallback; 46 import android.os.RemoteException; 47 import android.os.SystemProperties; 48 import android.text.TextUtils; 49 import android.util.ArraySet; 50 import android.util.Slog; 51 import android.util.proto.ProtoOutputStream; 52 import android.view.RemoteAnimationTarget; 53 import android.view.SurfaceControl; 54 import android.view.WindowInsets; 55 import android.window.BackAnimationAdapter; 56 import android.window.BackNavigationInfo; 57 import android.window.IBackAnimationFinishedCallback; 58 import android.window.IWindowlessStartingSurfaceCallback; 59 import android.window.OnBackInvokedCallbackInfo; 60 import android.window.TaskSnapshot; 61 62 import com.android.internal.annotations.VisibleForTesting; 63 import com.android.internal.policy.TransitionAnimation; 64 import com.android.internal.protolog.common.ProtoLog; 65 import com.android.window.flags.Flags; 66 67 import java.io.PrintWriter; 68 import java.util.ArrayList; 69 import java.util.Arrays; 70 import java.util.Objects; 71 72 /** 73 * Controller to handle actions related to the back gesture on the server side. 74 */ 75 class BackNavigationController { 76 private static final String TAG = "CoreBackPreview"; 77 private WindowManagerService mWindowManagerService; 78 private boolean mBackAnimationInProgress; 79 private @BackNavigationInfo.BackTargetType int mLastBackType; 80 private boolean mShowWallpaper; 81 private Runnable mPendingAnimation; 82 private final NavigationMonitor mNavigationMonitor = new NavigationMonitor(); 83 84 private AnimationHandler mAnimationHandler; 85 86 /** 87 * The transition who match the back navigation targets, 88 * release animation after this transition finish. 89 */ 90 private Transition mWaitTransitionFinish; 91 private final ArrayList<WindowContainer> mTmpOpenApps = new ArrayList<>(); 92 private final ArrayList<WindowContainer> mTmpCloseApps = new ArrayList<>(); 93 94 // This will be set if the back navigation is in progress and the current transition is still 95 // running. The pending animation builder will do the animation stuff includes creating leashes, 96 // re-parenting leashes and set launch behind, etc. Will be handled when transition finished. 97 private AnimationHandler.ScheduleAnimationBuilder mPendingAnimationBuilder; 98 99 private static int sDefaultAnimationResId; 100 101 /** 102 * true if the back predictability feature is enabled 103 */ 104 static final boolean sPredictBackEnable = 105 SystemProperties.getBoolean("persist.wm.debug.predictive_back", true); 106 107 // Notify focus window changed onFocusChanged(WindowState newFocus)108 void onFocusChanged(WindowState newFocus) { 109 mNavigationMonitor.onFocusWindowChanged(newFocus); 110 } 111 112 /** 113 * Set up the necessary leashes and build a {@link BackNavigationInfo} instance for an upcoming 114 * back gesture animation. 115 * 116 * @return a {@link BackNavigationInfo} instance containing the required leashes and metadata 117 * for the animation, or null if we don't know how to animate the current window and need to 118 * fallback on dispatching the key event. 119 */ 120 @VisibleForTesting 121 @Nullable startBackNavigation(@onNull RemoteCallback navigationObserver, BackAnimationAdapter adapter)122 BackNavigationInfo startBackNavigation(@NonNull RemoteCallback navigationObserver, 123 BackAnimationAdapter adapter) { 124 if (!sPredictBackEnable) { 125 return null; 126 } 127 final WindowManagerService wmService = mWindowManagerService; 128 129 int backType = BackNavigationInfo.TYPE_UNDEFINED; 130 131 // The currently visible activity (if any). 132 ActivityRecord currentActivity = null; 133 134 // The currently visible task (if any). 135 Task currentTask = null; 136 137 // The previous task we're going back to. Can be the same as currentTask, if there are 138 // multiple Activities in the Stack. 139 Task prevTask = null; 140 141 WindowContainer<?> removedWindowContainer = null; 142 WindowState window; 143 144 BackNavigationInfo.Builder infoBuilder = new BackNavigationInfo.Builder(); 145 synchronized (wmService.mGlobalLock) { 146 if (isMonitoringTransition()) { 147 Slog.w(TAG, "Previous animation hasn't finish, status: " + mAnimationHandler); 148 // Don't start any animation for it. 149 return null; 150 } 151 152 window = wmService.getFocusedWindowLocked(); 153 154 if (window == null) { 155 // We don't have any focused window, fallback ont the top currentTask of the focused 156 // display. 157 ProtoLog.w(WM_DEBUG_BACK_PREVIEW, 158 "No focused window, defaulting to top current task's window"); 159 currentTask = wmService.mAtmService.getTopDisplayFocusedRootTask(); 160 window = currentTask != null 161 ? currentTask.getWindow(WindowState::isFocused) : null; 162 } 163 164 if (window == null) { 165 Slog.e(TAG, "Window is null, returning null."); 166 return null; 167 } 168 169 // Move focus to the top embedded window if possible 170 if (mWindowManagerService.moveFocusToAdjacentEmbeddedWindow(window)) { 171 window = wmService.getFocusedWindowLocked(); 172 if (window == null) { 173 Slog.e(TAG, "New focused window is null, returning null."); 174 return null; 175 } 176 } 177 178 if (!window.isDrawn()) { 179 ProtoLog.d(WM_DEBUG_BACK_PREVIEW, 180 "Focused window didn't have a valid surface drawn."); 181 return null; 182 } 183 184 currentActivity = window.mActivityRecord; 185 currentTask = window.getTask(); 186 if ((currentTask != null && !currentTask.isVisibleRequested()) 187 || (currentActivity != null && !currentActivity.isVisibleRequested())) { 188 // Closing transition is happening on focus window and should be update soon, 189 // don't drive back navigation with it. 190 ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Focus window is closing."); 191 return null; 192 } 193 // Now let's find if this window has a callback from the client side. 194 final OnBackInvokedCallbackInfo callbackInfo = window.getOnBackInvokedCallbackInfo(); 195 if (callbackInfo == null) { 196 Slog.e(TAG, "No callback registered, returning null."); 197 return null; 198 } 199 if (!callbackInfo.isSystemCallback()) { 200 backType = BackNavigationInfo.TYPE_CALLBACK; 201 } 202 infoBuilder.setOnBackInvokedCallback(callbackInfo.getCallback()); 203 infoBuilder.setAnimationCallback(callbackInfo.isAnimationCallback()); 204 infoBuilder.setTouchableRegion(window.getFrame()); 205 infoBuilder.setAppProgressAllowed((window.getAttrs().privateFlags 206 & PRIVATE_FLAG_APP_PROGRESS_GENERATION_ALLOWED) != 0); 207 mNavigationMonitor.startMonitor(window, navigationObserver); 208 209 ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "startBackNavigation currentTask=%s, " 210 + "topRunningActivity=%s, callbackInfo=%s, currentFocus=%s", 211 currentTask, currentActivity, callbackInfo, window); 212 213 // If we don't need to set up the animation, we return early. This is the case when 214 // - We have an application callback. 215 // - We don't have any ActivityRecord or Task to animate. 216 // - The IME is opened, and we just need to close it. 217 // - The home activity is the focused activity & it's not TYPE_BASE_APPLICATION 218 // - The current activity will do shared element transition when exiting. 219 if (backType == BackNavigationInfo.TYPE_CALLBACK 220 || currentActivity == null 221 || currentTask == null 222 || (currentActivity.isActivityTypeHome() 223 && window.mAttrs.type == TYPE_BASE_APPLICATION) 224 || currentActivity.mHasSceneTransition) { 225 infoBuilder.setType(BackNavigationInfo.TYPE_CALLBACK); 226 infoBuilder.setOnBackNavigationDone(new RemoteCallback(result -> 227 onBackNavigationDone(result, BackNavigationInfo.TYPE_CALLBACK))); 228 mLastBackType = BackNavigationInfo.TYPE_CALLBACK; 229 return infoBuilder.build(); 230 } 231 232 // The previous activity we're going back to. This can be either a child of currentTask 233 // if there are more than one Activity in currentTask, or a child of prevTask, if 234 // currentActivity is the last child of currentTask. 235 // We don't have an application callback, let's find the destination of the back gesture 236 // The search logic should align with ActivityClientController#finishActivity 237 final ArrayList<ActivityRecord> prevActivities = new ArrayList<>(); 238 final boolean canAnimate = getAnimatablePrevActivities(currentTask, currentActivity, 239 prevActivities); 240 final boolean isOccluded = isKeyguardOccluded(window); 241 if (!canAnimate) { 242 backType = BackNavigationInfo.TYPE_CALLBACK; 243 } else if ((window.getParent().getChildCount() > 1 244 && window.getParent().getChildAt(0) != window)) { 245 // TODO Dialog window does not need to attach on activity, check 246 // window.mAttrs.type != TYPE_BASE_APPLICATION 247 // Are we the top window of our parent? If not, we are a window on top of the 248 // activity, we won't close the activity. 249 backType = BackNavigationInfo.TYPE_DIALOG_CLOSE; 250 removedWindowContainer = window; 251 } else if (hasTranslucentActivity(currentActivity, prevActivities)) { 252 // skip if one of participant activity is translucent 253 backType = BackNavigationInfo.TYPE_CALLBACK; 254 } else if (prevActivities.size() > 0) { 255 if ((!isOccluded || isAllActivitiesCanShowWhenLocked(prevActivities)) 256 && isAllActivitiesCreated(prevActivities)) { 257 // We have another Activity in the same currentTask to go to 258 final WindowContainer parent = currentActivity.getParent(); 259 final boolean canCustomize = parent != null 260 && (parent.asTask() != null 261 || (parent.asTaskFragment() != null 262 && parent.canCustomizeAppTransition())); 263 if (canCustomize) { 264 if (isCustomizeExitAnimation(window)) { 265 infoBuilder.setWindowAnimations( 266 window.mAttrs.packageName, window.mAttrs.windowAnimations); 267 } 268 final ActivityRecord.CustomAppTransition customAppTransition = 269 currentActivity.getCustomAnimation(false/* open */); 270 if (customAppTransition != null) { 271 infoBuilder.setCustomAnimation(currentActivity.packageName, 272 customAppTransition.mEnterAnim, 273 customAppTransition.mExitAnim, 274 customAppTransition.mBackgroundColor); 275 } 276 } 277 infoBuilder.setLetterboxColor(currentActivity.mLetterboxUiController 278 .getLetterboxBackgroundColor().toArgb()); 279 removedWindowContainer = currentActivity; 280 prevTask = prevActivities.get(0).getTask(); 281 backType = BackNavigationInfo.TYPE_CROSS_ACTIVITY; 282 } else { 283 // keyguard locked and activities are unable to show when locked. 284 backType = BackNavigationInfo.TYPE_CALLBACK; 285 } 286 } else if (currentTask.mAtmService.getLockTaskController().isTaskLocked(currentTask)) { 287 // Do not predict if current task is in task locked. 288 backType = BackNavigationInfo.TYPE_CALLBACK; 289 } else { 290 // Check back-to-home or cross-task 291 prevTask = currentTask.mRootWindowContainer.getTask(t -> { 292 if (t.showToCurrentUser() && !t.mChildren.isEmpty()) { 293 final ActivityRecord ar = t.getTopNonFinishingActivity(); 294 return ar != null && ar.showToCurrentUser(); 295 } 296 return false; 297 }, currentTask, false /*includeBoundary*/, true /*traverseTopToBottom*/); 298 final ActivityRecord tmpPre = prevTask != null 299 ? prevTask.getTopNonFinishingActivity() : null; 300 if (tmpPre != null) { 301 prevActivities.add(tmpPre); 302 findAdjacentActivityIfExist(tmpPre, prevActivities); 303 } 304 if (prevTask == null || prevActivities.isEmpty() 305 || (isOccluded && !isAllActivitiesCanShowWhenLocked(prevActivities))) { 306 backType = BackNavigationInfo.TYPE_CALLBACK; 307 } else if (prevTask.isActivityTypeHome()) { 308 removedWindowContainer = currentTask; 309 backType = BackNavigationInfo.TYPE_RETURN_TO_HOME; 310 final ActivityRecord ar = prevTask.getTopNonFinishingActivity(); 311 mShowWallpaper = ar != null && ar.hasWallpaper(); 312 } else { 313 // If it reaches the top activity, we will check the below task from parent. 314 // If it's null or multi-window and has different parent task, fallback the type 315 // to TYPE_CALLBACK. Or set the type to proper value when it's return to home or 316 // another task. 317 final Task prevParent = prevTask.getParent().asTask(); 318 final Task currParent = currentTask.getParent().asTask(); 319 if ((prevTask.inMultiWindowMode() && prevParent != currParent) 320 // Do not animate to translucent task, it could be trampoline. 321 || hasTranslucentActivity(currentActivity, prevActivities)) { 322 backType = BackNavigationInfo.TYPE_CALLBACK; 323 } else { 324 removedWindowContainer = prevTask; 325 backType = BackNavigationInfo.TYPE_CROSS_TASK; 326 } 327 } 328 } 329 infoBuilder.setType(backType); 330 331 ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Previous Destination is Activity:%s Task:%s " 332 + "removedContainer:%s, backType=%s", 333 prevActivities.size() > 0 ? TextUtils.join(";", prevActivities.stream() 334 .map(r -> r.mActivityComponent).toArray()) : null, 335 prevTask != null ? prevTask.getName() : null, 336 removedWindowContainer, 337 BackNavigationInfo.typeToString(backType)); 338 339 // For now, we only animate when going home, cross task or cross-activity. 340 boolean prepareAnimation = 341 (backType == BackNavigationInfo.TYPE_RETURN_TO_HOME 342 || backType == BackNavigationInfo.TYPE_CROSS_TASK 343 || backType == BackNavigationInfo.TYPE_CROSS_ACTIVITY 344 || backType == BackNavigationInfo.TYPE_DIALOG_CLOSE) 345 && adapter != null; 346 347 if (prepareAnimation) { 348 final AnimationHandler.ScheduleAnimationBuilder builder = 349 mAnimationHandler.prepareAnimation( 350 backType, 351 adapter, 352 mNavigationMonitor, 353 currentTask, 354 prevTask, 355 currentActivity, 356 prevActivities, 357 removedWindowContainer); 358 mBackAnimationInProgress = builder != null; 359 if (mBackAnimationInProgress) { 360 if (removedWindowContainer.hasCommittedReparentToAnimationLeash() 361 || removedWindowContainer.mTransitionController.inTransition() 362 || mWindowManagerService.mSyncEngine.hasPendingSyncSets()) { 363 ProtoLog.w(WM_DEBUG_BACK_PREVIEW, 364 "Pending back animation due to another animation is running"); 365 mPendingAnimationBuilder = builder; 366 // Current transition is still running, we have to defer the hiding to the 367 // client process to prevent the unexpected relayout when handling the back 368 // animation. 369 for (int i = prevActivities.size() - 1; i >= 0; --i) { 370 prevActivities.get(i).setDeferHidingClient(true); 371 } 372 } else { 373 scheduleAnimation(builder); 374 } 375 } 376 } 377 infoBuilder.setPrepareRemoteAnimation(prepareAnimation); 378 379 if (removedWindowContainer != null) { 380 final int finalBackType = backType; 381 final RemoteCallback onBackNavigationDone = new RemoteCallback(result -> 382 onBackNavigationDone(result, finalBackType)); 383 infoBuilder.setOnBackNavigationDone(onBackNavigationDone); 384 } else { 385 mNavigationMonitor.stopMonitorForRemote(); 386 } 387 mLastBackType = backType; 388 return infoBuilder.build(); 389 } 390 } 391 392 /** 393 * Gets previous activities from currentActivity. 394 * 395 * @return false if unable to predict what will happen 396 */ 397 @VisibleForTesting getAnimatablePrevActivities(@onNull Task currentTask, @NonNull ActivityRecord currentActivity, @NonNull ArrayList<ActivityRecord> outPrevActivities)398 static boolean getAnimatablePrevActivities(@NonNull Task currentTask, 399 @NonNull ActivityRecord currentActivity, 400 @NonNull ArrayList<ActivityRecord> outPrevActivities) { 401 if (currentActivity.mAtmService 402 .mTaskOrganizerController.shouldInterceptBackPressedOnRootTask( 403 currentTask.getRootTask())) { 404 // The task organizer will handle back pressed, don't play animation. 405 return false; 406 } 407 final ActivityRecord root = currentTask.getRootActivity(false /*ignoreRelinquishIdentity*/, 408 true /*setToBottomIfNone*/); 409 if (root != null && ActivityClientController.shouldMoveTaskToBack(currentActivity, root)) { 410 return true; 411 } 412 413 // Searching previous 414 final ActivityRecord prevActivity = currentTask.getActivity((below) -> !below.finishing, 415 currentActivity, false /*includeBoundary*/, true /*traverseTopToBottom*/); 416 final TaskFragment currTF = currentActivity.getTaskFragment(); 417 if (currTF != null && currTF.asTask() == null) { 418 // The currentActivity is embedded, search for the candidate previous activities. 419 if (prevActivity != null && currTF.hasChild(prevActivity)) { 420 // PrevActivity is under the same task fragment, that's it. 421 outPrevActivities.add(prevActivity); 422 return true; 423 } 424 if (currTF.getAdjacentTaskFragment() == null) { 425 final TaskFragment nextTF = findNextTaskFragment(currentTask, currTF); 426 if (isSecondCompanionToFirst(currTF, nextTF)) { 427 // TF is isStacked, search bottom activity from companion TF. 428 // 429 // Sample hierarchy: search for underPrevious if any. 430 // Current TF 431 // Companion TF (bottomActivityInCompanion) 432 // Bottom Activity not inside companion TF (underPrevious) 433 // find bottom activity in Companion TF. 434 final ActivityRecord bottomActivityInCompanion = nextTF.getActivity( 435 (below) -> !below.finishing, false /* traverseTopToBottom */); 436 final ActivityRecord underPrevious = currentTask.getActivity( 437 (below) -> !below.finishing, bottomActivityInCompanion, 438 false /*includeBoundary*/, true /*traverseTopToBottom*/); 439 if (underPrevious != null) { 440 outPrevActivities.add(underPrevious); 441 addPreviousAdjacentActivityIfExist(underPrevious, outPrevActivities); 442 } 443 return true; 444 } 445 } else { 446 // If adjacent TF has companion to current TF, those two TF will be closed together. 447 final TaskFragment adjacentTF = currTF.getAdjacentTaskFragment(); 448 if (isSecondCompanionToFirst(currTF, adjacentTF)) { 449 // The two TFs are adjacent (visually displayed side-by-side), search if any 450 // activity below the lowest one. 451 final WindowContainer commonParent = currTF.getParent(); 452 final TaskFragment lowerTF = commonParent.mChildren.indexOf(currTF) 453 < commonParent.mChildren.indexOf(adjacentTF) 454 ? currTF : adjacentTF; 455 final ActivityRecord lowerActivity = lowerTF.getTopNonFinishingActivity(); 456 // TODO (b/274997067) close currTF + companionTF, open next activities if any. 457 // Allow to predict next task if no more activity in task. Or return previous 458 // activities for cross-activity animation. 459 return currentTask.getActivity((below) -> !below.finishing, lowerActivity, 460 false /*includeBoundary*/, true /*traverseTopToBottom*/) == null; 461 } 462 // Unable to predict if no companion, it can only close current activity and make 463 // prev Activity full screened. 464 return false; 465 } 466 } 467 468 if (prevActivity == null) { 469 // No previous activity in this Task nor TaskFragment, it can still predict if previous 470 // task exists. 471 return true; 472 } 473 // Add possible adjacent activity if prevActivity is embedded 474 addPreviousAdjacentActivityIfExist(prevActivity, outPrevActivities); 475 outPrevActivities.add(prevActivity); 476 return true; 477 } 478 findNextTaskFragment(@onNull Task currentTask, @NonNull TaskFragment topTF)479 private static TaskFragment findNextTaskFragment(@NonNull Task currentTask, 480 @NonNull TaskFragment topTF) { 481 final int topIndex = currentTask.mChildren.indexOf(topTF); 482 if (topIndex <= 0) { 483 return null; 484 } 485 final WindowContainer next = currentTask.mChildren.get(topIndex - 1); 486 return next.asTaskFragment(); 487 } 488 489 /** 490 * Whether the second TF has set companion to first TF. 491 * When set, the second TF will be removed by organizer if the first TF is removed. 492 */ isSecondCompanionToFirst(TaskFragment first, TaskFragment second)493 private static boolean isSecondCompanionToFirst(TaskFragment first, TaskFragment second) { 494 return second != null && second.getCompanionTaskFragment() == first; 495 } 496 addPreviousAdjacentActivityIfExist(@onNull ActivityRecord prevActivity, @NonNull ArrayList<ActivityRecord> outPrevActivities)497 private static void addPreviousAdjacentActivityIfExist(@NonNull ActivityRecord prevActivity, 498 @NonNull ArrayList<ActivityRecord> outPrevActivities) { 499 final TaskFragment prevTF = prevActivity.getTaskFragment(); 500 if (prevTF == null || prevTF.asTask() != null) { 501 return; 502 } 503 504 final TaskFragment prevTFAdjacent = prevTF.getAdjacentTaskFragment(); 505 if (prevTFAdjacent == null || prevTFAdjacent.asTask() != null) { 506 return; 507 } 508 final ActivityRecord prevActivityAdjacent = 509 prevTFAdjacent.getTopNonFinishingActivity(); 510 if (prevActivityAdjacent != null) { 511 outPrevActivities.add(prevActivityAdjacent); 512 } 513 } 514 findAdjacentActivityIfExist(@onNull ActivityRecord mainActivity, @NonNull ArrayList<ActivityRecord> outList)515 private static void findAdjacentActivityIfExist(@NonNull ActivityRecord mainActivity, 516 @NonNull ArrayList<ActivityRecord> outList) { 517 final TaskFragment mainTF = mainActivity.getTaskFragment(); 518 if (mainTF == null || mainTF.getAdjacentTaskFragment() == null) { 519 return; 520 } 521 final TaskFragment adjacentTF = mainTF.getAdjacentTaskFragment(); 522 final ActivityRecord topActivity = adjacentTF.getTopNonFinishingActivity(); 523 if (topActivity == null) { 524 return; 525 } 526 outList.add(topActivity); 527 } 528 hasTranslucentActivity(@onNull ActivityRecord currentActivity, @NonNull ArrayList<ActivityRecord> prevActivities)529 private static boolean hasTranslucentActivity(@NonNull ActivityRecord currentActivity, 530 @NonNull ArrayList<ActivityRecord> prevActivities) { 531 if (!currentActivity.occludesParent() || currentActivity.showWallpaper()) { 532 return true; 533 } 534 for (int i = prevActivities.size() - 1; i >= 0; --i) { 535 final ActivityRecord test = prevActivities.get(i); 536 if (!test.occludesParent() || test.hasWallpaper()) { 537 return true; 538 } 539 } 540 return false; 541 } 542 isAllActivitiesCanShowWhenLocked( @onNull ArrayList<ActivityRecord> prevActivities)543 private static boolean isAllActivitiesCanShowWhenLocked( 544 @NonNull ArrayList<ActivityRecord> prevActivities) { 545 for (int i = prevActivities.size() - 1; i >= 0; --i) { 546 if (!prevActivities.get(i).canShowWhenLocked()) { 547 return false; 548 } 549 } 550 return !prevActivities.isEmpty(); 551 } 552 isAllActivitiesCreated( @onNull ArrayList<ActivityRecord> prevActivities)553 private static boolean isAllActivitiesCreated( 554 @NonNull ArrayList<ActivityRecord> prevActivities) { 555 for (int i = prevActivities.size() - 1; i >= 0; --i) { 556 final ActivityRecord check = prevActivities.get(i); 557 if (check.isState(ActivityRecord.State.INITIALIZING)) { 558 return false; 559 } 560 } 561 return !prevActivities.isEmpty(); 562 } 563 isMonitoringTransition()564 boolean isMonitoringTransition() { 565 return mAnimationHandler.mComposed || mNavigationMonitor.isMonitorForRemote(); 566 } 567 scheduleAnimation(@onNull AnimationHandler.ScheduleAnimationBuilder builder)568 private void scheduleAnimation(@NonNull AnimationHandler.ScheduleAnimationBuilder builder) { 569 mPendingAnimation = builder.build(); 570 mWindowManagerService.mWindowPlacerLocked.requestTraversal(); 571 if (mShowWallpaper) { 572 mWindowManagerService.getDefaultDisplayContentLocked().mWallpaperController 573 .adjustWallpaperWindows(); 574 } 575 } 576 isWaitBackTransition()577 private boolean isWaitBackTransition() { 578 return mAnimationHandler.mComposed && mAnimationHandler.mWaitTransition; 579 } 580 isKeyguardOccluded(WindowState focusWindow)581 boolean isKeyguardOccluded(WindowState focusWindow) { 582 final KeyguardController kc = mWindowManagerService.mAtmService.mKeyguardController; 583 final int displayId = focusWindow.getDisplayId(); 584 return kc.isKeyguardOccluded(displayId); 585 } 586 587 /** 588 * There are two ways to customize activity exit animation, one is to provide the 589 * windowAnimationStyle by Activity#setTheme, another one is to set resId by 590 * Window#setWindowAnimations. 591 * Not all run-time customization methods can be checked from here, such as 592 * overridePendingTransition, which the animation resource will be set just before the 593 * transition is about to happen. 594 */ isCustomizeExitAnimation(WindowState window)595 private static boolean isCustomizeExitAnimation(WindowState window) { 596 // The default animation ResId is loaded from system package, so the result must match. 597 if (Objects.equals(window.mAttrs.packageName, "android")) { 598 return false; 599 } 600 if (window.mAttrs.windowAnimations != 0) { 601 final TransitionAnimation transitionAnimation = window.getDisplayContent() 602 .mAppTransition.mTransitionAnimation; 603 final int attr = com.android.internal.R.styleable 604 .WindowAnimation_activityCloseExitAnimation; 605 final int appResId = transitionAnimation.getAnimationResId( 606 window.mAttrs, attr, TRANSIT_OLD_NONE); 607 if (ResourceId.isValid(appResId)) { 608 if (sDefaultAnimationResId == 0) { 609 sDefaultAnimationResId = transitionAnimation.getDefaultAnimationResId(attr, 610 TRANSIT_OLD_NONE); 611 } 612 return sDefaultAnimationResId != appResId; 613 } 614 } 615 return false; 616 } 617 618 // For legacy transition. 619 /** 620 * Once we find the transition targets match back animation targets, remove the target from 621 * list, so that transition won't count them in since the close animation was finished. 622 * 623 * @return {@code true} if the participants of this transition was animated by back gesture 624 * animations, and shouldn't join next transition. 625 */ removeIfContainsBackAnimationTargets(ArraySet<ActivityRecord> openApps, ArraySet<ActivityRecord> closeApps)626 boolean removeIfContainsBackAnimationTargets(ArraySet<ActivityRecord> openApps, 627 ArraySet<ActivityRecord> closeApps) { 628 if (!isMonitoringTransition()) { 629 return false; 630 } 631 mTmpCloseApps.addAll(closeApps); 632 final boolean matchAnimationTargets = removeIfWaitForBackTransition(openApps, closeApps); 633 if (!matchAnimationTargets) { 634 mNavigationMonitor.onTransitionReadyWhileNavigate(mTmpOpenApps, mTmpCloseApps); 635 } 636 mTmpCloseApps.clear(); 637 return matchAnimationTargets; 638 } 639 removeIfWaitForBackTransition(ArraySet<ActivityRecord> openApps, ArraySet<ActivityRecord> closeApps)640 boolean removeIfWaitForBackTransition(ArraySet<ActivityRecord> openApps, 641 ArraySet<ActivityRecord> closeApps) { 642 if (!isWaitBackTransition()) { 643 return false; 644 } 645 // Note: TmpOpenApps is empty. Unlike shell transition, the open apps will be removed from 646 // mOpeningApps if there is no visibility change. 647 if (mAnimationHandler.containsBackAnimationTargets(mTmpOpenApps, mTmpCloseApps)) { 648 // remove close target from close list, open target from open list; 649 // but the open target can be in close list. 650 for (int i = openApps.size() - 1; i >= 0; --i) { 651 final ActivityRecord ar = openApps.valueAt(i); 652 if (mAnimationHandler.isTarget(ar, true /* open */)) { 653 openApps.removeAt(i); 654 mAnimationHandler.markStartingSurfaceMatch(null /* reparentTransaction */); 655 } 656 } 657 for (int i = closeApps.size() - 1; i >= 0; --i) { 658 final ActivityRecord ar = closeApps.valueAt(i); 659 if (mAnimationHandler.isTarget(ar, false /* open */)) { 660 closeApps.removeAt(i); 661 } 662 } 663 return true; 664 } 665 return false; 666 } 667 removePredictiveSurfaceIfNeeded(ActivityRecord openActivity)668 void removePredictiveSurfaceIfNeeded(ActivityRecord openActivity) { 669 mAnimationHandler.markWindowHasDrawn(openActivity); 670 } 671 672 @VisibleForTesting 673 class NavigationMonitor { 674 // The window which triggering the back navigation. 675 private WindowState mNavigatingWindow; 676 private RemoteCallback mObserver; 677 startMonitor(@onNull WindowState window, @NonNull RemoteCallback observer)678 void startMonitor(@NonNull WindowState window, @NonNull RemoteCallback observer) { 679 mNavigatingWindow = window; 680 mObserver = observer; 681 } 682 stopMonitorForRemote()683 void stopMonitorForRemote() { 684 mObserver = null; 685 } 686 stopMonitorTransition()687 void stopMonitorTransition() { 688 mNavigatingWindow = null; 689 } 690 isMonitorForRemote()691 boolean isMonitorForRemote() { 692 return mNavigatingWindow != null && mObserver != null; 693 } 694 isMonitorAnimationOrTransition()695 boolean isMonitorAnimationOrTransition() { 696 return mNavigatingWindow != null 697 && (mAnimationHandler.mComposed || mAnimationHandler.mWaitTransition); 698 } 699 700 /** 701 * Notify focus window changed during back navigation. This will cancel the gesture for 702 * scenarios like: a system window popup, or when an activity add a new window. 703 * 704 * This method should only be used to check window-level change, otherwise it may cause 705 * misjudgment in multi-window mode. For example: in split-screen, when user is 706 * navigating on the top task, bottom task can start a new task, which will gain focus for 707 * a short time, but we should not cancel the navigation. 708 */ onFocusWindowChanged(WindowState newFocus)709 private void onFocusWindowChanged(WindowState newFocus) { 710 if (!atSameDisplay(newFocus) 711 || !(isMonitorForRemote() || isMonitorAnimationOrTransition())) { 712 return; 713 } 714 // Keep navigating if either new focus == navigating window or null. 715 if (newFocus != null && newFocus != mNavigatingWindow 716 && (newFocus.mActivityRecord == null 717 || (newFocus.mActivityRecord == mNavigatingWindow.mActivityRecord))) { 718 cancelBackNavigating("focusWindowChanged"); 719 } 720 } 721 722 /** 723 * Notify an unexpected transition has happened during back navigation. 724 */ onTransitionReadyWhileNavigate(ArrayList<WindowContainer> opening, ArrayList<WindowContainer> closing)725 private void onTransitionReadyWhileNavigate(ArrayList<WindowContainer> opening, 726 ArrayList<WindowContainer> closing) { 727 if (!isMonitorForRemote() && !isMonitorAnimationOrTransition()) { 728 return; 729 } 730 final ArrayList<WindowContainer> all = new ArrayList<>(opening); 731 all.addAll(closing); 732 for (int i = all.size() - 1; i >= 0; --i) { 733 if (all.get(i).hasChild(mNavigatingWindow)) { 734 cancelBackNavigating("transitionHappens"); 735 break; 736 } 737 } 738 } 739 atSameDisplay(WindowState newFocus)740 private boolean atSameDisplay(WindowState newFocus) { 741 if (mNavigatingWindow == null) { 742 return false; 743 } 744 final int navigatingDisplayId = mNavigatingWindow.getDisplayId(); 745 return newFocus == null || newFocus.getDisplayId() == navigatingDisplayId; 746 } 747 cancelBackNavigating(String reason)748 private void cancelBackNavigating(String reason) { 749 EventLogTags.writeWmBackNaviCanceled(reason); 750 if (isMonitorForRemote()) { 751 mObserver.sendResult(null /* result */); 752 } 753 if (isMonitorAnimationOrTransition()) { 754 clearBackAnimations(true /* cancel */); 755 } 756 cancelPendingAnimation(); 757 } 758 } 759 760 // For shell transition 761 /** 762 * Check whether the transition targets was animated by back gesture animation. 763 * Because the opening target could request to do other stuff at onResume, so it could become 764 * close target for a transition. So the condition here is 765 * The closing target should only exist in close list, but the opening target can be either in 766 * open or close list. 767 */ onTransactionReady(Transition transition, ArrayList<Transition.ChangeInfo> targets, SurfaceControl.Transaction startTransaction)768 void onTransactionReady(Transition transition, ArrayList<Transition.ChangeInfo> targets, 769 SurfaceControl.Transaction startTransaction) { 770 if (!isMonitoringTransition() || targets.isEmpty()) { 771 return; 772 } 773 if (mAnimationHandler.hasTargetDetached()) { 774 mNavigationMonitor.cancelBackNavigating("targetDetached"); 775 return; 776 } 777 for (int i = targets.size() - 1; i >= 0; --i) { 778 final WindowContainer wc = targets.get(i).mContainer; 779 if (wc.asActivityRecord() == null && wc.asTask() == null 780 && wc.asTaskFragment() == null) { 781 continue; 782 } 783 // Only care if visibility changed. 784 if (targets.get(i).getTransitMode(wc) == TRANSIT_CHANGE) { 785 continue; 786 } 787 // WC can be visible due to setLaunchBehind 788 if (wc.isVisibleRequested()) { 789 mTmpOpenApps.add(wc); 790 } else { 791 mTmpCloseApps.add(wc); 792 } 793 } 794 final boolean matchAnimationTargets = isWaitBackTransition() 795 && (transition.mType == TRANSIT_CLOSE || transition.mType == TRANSIT_TO_BACK) 796 && mAnimationHandler.containsBackAnimationTargets(mTmpOpenApps, mTmpCloseApps); 797 ProtoLog.d(WM_DEBUG_BACK_PREVIEW, 798 "onTransactionReady, opening: %s, closing: %s, animating: %s, match: %b", 799 mTmpOpenApps, mTmpCloseApps, mAnimationHandler, matchAnimationTargets); 800 if (!matchAnimationTargets) { 801 mNavigationMonitor.onTransitionReadyWhileNavigate(mTmpOpenApps, mTmpCloseApps); 802 } else { 803 if (mWaitTransitionFinish != null) { 804 Slog.e(TAG, "Gesture animation is applied on another transition?"); 805 } 806 mWaitTransitionFinish = transition; 807 // Flag target matches to defer remove the splash screen. 808 for (int i = mTmpOpenApps.size() - 1; i >= 0; --i) { 809 final WindowContainer wc = mTmpOpenApps.get(i); 810 if (mAnimationHandler.isTarget(wc, true /* open */)) { 811 mAnimationHandler.markStartingSurfaceMatch(startTransaction); 812 break; 813 } 814 } 815 // release animation leash 816 if (mAnimationHandler.mOpenAnimAdaptor.mCloseTransaction != null) { 817 startTransaction.merge(mAnimationHandler.mOpenAnimAdaptor.mCloseTransaction); 818 mAnimationHandler.mOpenAnimAdaptor.mCloseTransaction = null; 819 } 820 // Because the target will reparent to transition root, so it cannot be controlled by 821 // animation leash. Hide the close target when transition starts. 822 startTransaction.hide(mAnimationHandler.mCloseAdaptor.mTarget.getSurfaceControl()); 823 } 824 mTmpOpenApps.clear(); 825 mTmpCloseApps.clear(); 826 } 827 isMonitorTransitionTarget(WindowContainer wc)828 boolean isMonitorTransitionTarget(WindowContainer wc) { 829 if (!isWaitBackTransition() || mWaitTransitionFinish == null) { 830 return false; 831 } 832 return mAnimationHandler.isTarget(wc, wc.isVisibleRequested() /* open */); 833 } 834 shouldPauseTouch(WindowContainer wc)835 boolean shouldPauseTouch(WindowContainer wc) { 836 // Once the close transition is ready, it means the onBackInvoked callback has invoked, and 837 // app is ready to trigger next transition, no matter what it will be. 838 return mAnimationHandler.mComposed && mWaitTransitionFinish == null 839 && mAnimationHandler.isTarget(wc, wc.isVisibleRequested() /* open */); 840 } 841 842 /** 843 * Cleanup animation, this can either happen when legacy transition ready, or when the Shell 844 * transition finish. 845 */ clearBackAnimations(boolean cancel)846 void clearBackAnimations(boolean cancel) { 847 mAnimationHandler.clearBackAnimateTarget(cancel); 848 mNavigationMonitor.stopMonitorTransition(); 849 mWaitTransitionFinish = null; 850 } 851 852 /** 853 * Handle the pending animation when the running transition finished, all the visibility change 854 * has applied so ready to start pending predictive back animation. 855 * @param targets The final animation targets derived in transition. 856 * @param finishedTransition The finished transition target. 857 */ onTransitionFinish(ArrayList<Transition.ChangeInfo> targets, @NonNull Transition finishedTransition)858 void onTransitionFinish(ArrayList<Transition.ChangeInfo> targets, 859 @NonNull Transition finishedTransition) { 860 if (finishedTransition == mWaitTransitionFinish) { 861 clearBackAnimations(false /* cancel */); 862 } 863 if (!mBackAnimationInProgress || mPendingAnimationBuilder == null) { 864 return; 865 } 866 ProtoLog.d(WM_DEBUG_BACK_PREVIEW, 867 "Handling the deferred animation after transition finished"); 868 869 // Find the participated container collected by transition when : 870 // Open transition -> the open target in back navigation, the close target in transition. 871 // Close transition -> the close target in back navigation, the open target in transition. 872 boolean hasTarget = false; 873 for (int i = 0; i < finishedTransition.mParticipants.size(); i++) { 874 final WindowContainer wc = finishedTransition.mParticipants.valueAt(i); 875 if (wc.asActivityRecord() == null && wc.asTask() == null 876 && wc.asTaskFragment() == null) { 877 continue; 878 } 879 880 if (mPendingAnimationBuilder.containTarget(wc)) { 881 hasTarget = true; 882 break; 883 } 884 } 885 886 if (!hasTarget) { 887 // Skip if no target participated in current finished transition. 888 Slog.w(TAG, "Finished transition didn't include the targets" 889 + " open: " + Arrays.toString(mPendingAnimationBuilder.mOpenTargets) 890 + " close: " + mPendingAnimationBuilder.mCloseTarget); 891 cancelPendingAnimation(); 892 return; 893 } 894 895 // Ensure the final animation targets which hidden by transition could be visible. 896 for (int i = 0; i < targets.size(); i++) { 897 final WindowContainer wc = targets.get(i).mContainer; 898 wc.prepareSurfaces(); 899 } 900 901 // The pending builder could be cleared due to prepareSurfaces 902 // => updateNonSystemOverlayWindowsVisibilityIfNeeded 903 // => setForceHideNonSystemOverlayWindowIfNeeded 904 // => updateFocusedWindowLocked => onFocusWindowChanged. 905 if (mPendingAnimationBuilder != null) { 906 scheduleAnimation(mPendingAnimationBuilder); 907 mPendingAnimationBuilder = null; 908 } 909 } 910 cancelPendingAnimation()911 private void cancelPendingAnimation() { 912 if (mPendingAnimationBuilder == null) { 913 return; 914 } 915 try { 916 mPendingAnimationBuilder.mBackAnimationAdapter.getRunner().onAnimationCancelled(); 917 } catch (RemoteException e) { 918 Slog.e(TAG, "Remote animation gone", e); 919 } 920 mPendingAnimationBuilder = null; 921 } 922 923 /** 924 * Create and handling animations status for an open/close animation targets. 925 */ 926 static class AnimationHandler { 927 private final boolean mShowWindowlessSurface; 928 private final WindowManagerService mWindowManagerService; 929 private BackWindowAnimationAdaptor mCloseAdaptor; 930 private BackWindowAnimationAdaptorWrapper mOpenAnimAdaptor; 931 private boolean mComposed; 932 private boolean mWaitTransition; 933 private int mSwitchType = UNKNOWN; 934 935 // This will be set before transition happen, to know whether the real opening target 936 // exactly match animating target. When target match, reparent the starting surface to 937 // the opening target like starting window do. 938 private boolean mStartingSurfaceTargetMatch; 939 private ActivityRecord[] mOpenActivities; 940 AnimationHandler(WindowManagerService wms)941 AnimationHandler(WindowManagerService wms) { 942 mWindowManagerService = wms; 943 final Context context = wms.mContext; 944 mShowWindowlessSurface = context.getResources().getBoolean( 945 com.android.internal.R.bool.config_predictShowStartingSurface) 946 && Flags.activitySnapshotByDefault(); 947 } 948 private static final int UNKNOWN = 0; 949 private static final int TASK_SWITCH = 1; 950 private static final int ACTIVITY_SWITCH = 2; 951 private static final int DIALOG_CLOSE = 3; 952 isActivitySwitch(@onNull WindowContainer close, @NonNull WindowContainer[] open)953 private static boolean isActivitySwitch(@NonNull WindowContainer close, 954 @NonNull WindowContainer[] open) { 955 if (open == null || open.length == 0 || close.asActivityRecord() == null) { 956 return false; 957 } 958 final Task closeTask = close.asActivityRecord().getTask(); 959 for (int i = open.length - 1; i >= 0; --i) { 960 if (open[i].asActivityRecord() == null 961 || (closeTask != open[i].asActivityRecord().getTask())) { 962 return false; 963 } 964 } 965 return true; 966 } 967 isTaskSwitch(@onNull WindowContainer close, @NonNull WindowContainer[] open)968 private static boolean isTaskSwitch(@NonNull WindowContainer close, 969 @NonNull WindowContainer[] open) { 970 if (open == null || open.length != 1 || close.asTask() == null) { 971 return false; 972 } 973 return open[0].asTask() != null && (close.asTask() != open[0].asTask()); 974 } 975 isDialogClose(WindowContainer close)976 private static boolean isDialogClose(WindowContainer close) { 977 return close.asWindowState() != null; 978 } 979 initiate(@onNull WindowContainer close, @NonNull WindowContainer[] open, @NonNull ActivityRecord[] openingActivities)980 private void initiate(@NonNull WindowContainer close, @NonNull WindowContainer[] open, 981 @NonNull ActivityRecord[] openingActivities) { 982 if (isActivitySwitch(close, open)) { 983 mSwitchType = ACTIVITY_SWITCH; 984 } else if (isTaskSwitch(close, open)) { 985 mSwitchType = TASK_SWITCH; 986 } else if (isDialogClose(close)) { 987 mSwitchType = DIALOG_CLOSE; 988 } else { 989 mSwitchType = UNKNOWN; 990 return; 991 } 992 993 mCloseAdaptor = createAdaptor(close, false, mSwitchType); 994 if (mCloseAdaptor.mAnimationTarget == null) { 995 Slog.w(TAG, "composeNewAnimations fail, skip"); 996 clearBackAnimateTarget(true /* cancel */); 997 return; 998 } 999 1000 // Start fixed rotation for previous activity before create animation. 1001 if (openingActivities.length == 1) { 1002 final ActivityRecord next = openingActivities[0]; 1003 final DisplayContent dc = next.mDisplayContent; 1004 dc.rotateInDifferentOrientationIfNeeded(next); 1005 if (next.hasFixedRotationTransform()) { 1006 // Set the record so we can recognize it to continue to update display 1007 // orientation if the previous activity becomes the top later. 1008 dc.setFixedRotationLaunchingApp(next, 1009 next.getWindowConfiguration().getRotation()); 1010 } 1011 } 1012 mOpenAnimAdaptor = new BackWindowAnimationAdaptorWrapper(true, mSwitchType, open); 1013 if (!mOpenAnimAdaptor.isValid()) { 1014 Slog.w(TAG, "compose animations fail, skip"); 1015 clearBackAnimateTarget(true /* cancel */); 1016 return; 1017 } 1018 mOpenActivities = openingActivities; 1019 } 1020 composeAnimations(@onNull WindowContainer close, @NonNull WindowContainer[] open, @NonNull ActivityRecord[] openingActivities)1021 private boolean composeAnimations(@NonNull WindowContainer close, 1022 @NonNull WindowContainer[] open, @NonNull ActivityRecord[] openingActivities) { 1023 if (mComposed || mWaitTransition) { 1024 Slog.e(TAG, "Previous animation is running " + this); 1025 return false; 1026 } 1027 clearBackAnimateTarget(true /* cancel */); 1028 if (close == null || open == null || open.length == 0 || open.length > 2) { 1029 Slog.e(TAG, "reset animation with null target close: " 1030 + close + " open: " + Arrays.toString(open)); 1031 return false; 1032 } 1033 initiate(close, open, openingActivities); 1034 if (mSwitchType == UNKNOWN) { 1035 return false; 1036 } 1037 mComposed = true; 1038 mWaitTransition = false; 1039 return true; 1040 } 1041 getAnimationTargets()1042 @Nullable RemoteAnimationTarget[] getAnimationTargets() { 1043 if (!mComposed) { 1044 return null; 1045 } 1046 final RemoteAnimationTarget[] targets = new RemoteAnimationTarget[2]; 1047 targets[0] = mCloseAdaptor.mAnimationTarget; 1048 targets[1] = mOpenAnimAdaptor.mRemoteAnimationTarget; 1049 return targets; 1050 } 1051 isSupportWindowlessSurface()1052 boolean isSupportWindowlessSurface() { 1053 return mWindowManagerService.mAtmService.mTaskOrganizerController 1054 .isSupportWindowlessStartingSurface(); 1055 } 1056 containTarget(@onNull ArrayList<WindowContainer> wcs, boolean open)1057 boolean containTarget(@NonNull ArrayList<WindowContainer> wcs, boolean open) { 1058 for (int i = wcs.size() - 1; i >= 0; --i) { 1059 if (isTarget(wcs.get(i), open)) { 1060 return true; 1061 } 1062 } 1063 return wcs.isEmpty(); 1064 } 1065 isTarget(@onNull WindowContainer wc, boolean open)1066 boolean isTarget(@NonNull WindowContainer wc, boolean open) { 1067 if (!mComposed) { 1068 return false; 1069 } 1070 if (open) { 1071 for (int i = mOpenAnimAdaptor.mAdaptors.length - 1; i >= 0; --i) { 1072 if (isAnimateTarget(wc, mOpenAnimAdaptor.mAdaptors[i].mTarget, mSwitchType)) { 1073 return true; 1074 } 1075 } 1076 return false; 1077 } 1078 return isAnimateTarget(wc, mCloseAdaptor.mTarget, mSwitchType); 1079 } 1080 markWindowHasDrawn(ActivityRecord activity)1081 void markWindowHasDrawn(ActivityRecord activity) { 1082 if (!mComposed || mWaitTransition) { 1083 return; 1084 } 1085 boolean allWindowDrawn = true; 1086 for (int i = mOpenAnimAdaptor.mAdaptors.length - 1; i >= 0; --i) { 1087 final BackWindowAnimationAdaptor next = mOpenAnimAdaptor.mAdaptors[i]; 1088 if (isAnimateTarget(activity, next.mTarget, mSwitchType)) { 1089 next.mAppWindowDrawn = true; 1090 } 1091 allWindowDrawn &= next.mAppWindowDrawn; 1092 } 1093 if (allWindowDrawn) { 1094 mOpenAnimAdaptor.cleanUpWindowlessSurface(true); 1095 } 1096 } 1097 isAnimateTarget(@onNull WindowContainer window, @NonNull WindowContainer animationTarget, int switchType)1098 private static boolean isAnimateTarget(@NonNull WindowContainer window, 1099 @NonNull WindowContainer animationTarget, int switchType) { 1100 if (switchType == TASK_SWITCH) { 1101 // simplify home search for multiple hierarchy 1102 if (window.isActivityTypeHome() && animationTarget.isActivityTypeHome()) { 1103 return true; 1104 } 1105 return window == animationTarget 1106 || (animationTarget.asTask() != null && animationTarget.hasChild(window)) 1107 || (animationTarget.asActivityRecord() != null 1108 && window.hasChild(animationTarget)); 1109 } else if (switchType == ACTIVITY_SWITCH) { 1110 return window == animationTarget 1111 || (window.asTaskFragment() != null && window.hasChild(animationTarget)); 1112 } 1113 return false; 1114 } 1115 finishPresentAnimations(boolean cancel)1116 void finishPresentAnimations(boolean cancel) { 1117 if (mOpenActivities != null) { 1118 for (int i = mOpenActivities.length - 1; i >= 0; --i) { 1119 final ActivityRecord resetActivity = mOpenActivities[i]; 1120 if (resetActivity.mDisplayContent.isFixedRotationLaunchingApp(resetActivity)) { 1121 resetActivity.mDisplayContent 1122 .continueUpdateOrientationForDiffOrienLaunchingApp(); 1123 } 1124 if (resetActivity.mLaunchTaskBehind) { 1125 restoreLaunchBehind(resetActivity, cancel); 1126 } 1127 } 1128 } 1129 if (mCloseAdaptor != null) { 1130 mCloseAdaptor.mTarget.cancelAnimation(); 1131 mCloseAdaptor = null; 1132 } 1133 if (mOpenAnimAdaptor != null) { 1134 mOpenAnimAdaptor.cleanUp(mStartingSurfaceTargetMatch); 1135 mOpenAnimAdaptor = null; 1136 } 1137 } 1138 markStartingSurfaceMatch(SurfaceControl.Transaction reparentTransaction)1139 void markStartingSurfaceMatch(SurfaceControl.Transaction reparentTransaction) { 1140 if (mStartingSurfaceTargetMatch) { 1141 return; 1142 } 1143 mStartingSurfaceTargetMatch = true; 1144 mOpenAnimAdaptor.reparentWindowlessSurfaceToTarget(reparentTransaction); 1145 } 1146 clearBackAnimateTarget(boolean cancel)1147 void clearBackAnimateTarget(boolean cancel) { 1148 if (mComposed) { 1149 mComposed = false; 1150 finishPresentAnimations(cancel); 1151 } 1152 mWaitTransition = false; 1153 mStartingSurfaceTargetMatch = false; 1154 mSwitchType = UNKNOWN; 1155 mOpenActivities = null; 1156 } 1157 1158 // The close target must in close list 1159 // The open target can either in close or open list containsBackAnimationTargets(@onNull ArrayList<WindowContainer> openApps, @NonNull ArrayList<WindowContainer> closeApps)1160 boolean containsBackAnimationTargets(@NonNull ArrayList<WindowContainer> openApps, 1161 @NonNull ArrayList<WindowContainer> closeApps) { 1162 return containTarget(closeApps, false /* open */) 1163 && (containTarget(openApps, true /* open */) 1164 || containTarget(openApps, false /* open */)); 1165 } 1166 1167 /** 1168 * Check if any animation target is detached, possibly due to app crash. 1169 */ hasTargetDetached()1170 boolean hasTargetDetached() { 1171 if (!mComposed) { 1172 return false; 1173 } 1174 for (int i = mOpenAnimAdaptor.mAdaptors.length - 1; i >= 0; --i) { 1175 if (!mOpenAnimAdaptor.mAdaptors[i].mTarget.isAttached()) { 1176 return true; 1177 } 1178 } 1179 return !mCloseAdaptor.mTarget.isAttached(); 1180 } 1181 1182 @Override toString()1183 public String toString() { 1184 return "AnimationTargets{" 1185 + " openTarget= " 1186 + (mOpenAnimAdaptor != null ? dumpOpenAnimTargetsToString() : null) 1187 + " closeTarget= " 1188 + (mCloseAdaptor != null ? mCloseAdaptor.mTarget : null) 1189 + " mSwitchType= " 1190 + mSwitchType 1191 + " mComposed= " 1192 + mComposed 1193 + " mWaitTransition= " 1194 + mWaitTransition 1195 + '}'; 1196 } 1197 dumpOpenAnimTargetsToString()1198 private String dumpOpenAnimTargetsToString() { 1199 final StringBuilder sb = new StringBuilder(); 1200 sb.append("{"); 1201 for (int i = 0; i < mOpenAnimAdaptor.mAdaptors.length; i++) { 1202 if (i > 0) { 1203 sb.append(','); 1204 } 1205 sb.append(mOpenAnimAdaptor.mAdaptors[i].mTarget); 1206 } 1207 sb.append("}"); 1208 return sb.toString(); 1209 } 1210 createAdaptor( @onNull WindowContainer target, boolean isOpen, int switchType)1211 @NonNull private static BackWindowAnimationAdaptor createAdaptor( 1212 @NonNull WindowContainer target, boolean isOpen, int switchType) { 1213 final BackWindowAnimationAdaptor adaptor = 1214 new BackWindowAnimationAdaptor(target, isOpen, switchType); 1215 final SurfaceControl.Transaction pt = target.getPendingTransaction(); 1216 // Workaround to show TaskFragment which can be hide in Transitions and won't show 1217 // during isAnimating. 1218 if (isOpen && target.asActivityRecord() != null) { 1219 final TaskFragment fragment = target.asActivityRecord().getTaskFragment(); 1220 if (fragment != null) { 1221 // Ensure task fragment surface has updated, in case configuration has changed. 1222 fragment.updateOrganizedTaskFragmentSurface(); 1223 pt.show(fragment.mSurfaceControl); 1224 } 1225 } 1226 target.startAnimation(pt, adaptor, false /* hidden */, ANIMATION_TYPE_PREDICT_BACK); 1227 return adaptor; 1228 } 1229 1230 private static class BackWindowAnimationAdaptorWrapper { 1231 final BackWindowAnimationAdaptor[] mAdaptors; 1232 // The highest remote animation target, which can be a wrapper if multiple adaptors, 1233 // or the single opening target. 1234 final RemoteAnimationTarget mRemoteAnimationTarget; 1235 SurfaceControl.Transaction mCloseTransaction; 1236 1237 // The starting surface task Id. Used to clear the starting surface if the animation has 1238 // requested one during animating. 1239 private int mRequestedStartingSurfaceId = INVALID_TASK_ID; 1240 private SurfaceControl mStartingSurface; BackWindowAnimationAdaptorWrapper(boolean isOpen, int switchType, @NonNull WindowContainer... targets)1241 BackWindowAnimationAdaptorWrapper(boolean isOpen, int switchType, 1242 @NonNull WindowContainer... targets) { 1243 mAdaptors = new BackWindowAnimationAdaptor[targets.length]; 1244 for (int i = targets.length - 1; i >= 0; --i) { 1245 mAdaptors[i] = createAdaptor(targets[i], isOpen, switchType); 1246 } 1247 mRemoteAnimationTarget = targets.length > 1 ? createWrapTarget() 1248 : mAdaptors[0].mAnimationTarget; 1249 } 1250 isValid()1251 boolean isValid() { 1252 for (int i = mAdaptors.length - 1; i >= 0; --i) { 1253 if (mAdaptors[i].mAnimationTarget == null) { 1254 return false; 1255 } 1256 } 1257 return true; 1258 } 1259 cleanUp(boolean startingSurfaceMatch)1260 void cleanUp(boolean startingSurfaceMatch) { 1261 cleanUpWindowlessSurface(startingSurfaceMatch); 1262 for (int i = mAdaptors.length - 1; i >= 0; --i) { 1263 mAdaptors[i].mTarget.cancelAnimation(); 1264 } 1265 if (mCloseTransaction != null) { 1266 mCloseTransaction.apply(); 1267 mCloseTransaction = null; 1268 } 1269 } 1270 createWrapTarget()1271 private RemoteAnimationTarget createWrapTarget() { 1272 // Special handle for opening two activities together. 1273 // If we animate both activities separately, the animation area and rounded corner 1274 // would also being handled separately. To make them seem like "open" together, wrap 1275 // their leash with another animation leash. 1276 final Rect unionBounds = new Rect(); 1277 for (int i = mAdaptors.length - 1; i >= 0; --i) { 1278 unionBounds.union(mAdaptors[i].mAnimationTarget.localBounds); 1279 } 1280 final WindowContainer wc = mAdaptors[0].mTarget; 1281 final Task task = wc.asActivityRecord() != null 1282 ? wc.asActivityRecord().getTask() : wc.asTask(); 1283 final RemoteAnimationTarget represent = mAdaptors[0].mAnimationTarget; 1284 final SurfaceControl leashSurface = new SurfaceControl.Builder() 1285 .setName("cross-animation-leash") 1286 .setContainerLayer() 1287 .setHidden(false) 1288 .setParent(task.getSurfaceControl()) 1289 .setCallsite( 1290 "BackWindowAnimationAdaptorWrapper.getOrCreateAnimationTarget") 1291 .build(); 1292 mCloseTransaction = new SurfaceControl.Transaction(); 1293 mCloseTransaction.reparent(leashSurface, null); 1294 final SurfaceControl.Transaction pt = wc.getPendingTransaction(); 1295 pt.setLayer(leashSurface, wc.getParent().getLastLayer()); 1296 for (int i = mAdaptors.length - 1; i >= 0; --i) { 1297 BackWindowAnimationAdaptor adaptor = mAdaptors[i]; 1298 pt.reparent(adaptor.mAnimationTarget.leash, leashSurface); 1299 pt.setPosition(adaptor.mAnimationTarget.leash, 1300 adaptor.mAnimationTarget.localBounds.left, 1301 adaptor.mAnimationTarget.localBounds.top); 1302 // For adjacent activity embedded, reparent Activity to TaskFragment when 1303 // animation finish 1304 final WindowContainer parent = adaptor.mTarget.getParent(); 1305 if (parent != null) { 1306 mCloseTransaction.reparent(adaptor.mTarget.getSurfaceControl(), 1307 parent.getSurfaceControl()); 1308 } 1309 } 1310 return new RemoteAnimationTarget(represent.taskId, represent.mode, leashSurface, 1311 represent.isTranslucent, represent.clipRect, represent.contentInsets, 1312 represent.prefixOrderIndex, 1313 new Point(unionBounds.left, unionBounds.top), 1314 unionBounds, unionBounds, represent.windowConfiguration, 1315 true /* isNotInRecents */, null, null, represent.taskInfo, 1316 represent.allowEnterPip); 1317 } 1318 createStartingSurface(@ullable TaskSnapshot snapshot)1319 void createStartingSurface(@Nullable TaskSnapshot snapshot) { 1320 if (mAdaptors[0].mSwitchType == DIALOG_CLOSE) { 1321 return; 1322 } 1323 final WindowContainer mainOpen = mAdaptors[0].mTarget; 1324 final int switchType = mAdaptors[0].mSwitchType; 1325 final Task openTask = switchType == TASK_SWITCH 1326 ? mainOpen.asTask() : switchType == ACTIVITY_SWITCH 1327 ? mainOpen.asActivityRecord().getTask() : null; 1328 if (openTask == null) { 1329 return; 1330 } 1331 final ActivityRecord mainActivity = switchType == ACTIVITY_SWITCH 1332 ? mainOpen.asActivityRecord() 1333 : openTask.getTopNonFinishingActivity(); 1334 if (mainActivity == null) { 1335 return; 1336 } 1337 // If there is only one adaptor, attach the windowless window to top activity, 1338 // because fixed rotation only applies on activity. 1339 // Note that embedded activity won't use fixed rotation. 1340 final Configuration openConfig = mAdaptors.length == 1 1341 ? mainActivity.getConfiguration() : openTask.getConfiguration(); 1342 mRequestedStartingSurfaceId = openTask.mAtmService.mTaskOrganizerController 1343 .addWindowlessStartingSurface(openTask, mainActivity, 1344 mAdaptors.length == 1 ? mainActivity.getSurfaceControl() 1345 : mRemoteAnimationTarget.leash, snapshot, openConfig, 1346 new IWindowlessStartingSurfaceCallback.Stub() { 1347 // Once the starting surface has been created in shell, it will call 1348 // onSurfaceAdded to pass the created surface to core, so if a 1349 // transition is triggered by the back gesture, there doesn't need to 1350 // create another starting surface for the opening target, just reparent 1351 // the starting surface to the opening target. 1352 // Note if cleanUpWindowlessSurface happen prior than onSurfaceAdded 1353 // called, there won't be able to reparent the starting surface on 1354 // opening target. But if that happens and transition target is matched, 1355 // the app window should already draw. 1356 @Override 1357 public void onSurfaceAdded(SurfaceControl sc) { 1358 synchronized (openTask.mWmService.mGlobalLock) { 1359 if (mRequestedStartingSurfaceId != INVALID_TASK_ID) { 1360 mStartingSurface = sc; 1361 } 1362 } 1363 } 1364 }); 1365 } 1366 1367 // When back gesture has triggered and transition target matches navigation target, 1368 // reparent the starting surface to the opening target as it's starting window. reparentWindowlessSurfaceToTarget(SurfaceControl.Transaction reparentTransaction)1369 void reparentWindowlessSurfaceToTarget(SurfaceControl.Transaction reparentTransaction) { 1370 if (mRequestedStartingSurfaceId == INVALID_TASK_ID) { 1371 return; 1372 } 1373 // If open target matches, reparent to open activity or task 1374 if (mStartingSurface != null && mStartingSurface.isValid()) { 1375 SurfaceControl.Transaction transaction = reparentTransaction != null 1376 ? reparentTransaction : mAdaptors[0].mTarget.getPendingTransaction(); 1377 if (mAdaptors.length != 1) { 1378 // More than one opening window, reparent starting surface to leaf task. 1379 final WindowContainer wc = mAdaptors[0].mTarget; 1380 final Task task = wc.asActivityRecord() != null 1381 ? wc.asActivityRecord().getTask() : wc.asTask(); 1382 transaction.reparent(mStartingSurface, task != null 1383 ? task.getSurfaceControl() 1384 : mAdaptors[0].mTarget.getSurfaceControl()); 1385 } 1386 } 1387 } 1388 1389 /** 1390 * Ask shell to clear the starting surface. 1391 * @param openTransitionMatch if true, shell will play the remove starting window 1392 * animation, otherwise remove it directly. 1393 */ cleanUpWindowlessSurface(boolean openTransitionMatch)1394 void cleanUpWindowlessSurface(boolean openTransitionMatch) { 1395 if (mRequestedStartingSurfaceId == INVALID_TASK_ID) { 1396 return; 1397 } 1398 mAdaptors[0].mTarget.mWmService.mAtmService.mTaskOrganizerController 1399 .removeWindowlessStartingSurface(mRequestedStartingSurfaceId, 1400 !openTransitionMatch); 1401 mRequestedStartingSurfaceId = INVALID_TASK_ID; 1402 if (mStartingSurface != null && mStartingSurface.isValid()) { 1403 mStartingSurface.release(); 1404 mStartingSurface = null; 1405 } 1406 } 1407 } 1408 1409 private static class BackWindowAnimationAdaptor implements AnimationAdapter { 1410 SurfaceControl mCapturedLeash; 1411 boolean mAppWindowDrawn; 1412 private final Rect mBounds = new Rect(); 1413 private final WindowContainer mTarget; 1414 private final boolean mIsOpen; 1415 private RemoteAnimationTarget mAnimationTarget; 1416 private final int mSwitchType; 1417 BackWindowAnimationAdaptor(@onNull WindowContainer target, boolean isOpen, int switchType)1418 BackWindowAnimationAdaptor(@NonNull WindowContainer target, boolean isOpen, 1419 int switchType) { 1420 mBounds.set(target.getBounds()); 1421 mTarget = target; 1422 mIsOpen = isOpen; 1423 mSwitchType = switchType; 1424 } 1425 1426 @Override getShowWallpaper()1427 public boolean getShowWallpaper() { 1428 return false; 1429 } 1430 1431 @Override startAnimation(SurfaceControl animationLeash, SurfaceControl.Transaction t, int type, SurfaceAnimator.OnAnimationFinishedCallback finishCallback)1432 public void startAnimation(SurfaceControl animationLeash, SurfaceControl.Transaction t, 1433 int type, SurfaceAnimator.OnAnimationFinishedCallback finishCallback) { 1434 mCapturedLeash = animationLeash; 1435 createRemoteAnimationTarget(); 1436 final WindowState win = mTarget.asWindowState(); 1437 if (win != null && mSwitchType == DIALOG_CLOSE) { 1438 final Rect frame = win.getFrame(); 1439 final Point position = new Point(); 1440 win.transformFrameToSurfacePosition(frame.left, frame.top, position); 1441 t.setPosition(mCapturedLeash, position.x, position.y); 1442 } 1443 } 1444 1445 @Override onAnimationCancelled(SurfaceControl animationLeash)1446 public void onAnimationCancelled(SurfaceControl animationLeash) { 1447 if (mCapturedLeash == animationLeash) { 1448 mCapturedLeash = null; 1449 } 1450 } 1451 1452 @Override getDurationHint()1453 public long getDurationHint() { 1454 return 0; 1455 } 1456 1457 @Override getStatusBarTransitionsStartTime()1458 public long getStatusBarTransitionsStartTime() { 1459 return 0; 1460 } 1461 1462 @Override dump(PrintWriter pw, String prefix)1463 public void dump(PrintWriter pw, String prefix) { 1464 pw.print(prefix + "BackWindowAnimationAdaptor mCapturedLeash="); 1465 pw.print(mCapturedLeash); 1466 pw.println(); 1467 } 1468 1469 @Override dumpDebug(ProtoOutputStream proto)1470 public void dumpDebug(ProtoOutputStream proto) { 1471 1472 } 1473 createRemoteAnimationTarget()1474 RemoteAnimationTarget createRemoteAnimationTarget() { 1475 if (mAnimationTarget != null) { 1476 return mAnimationTarget; 1477 } 1478 1479 WindowState w = mTarget.asWindowState(); 1480 ActivityRecord r = w != null ? w.getActivityRecord() : null; 1481 Task t = r != null ? r.getTask() : mTarget.asTask(); 1482 if (t == null && mTarget.asTaskFragment() != null) { 1483 t = mTarget.asTaskFragment().getTask(); 1484 r = mTarget.asTaskFragment().getTopNonFinishingActivity(); 1485 } 1486 if (r == null) { 1487 r = t != null ? t.getTopNonFinishingActivity() 1488 : mTarget.asActivityRecord(); 1489 } 1490 if (t == null && r != null) { 1491 t = r.getTask(); 1492 } 1493 if (t == null || r == null) { 1494 Slog.e(TAG, "createRemoteAnimationTarget fail " + mTarget); 1495 return null; 1496 } 1497 final WindowState mainWindow = r.findMainWindow(); 1498 final Rect insets = mainWindow != null 1499 ? mainWindow.getInsetsStateWithVisibilityOverride().calculateInsets( 1500 mBounds, WindowInsets.Type.tappableElement(), 1501 false /* ignoreVisibility */).toRect() 1502 : new Rect(); 1503 final int mode = mIsOpen ? MODE_OPENING : MODE_CLOSING; 1504 mAnimationTarget = new RemoteAnimationTarget(t.mTaskId, mode, mCapturedLeash, 1505 !r.fillsParent(), new Rect(), 1506 insets, r.getPrefixOrderIndex(), new Point(mBounds.left, mBounds.top), 1507 mBounds, mBounds, t.getWindowConfiguration(), 1508 true /* isNotInRecents */, null, null, t.getTaskInfo(), 1509 r.checkEnterPictureInPictureAppOpsState()); 1510 return mAnimationTarget; 1511 } 1512 } 1513 prepareAnimation( int backType, BackAnimationAdapter adapter, NavigationMonitor monitor, Task currentTask, Task previousTask, ActivityRecord currentActivity, ArrayList<ActivityRecord> previousActivity, WindowContainer removedWindowContainer)1514 ScheduleAnimationBuilder prepareAnimation( 1515 int backType, 1516 BackAnimationAdapter adapter, 1517 NavigationMonitor monitor, 1518 Task currentTask, 1519 Task previousTask, 1520 ActivityRecord currentActivity, 1521 ArrayList<ActivityRecord> previousActivity, 1522 WindowContainer removedWindowContainer) { 1523 final ScheduleAnimationBuilder builder = 1524 new ScheduleAnimationBuilder(backType, adapter, monitor); 1525 switch (backType) { 1526 case BackNavigationInfo.TYPE_RETURN_TO_HOME: 1527 return builder 1528 .setIsLaunchBehind(true) 1529 .setComposeTarget(currentTask, previousTask); 1530 case BackNavigationInfo.TYPE_CROSS_ACTIVITY: 1531 ActivityRecord[] prevActs = new ActivityRecord[previousActivity.size()]; 1532 prevActs = previousActivity.toArray(prevActs); 1533 return builder 1534 .setComposeTarget(currentActivity, prevActs) 1535 .setIsLaunchBehind(false); 1536 case BackNavigationInfo.TYPE_CROSS_TASK: 1537 return builder 1538 .setComposeTarget(currentTask, previousTask) 1539 .setIsLaunchBehind(false); 1540 case BackNavigationInfo.TYPE_DIALOG_CLOSE: 1541 return builder 1542 .setComposeTarget(removedWindowContainer, currentActivity) 1543 .setIsLaunchBehind(false); 1544 } 1545 return null; 1546 } 1547 1548 class ScheduleAnimationBuilder { 1549 final int mType; 1550 final BackAnimationAdapter mBackAnimationAdapter; 1551 final NavigationMonitor mNavigationMonitor; 1552 WindowContainer mCloseTarget; 1553 WindowContainer[] mOpenTargets; 1554 boolean mIsLaunchBehind; 1555 ScheduleAnimationBuilder(int type, BackAnimationAdapter adapter, NavigationMonitor monitor)1556 ScheduleAnimationBuilder(int type, BackAnimationAdapter adapter, 1557 NavigationMonitor monitor) { 1558 mType = type; 1559 mBackAnimationAdapter = adapter; 1560 mNavigationMonitor = monitor; 1561 } 1562 setComposeTarget(@onNull WindowContainer close, @NonNull WindowContainer... open)1563 ScheduleAnimationBuilder setComposeTarget(@NonNull WindowContainer close, 1564 @NonNull WindowContainer... open) { 1565 mCloseTarget = close; 1566 mOpenTargets = open; 1567 return this; 1568 } 1569 setIsLaunchBehind(boolean launchBehind)1570 ScheduleAnimationBuilder setIsLaunchBehind(boolean launchBehind) { 1571 mIsLaunchBehind = launchBehind; 1572 return this; 1573 } 1574 1575 // WC must be Activity/TaskFragment/Task containTarget(@onNull WindowContainer wc)1576 boolean containTarget(@NonNull WindowContainer wc) { 1577 if (mOpenTargets != null) { 1578 for (int i = mOpenTargets.length - 1; i >= 0; --i) { 1579 if (wc == mOpenTargets[i] || mOpenTargets[i].hasChild(wc) 1580 || wc.hasChild(mOpenTargets[i])) { 1581 return true; 1582 } 1583 } 1584 } 1585 return wc == mCloseTarget || mCloseTarget.hasChild(wc) || wc.hasChild(mCloseTarget); 1586 } 1587 1588 /** 1589 * Apply preview strategy on the opening target 1590 * 1591 * @param openAnimationAdaptor The animator who can create starting surface. 1592 * @param visibleOpenActivities The visible activities in opening targets. 1593 */ applyPreviewStrategy( @onNull BackWindowAnimationAdaptorWrapper openAnimationAdaptor, @NonNull ActivityRecord[] visibleOpenActivities)1594 private void applyPreviewStrategy( 1595 @NonNull BackWindowAnimationAdaptorWrapper openAnimationAdaptor, 1596 @NonNull ActivityRecord[] visibleOpenActivities) { 1597 boolean needsLaunchBehind = true; 1598 if (isSupportWindowlessSurface() && mShowWindowlessSurface && !mIsLaunchBehind) { 1599 final WindowContainer mainOpen = openAnimationAdaptor.mAdaptors[0].mTarget; 1600 final TaskSnapshot snapshot = getSnapshot(mainOpen, visibleOpenActivities); 1601 openAnimationAdaptor.createStartingSurface(snapshot); 1602 // set LaunchBehind if we are creating splash screen surface. 1603 needsLaunchBehind = snapshot == null 1604 && openAnimationAdaptor.mRequestedStartingSurfaceId != INVALID_TASK_ID; 1605 } 1606 if (needsLaunchBehind) { 1607 for (int i = visibleOpenActivities.length - 1; i >= 0; --i) { 1608 setLaunchBehind(visibleOpenActivities[i]); 1609 } 1610 } 1611 // Force update mLastSurfaceShowing for opening activity and its task. 1612 if (mWindowManagerService.mRoot.mTransitionController.isShellTransitionsEnabled()) { 1613 for (int i = visibleOpenActivities.length - 1; i >= 0; --i) { 1614 WindowContainer.enforceSurfaceVisible(visibleOpenActivities[i]); 1615 } 1616 } 1617 } 1618 build()1619 @Nullable Runnable build() { 1620 if (mOpenTargets == null || mCloseTarget == null || mOpenTargets.length == 0) { 1621 return null; 1622 } 1623 final boolean shouldLaunchBehind = mIsLaunchBehind || !isSupportWindowlessSurface(); 1624 final ActivityRecord[] openingActivities = getTopOpenActivities(mOpenTargets); 1625 1626 if (shouldLaunchBehind && openingActivities == null) { 1627 Slog.e(TAG, "No opening activity"); 1628 return null; 1629 } 1630 1631 if (!composeAnimations(mCloseTarget, mOpenTargets, openingActivities)) { 1632 return null; 1633 } 1634 mCloseTarget.mTransitionController.mSnapshotController 1635 .mActivitySnapshotController.clearOnBackPressedActivities(); 1636 applyPreviewStrategy(mOpenAnimAdaptor, openingActivities); 1637 1638 final IBackAnimationFinishedCallback callback = makeAnimationFinishedCallback(); 1639 final RemoteAnimationTarget[] targets = getAnimationTargets(); 1640 1641 return () -> { 1642 try { 1643 if (hasTargetDetached() || !validateAnimationTargets(targets)) { 1644 mNavigationMonitor.cancelBackNavigating("cancelAnimation"); 1645 mBackAnimationAdapter.getRunner().onAnimationCancelled(); 1646 } else { 1647 mBackAnimationAdapter.getRunner().onAnimationStart( 1648 targets, null, null, callback); 1649 } 1650 } catch (RemoteException e) { 1651 e.printStackTrace(); 1652 } 1653 }; 1654 } 1655 makeAnimationFinishedCallback()1656 private IBackAnimationFinishedCallback makeAnimationFinishedCallback() { 1657 return new IBackAnimationFinishedCallback.Stub() { 1658 @Override 1659 public void onAnimationFinished(boolean triggerBack) { 1660 synchronized (mWindowManagerService.mGlobalLock) { 1661 if (!mComposed) { 1662 // animation was canceled 1663 return; 1664 } 1665 if (!triggerBack) { 1666 clearBackAnimateTarget(true /* cancel */); 1667 } else { 1668 mWaitTransition = true; 1669 } 1670 } 1671 // TODO Add timeout monitor if transition didn't happen 1672 } 1673 }; 1674 } 1675 } 1676 } 1677 1678 /** 1679 * Validate animation targets. 1680 */ 1681 private static boolean validateAnimationTargets(RemoteAnimationTarget[] apps) { 1682 if (apps == null || apps.length == 0) { 1683 return false; 1684 } 1685 for (int i = apps.length - 1; i >= 0; --i) { 1686 if (!apps[i].leash.isValid()) { 1687 return false; 1688 } 1689 } 1690 return true; 1691 } 1692 1693 /** 1694 * Finds next opening activity(ies) based on open targets, which could be: 1695 * 1. If the open window is Task, then the open activity can either be an activity, or 1696 * two activities inside two TaskFragments 1697 * 2. If the open window is Activity, then the open window can be an activity, or two 1698 * adjacent TaskFragments below it. 1699 */ 1700 @Nullable 1701 private static ActivityRecord[] getTopOpenActivities( 1702 @NonNull WindowContainer[] openWindows) { 1703 ActivityRecord[] openActivities = null; 1704 final WindowContainer mainTarget = openWindows[0]; 1705 if (mainTarget.asTask() != null) { 1706 final ArrayList<ActivityRecord> inTaskActivities = new ArrayList<>(); 1707 final Task task = mainTarget.asTask(); 1708 final ActivityRecord tmpPreActivity = task.getTopNonFinishingActivity(); 1709 if (tmpPreActivity != null) { 1710 inTaskActivities.add(tmpPreActivity); 1711 findAdjacentActivityIfExist(tmpPreActivity, inTaskActivities); 1712 } 1713 1714 openActivities = new ActivityRecord[inTaskActivities.size()]; 1715 for (int i = inTaskActivities.size() - 1; i >= 0; --i) { 1716 openActivities[i] = inTaskActivities.get(i); 1717 } 1718 } else if (mainTarget.asActivityRecord() != null) { 1719 final int size = openWindows.length; 1720 openActivities = new ActivityRecord[size]; 1721 for (int i = size - 1; i >= 0; --i) { 1722 openActivities[i] = openWindows[i].asActivityRecord(); 1723 } 1724 } 1725 return openActivities; 1726 } 1727 1728 private static void setLaunchBehind(@NonNull ActivityRecord activity) { 1729 if (!activity.isVisibleRequested()) { 1730 // The transition could commit the visibility and in the finishing state, that could 1731 // skip commitVisibility call in setVisibility cause the activity won't visible here. 1732 // Call it again to make sure the activity could be visible while handling the pending 1733 // animation. 1734 // Do not performLayout during prepare animation, because it could cause focus window 1735 // change. Let that happen after the BackNavigationInfo has returned to shell. 1736 activity.commitVisibility(true, false /* performLayout */); 1737 activity.mTransitionController.mSnapshotController 1738 .mActivitySnapshotController.addOnBackPressedActivity(activity); 1739 } 1740 activity.mLaunchTaskBehind = true; 1741 1742 ProtoLog.d(WM_DEBUG_BACK_PREVIEW, 1743 "Setting Activity.mLauncherTaskBehind to true. Activity=%s", activity); 1744 activity.mTaskSupervisor.mStoppingActivities.remove(activity); 1745 activity.getDisplayContent().ensureActivitiesVisible(null /* starting */, 1746 true /* notifyClients */); 1747 } 1748 1749 private static void restoreLaunchBehind(@NonNull ActivityRecord activity, boolean cancel) { 1750 if (!activity.isAttached()) { 1751 // The activity was detached from hierarchy. 1752 return; 1753 } 1754 activity.mLaunchTaskBehind = false; 1755 ProtoLog.d(WM_DEBUG_BACK_PREVIEW, 1756 "Setting Activity.mLauncherTaskBehind to false. Activity=%s", 1757 activity); 1758 if (cancel) { 1759 // Restore the launch-behind state 1760 // TODO b/347168362 Change status directly during collecting for a transition. 1761 activity.mTaskSupervisor.scheduleLaunchTaskBehindComplete(activity.token); 1762 // Ignore all change 1763 activity.mTransitionController.mSnapshotController 1764 .mActivitySnapshotController.clearOnBackPressedActivities(); 1765 } 1766 } 1767 1768 void checkAnimationReady(WallpaperController wallpaperController) { 1769 if (!mBackAnimationInProgress) { 1770 return; 1771 } 1772 1773 final boolean wallpaperReady = !mShowWallpaper 1774 || (wallpaperController.getWallpaperTarget() != null 1775 && wallpaperController.wallpaperTransitionReady()); 1776 if (wallpaperReady && mPendingAnimation != null) { 1777 mWindowManagerService.mAnimator.addAfterPrepareSurfacesRunnable(this::startAnimation); 1778 } 1779 } 1780 1781 void startAnimation() { 1782 if (!mBackAnimationInProgress) { 1783 // gesture is already finished, do not start animation 1784 if (mPendingAnimation != null) { 1785 clearBackAnimations(true /* cancel */); 1786 mPendingAnimation = null; 1787 } 1788 return; 1789 } 1790 if (mPendingAnimation != null) { 1791 mPendingAnimation.run(); 1792 mPendingAnimation = null; 1793 } 1794 } 1795 1796 private void onBackNavigationDone(Bundle result, int backType) { 1797 if (result == null) { 1798 return; 1799 } 1800 if (result.containsKey(BackNavigationInfo.KEY_NAVIGATION_FINISHED)) { 1801 final boolean triggerBack = result.getBoolean( 1802 BackNavigationInfo.KEY_NAVIGATION_FINISHED); 1803 ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "onBackNavigationDone backType=%s, " 1804 + "triggerBack=%b", backType, triggerBack); 1805 1806 synchronized (mWindowManagerService.mGlobalLock) { 1807 mNavigationMonitor.stopMonitorForRemote(); 1808 mBackAnimationInProgress = false; 1809 mShowWallpaper = false; 1810 // All animation should be done, clear any un-send animation. 1811 mPendingAnimation = null; 1812 mPendingAnimationBuilder = null; 1813 } 1814 } 1815 if (result.getBoolean(BackNavigationInfo.KEY_GESTURE_FINISHED)) { 1816 synchronized (mWindowManagerService.mGlobalLock) { 1817 final AnimationHandler ah = mAnimationHandler; 1818 if (!ah.mComposed || ah.mWaitTransition || ah.mOpenActivities == null 1819 || (ah.mSwitchType != AnimationHandler.TASK_SWITCH 1820 && ah.mSwitchType != AnimationHandler.ACTIVITY_SWITCH)) { 1821 return; 1822 } 1823 for (int i = mAnimationHandler.mOpenActivities.length - 1; i >= 0; --i) { 1824 final ActivityRecord preDrawActivity = mAnimationHandler.mOpenActivities[i]; 1825 if (!preDrawActivity.mLaunchTaskBehind) { 1826 setLaunchBehind(preDrawActivity); 1827 } 1828 } 1829 } 1830 } 1831 } 1832 1833 static TaskSnapshot getSnapshot(@NonNull WindowContainer w, 1834 ActivityRecord[] visibleOpenActivities) { 1835 TaskSnapshot snapshot = null; 1836 if (w.asTask() != null) { 1837 final Task task = w.asTask(); 1838 snapshot = task.mRootWindowContainer.mWindowManager.mTaskSnapshotController.getSnapshot( 1839 task.mTaskId, task.mUserId, false /* restoreFromDisk */, 1840 false /* isLowResolution */); 1841 } else if (w.asActivityRecord() != null) { 1842 final ActivityRecord ar = w.asActivityRecord(); 1843 snapshot = ar.mWmService.mSnapshotController.mActivitySnapshotController 1844 .getSnapshot(visibleOpenActivities); 1845 } 1846 1847 return isSnapshotCompatible(snapshot, visibleOpenActivities) ? snapshot : null; 1848 } 1849 1850 static boolean isSnapshotCompatible(@NonNull TaskSnapshot snapshot, 1851 @NonNull ActivityRecord[] visibleOpenActivities) { 1852 if (snapshot == null) { 1853 return false; 1854 } 1855 boolean oneComponentMatch = false; 1856 for (int i = visibleOpenActivities.length - 1; i >= 0; --i) { 1857 final ActivityRecord ar = visibleOpenActivities[i]; 1858 if (!ar.isSnapshotOrientationCompatible(snapshot)) { 1859 return false; 1860 } 1861 oneComponentMatch |= ar.isSnapshotComponentCompatible(snapshot); 1862 } 1863 return oneComponentMatch; 1864 } 1865 1866 1867 void setWindowManager(WindowManagerService wm) { 1868 mWindowManagerService = wm; 1869 mAnimationHandler = new AnimationHandler(wm); 1870 } 1871 1872 boolean isWallpaperVisible(WindowState w) { 1873 return mAnimationHandler.mComposed && mShowWallpaper 1874 && w.mAttrs.type == TYPE_BASE_APPLICATION && w.mActivityRecord != null 1875 && mAnimationHandler.isTarget(w.mActivityRecord, true /* open */); 1876 } 1877 1878 // Called from WindowManagerService to write to a protocol buffer output stream. 1879 void dumpDebug(ProtoOutputStream proto, long fieldId) { 1880 final long token = proto.start(fieldId); 1881 proto.write(ANIMATION_IN_PROGRESS, mBackAnimationInProgress); 1882 proto.write(LAST_BACK_TYPE, mLastBackType); 1883 proto.write(SHOW_WALLPAPER, mShowWallpaper); 1884 if (mAnimationHandler.mOpenAnimAdaptor != null 1885 && mAnimationHandler.mOpenAnimAdaptor.mAdaptors.length > 0) { 1886 mAnimationHandler.mOpenActivities[0].writeNameToProto( 1887 proto, MAIN_OPEN_ACTIVITY); 1888 } else { 1889 proto.write(MAIN_OPEN_ACTIVITY, ""); 1890 } 1891 // TODO (b/268563842) Only meaningful after new test added 1892 proto.write(ANIMATION_RUNNING, mAnimationHandler.mComposed 1893 || mAnimationHandler.mWaitTransition); 1894 proto.end(token); 1895 } 1896 } 1897