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