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