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