• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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