• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.wm.shell.back;
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.TRANSIT_CHANGE;
23 import static android.view.WindowManager.TRANSIT_CLOSE_PREPARE_BACK_NAVIGATION;
24 import static android.window.BackEvent.EDGE_NONE;
25 import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
26 import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
27 import static android.window.TransitionInfo.FLAG_MOVED_TO_TOP;
28 import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER;
29 
30 import static com.android.internal.jank.InteractionJankMonitor.CUJ_PREDICTIVE_BACK_HOME;
31 import static com.android.systemui.Flags.predictiveBackDelayWmTransition;
32 import static com.android.window.flags.Flags.unifyBackNavigationTransition;
33 import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW;
34 
35 import android.annotation.NonNull;
36 import android.annotation.Nullable;
37 import android.annotation.SuppressLint;
38 import android.app.ActivityManager;
39 import android.app.ActivityTaskManager;
40 import android.app.IActivityTaskManager;
41 import android.app.TaskInfo;
42 import android.content.ComponentName;
43 import android.content.Context;
44 import android.content.res.Configuration;
45 import android.graphics.Point;
46 import android.graphics.Rect;
47 import android.hardware.input.InputManager;
48 import android.os.Bundle;
49 import android.os.Handler;
50 import android.os.IBinder;
51 import android.os.RemoteCallback;
52 import android.os.RemoteException;
53 import android.os.SystemClock;
54 import android.util.Log;
55 import android.view.IRemoteAnimationRunner;
56 import android.view.InputDevice;
57 import android.view.KeyCharacterMap;
58 import android.view.KeyEvent;
59 import android.view.MotionEvent;
60 import android.view.RemoteAnimationTarget;
61 import android.view.SurfaceControl;
62 import android.view.WindowManager;
63 import android.window.BackAnimationAdapter;
64 import android.window.BackEvent;
65 import android.window.BackMotionEvent;
66 import android.window.BackNavigationInfo;
67 import android.window.BackTouchTracker;
68 import android.window.IBackAnimationFinishedCallback;
69 import android.window.IBackAnimationHandoffHandler;
70 import android.window.IBackAnimationRunner;
71 import android.window.IOnBackInvokedCallback;
72 import android.window.TransitionInfo;
73 import android.window.TransitionRequestInfo;
74 import android.window.WindowAnimationState;
75 import android.window.WindowContainerToken;
76 import android.window.WindowContainerTransaction;
77 
78 import com.android.internal.annotations.VisibleForTesting;
79 import com.android.internal.protolog.ProtoLog;
80 import com.android.internal.util.LatencyTracker;
81 import com.android.internal.view.AppearanceRegion;
82 import com.android.systemui.animation.TransitionAnimator;
83 import com.android.window.flags.Flags;
84 import com.android.wm.shell.R;
85 import com.android.wm.shell.common.ExternalInterfaceBinder;
86 import com.android.wm.shell.common.RemoteCallable;
87 import com.android.wm.shell.common.ShellExecutor;
88 import com.android.wm.shell.shared.TransitionUtil;
89 import com.android.wm.shell.shared.annotations.ShellMainThread;
90 import com.android.wm.shell.sysui.ConfigurationChangeListener;
91 import com.android.wm.shell.sysui.ShellCommandHandler;
92 import com.android.wm.shell.sysui.ShellController;
93 import com.android.wm.shell.sysui.ShellInit;
94 import com.android.wm.shell.transition.Transitions;
95 
96 import java.io.PrintWriter;
97 import java.util.ArrayList;
98 import java.util.function.Predicate;
99 
100 /**
101  * Controls the window animation run when a user initiates a back gesture.
102  */
103 public class BackAnimationController implements RemoteCallable<BackAnimationController>,
104         ConfigurationChangeListener {
105     private static final String TAG = "ShellBackPreview";
106 
107     /**
108      * Max duration to wait for an animation to finish before triggering the real back.
109      */
110     private static final long MAX_ANIMATION_DURATION = 2000;
111     private final LatencyTracker mLatencyTracker;
112     @ShellMainThread private final Handler mHandler;
113 
114     /** True when a back gesture is ongoing */
115     @VisibleForTesting public boolean mBackGestureStarted = false;
116 
117     /** Tracks if an uninterruptible animation is in progress */
118     private boolean mPostCommitAnimationInProgress = false;
119     private boolean mRealCallbackInvoked = false;
120 
121     /** Tracks if we should start the back gesture on the next motion move event */
122     private boolean mShouldStartOnNextMoveEvent = false;
123     private boolean mOnBackStartDispatched = false;
124     private boolean mThresholdCrossed = false;
125     private boolean mPointersPilfered = false;
126     private final boolean mRequirePointerPilfer;
127 
128     /** Registry for the back animations */
129     private final ShellBackAnimationRegistry mShellBackAnimationRegistry;
130 
131     @Nullable
132     private BackNavigationInfo mBackNavigationInfo;
133     private boolean mReceivedNullNavigationInfo = false;
134     private final IActivityTaskManager mActivityTaskManager;
135     private final Context mContext;
136     private final ShellController mShellController;
137     private final ShellCommandHandler mShellCommandHandler;
138     private final ShellExecutor mShellExecutor;
139     private final WindowManager mWindowManager;
140     private final Transitions mTransitions;
141     @VisibleForTesting
142     final BackTransitionHandler mBackTransitionHandler;
143     @VisibleForTesting
144     final Rect mTouchableArea = new Rect();
145 
146     /**
147      * Tracks the current user back gesture.
148      */
149     private BackTouchTracker mCurrentTracker = new BackTouchTracker();
150 
151     /**
152      * Tracks the next back gesture in case a new user gesture has started while the back animation
153      * (and navigation) associated with {@link #mCurrentTracker} have not yet finished.
154      */
155     private BackTouchTracker mQueuedTracker = new BackTouchTracker();
156 
157     private final BackTransitionObserver mBackTransitionObserver =
158             new BackTransitionObserver();
159 
160     private final Runnable mAnimationTimeoutRunnable = () -> {
161         ProtoLog.w(WM_SHELL_BACK_PREVIEW, "Animation didn't finish in %d ms. Resetting...",
162                 MAX_ANIMATION_DURATION);
163         finishBackAnimation();
164     };
165 
166     private IBackAnimationFinishedCallback mBackAnimationFinishedCallback;
167     @VisibleForTesting
168     BackAnimationAdapter mBackAnimationAdapter;
169 
170     @Nullable
171     private IOnBackInvokedCallback mActiveCallback;
172     @Nullable
173     @VisibleForTesting
174     RemoteAnimationTarget[] mApps;
175 
176     @VisibleForTesting
177     final RemoteCallback mNavigationObserver = new RemoteCallback(
178             new RemoteCallback.OnResultListener() {
179                 @Override
180                 public void onResult(@Nullable Bundle result) {
181                     mShellExecutor.execute(() -> {
182                         if (mBackGestureStarted && result != null && result.getBoolean(
183                                 BackNavigationInfo.KEY_TOUCH_GESTURE_TRANSFERRED)) {
184                             // Host app won't able to process motion event anymore, so pilfer
185                             // pointers anyway.
186                             if (mBackNavigationInfo != null) {
187                                 mBackNavigationInfo.disableAppProgressGenerationAllowed();
188                             }
189                             tryPilferPointers();
190                             return;
191                         }
192                         if (!mBackGestureStarted || mPostCommitAnimationInProgress) {
193                             // If an uninterruptible animation is already in progress, we should
194                             // ignore this due to it may cause focus lost. (alpha = 0)
195                             return;
196                         }
197                         ProtoLog.i(WM_SHELL_BACK_PREVIEW, "Navigation window gone.");
198                         setTriggerBack(false);
199                         // Trigger close transition if necessary.
200                         mBackTransitionHandler.onAnimationFinished();
201                         resetTouchTracker();
202                         // Don't wait for animation start
203                         mShellExecutor.removeCallbacks(mAnimationTimeoutRunnable);
204                     });
205                 }
206             });
207 
208     private final BackAnimationBackground mAnimationBackground;
209     private StatusBarCustomizer mCustomizer;
210     private boolean mTrackingLatency;
211 
212     // Keep previous navigation type before remove mBackNavigationInfo.
213     @BackNavigationInfo.BackTargetType
214     private int mPreviousNavigationType;
215     private Runnable mPilferPointerCallback;
216     private BackAnimation.TopUiRequest mRequestTopUiCallback;
217 
218     private final IBackAnimationHandoffHandler mHandoffHandler =
219             new IBackAnimationHandoffHandler.Stub() {
220                 @Override
221                 public void handOffAnimation(
222                         RemoteAnimationTarget[] targets, WindowAnimationState[] states) {
223                     mBackTransitionHandler.handOffAnimation(targets, states);
224                 }
225             };
226 
BackAnimationController( @onNull ShellInit shellInit, @NonNull ShellController shellController, @NonNull @ShellMainThread ShellExecutor shellExecutor, Context context, @NonNull BackAnimationBackground backAnimationBackground, ShellBackAnimationRegistry shellBackAnimationRegistry, ShellCommandHandler shellCommandHandler, Transitions transitions, @ShellMainThread Handler handler)227     public BackAnimationController(
228             @NonNull ShellInit shellInit,
229             @NonNull ShellController shellController,
230             @NonNull @ShellMainThread ShellExecutor shellExecutor,
231             Context context,
232             @NonNull BackAnimationBackground backAnimationBackground,
233             ShellBackAnimationRegistry shellBackAnimationRegistry,
234             ShellCommandHandler shellCommandHandler,
235             Transitions transitions,
236             @ShellMainThread Handler handler) {
237         this(
238                 shellInit,
239                 shellController,
240                 shellExecutor,
241                 ActivityTaskManager.getService(),
242                 context,
243                 backAnimationBackground,
244                 shellBackAnimationRegistry,
245                 shellCommandHandler,
246                 transitions,
247                 handler);
248     }
249 
250     @VisibleForTesting
BackAnimationController( @onNull ShellInit shellInit, @NonNull ShellController shellController, @NonNull @ShellMainThread ShellExecutor shellExecutor, @NonNull IActivityTaskManager activityTaskManager, Context context, @NonNull BackAnimationBackground backAnimationBackground, ShellBackAnimationRegistry shellBackAnimationRegistry, ShellCommandHandler shellCommandHandler, Transitions transitions, @NonNull @ShellMainThread Handler handler)251     BackAnimationController(
252             @NonNull ShellInit shellInit,
253             @NonNull ShellController shellController,
254             @NonNull @ShellMainThread ShellExecutor shellExecutor,
255             @NonNull IActivityTaskManager activityTaskManager,
256             Context context,
257             @NonNull BackAnimationBackground backAnimationBackground,
258             ShellBackAnimationRegistry shellBackAnimationRegistry,
259             ShellCommandHandler shellCommandHandler,
260             Transitions transitions,
261             @NonNull @ShellMainThread Handler handler) {
262         mShellController = shellController;
263         mShellExecutor = shellExecutor;
264         mActivityTaskManager = activityTaskManager;
265         mContext = context;
266         mRequirePointerPilfer =
267                 context.getResources().getBoolean(R.bool.config_backAnimationRequiresPointerPilfer);
268         shellInit.addInitCallback(this::onInit, this);
269         mAnimationBackground = backAnimationBackground;
270         mShellBackAnimationRegistry = shellBackAnimationRegistry;
271         mLatencyTracker = LatencyTracker.getInstance(mContext);
272         mShellCommandHandler = shellCommandHandler;
273         mWindowManager = context.getSystemService(WindowManager.class);
274         mTransitions = transitions;
275         mBackTransitionHandler = new BackTransitionHandler(mTransitions);
276         mTransitions.addHandler(mBackTransitionHandler);
277         mHandler = handler;
278         mTransitions.registerObserver(mBackTransitionObserver);
279         mBackTransitionObserver.setBackTransitionHandler(mBackTransitionHandler);
280         updateTouchableArea();
281     }
282 
onInit()283     private void onInit() {
284         createAdapter();
285         mShellController.addExternalInterface(IBackAnimation.DESCRIPTOR,
286                 this::createExternalInterface, this);
287         mShellCommandHandler.addDumpCallback(this::dump, this);
288         mShellController.addConfigurationChangeListener(this);
289         registerBackGestureDelegate();
290     }
291 
getBackAnimationImpl()292     public BackAnimation getBackAnimationImpl() {
293         return mBackAnimation;
294     }
295 
createExternalInterface()296     private ExternalInterfaceBinder createExternalInterface() {
297         return new IBackAnimationImpl(this);
298     }
299 
300     private final BackAnimationImpl mBackAnimation = new BackAnimationImpl();
301 
302     @Override
onConfigurationChanged(Configuration newConfig)303     public void onConfigurationChanged(Configuration newConfig) {
304         mShellBackAnimationRegistry.onConfigurationChanged(newConfig);
305         updateTouchableArea();
306     }
307 
updateTouchableArea()308     private void updateTouchableArea() {
309         mTouchableArea.set(mWindowManager.getCurrentWindowMetrics().getBounds());
310     }
311 
312     @Override
getContext()313     public Context getContext() {
314         return mContext;
315     }
316 
317     @Override
getRemoteCallExecutor()318     public ShellExecutor getRemoteCallExecutor() {
319         return mShellExecutor;
320     }
321 
322     private class BackAnimationImpl implements BackAnimation {
323         @Override
onBackMotion( float touchX, float touchY, int keyAction, @BackEvent.SwipeEdge int swipeEdge )324         public void onBackMotion(
325                 float touchX,
326                 float touchY,
327                 int keyAction,
328                 @BackEvent.SwipeEdge int swipeEdge
329         ) {
330             mShellExecutor.execute(
331                     () -> onMotionEvent(touchX, touchY, keyAction, swipeEdge));
332         }
333 
334         @Override
onThresholdCrossed()335         public void onThresholdCrossed() {
336             if (predictiveBackDelayWmTransition()) {
337                 mShellExecutor.execute(BackAnimationController.this::onThresholdCrossed);
338             } else {
339                 BackAnimationController.this.onThresholdCrossed();
340             }
341         }
342 
343         @Override
setTriggerBack(boolean triggerBack)344         public void setTriggerBack(boolean triggerBack) {
345             mShellExecutor.execute(() -> BackAnimationController.this.setTriggerBack(triggerBack));
346         }
347 
348         @Override
setSwipeThresholds( float linearDistance, float maxDistance, float nonLinearFactor)349         public void setSwipeThresholds(
350                 float linearDistance,
351                 float maxDistance,
352                 float nonLinearFactor) {
353             mShellExecutor.execute(() -> BackAnimationController.this.setSwipeThresholds(
354                     linearDistance, maxDistance, nonLinearFactor));
355         }
356 
357         @Override
setStatusBarCustomizer(StatusBarCustomizer customizer)358         public void setStatusBarCustomizer(StatusBarCustomizer customizer) {
359             mCustomizer = customizer;
360             mAnimationBackground.setStatusBarCustomizer(customizer);
361         }
362 
363         @Override
setPilferPointerCallback(Runnable callback)364         public void setPilferPointerCallback(Runnable callback) {
365             mShellExecutor.execute(() -> {
366                 mPilferPointerCallback = callback;
367             });
368         }
369 
370         @Override
setTopUiRequestCallback(TopUiRequest topUiRequest)371         public void setTopUiRequestCallback(TopUiRequest topUiRequest) {
372             mShellExecutor.execute(() -> mRequestTopUiCallback = topUiRequest);
373         }
374     }
375 
376     private class IBackAnimationImpl extends IBackAnimation.Stub
377             implements ExternalInterfaceBinder {
378         private BackAnimationController mController;
379 
IBackAnimationImpl(BackAnimationController controller)380         IBackAnimationImpl(BackAnimationController controller) {
381             mController = controller;
382         }
383 
384         @Override
setBackToLauncherCallback(IOnBackInvokedCallback callback, IRemoteAnimationRunner runner)385         public void setBackToLauncherCallback(IOnBackInvokedCallback callback,
386                 IRemoteAnimationRunner runner) {
387             executeRemoteCallWithTaskPermission(mController, "setBackToLauncherCallback",
388                     (controller) -> controller.registerAnimation(
389                             BackNavigationInfo.TYPE_RETURN_TO_HOME,
390                             new BackAnimationRunner(
391                                     callback,
392                                     runner,
393                                     controller.mContext,
394                                     CUJ_PREDICTIVE_BACK_HOME,
395                                     mHandler)));
396         }
397 
398         @Override
clearBackToLauncherCallback()399         public void clearBackToLauncherCallback() {
400             executeRemoteCallWithTaskPermission(mController, "clearBackToLauncherCallback",
401                     (controller) -> controller.unregisterAnimation(
402                             BackNavigationInfo.TYPE_RETURN_TO_HOME));
403         }
404 
customizeStatusBarAppearance(AppearanceRegion appearance)405         public void customizeStatusBarAppearance(AppearanceRegion appearance) {
406             executeRemoteCallWithTaskPermission(mController, "useLauncherSysBarFlags",
407                     (controller) -> controller.customizeStatusBarAppearance(appearance));
408         }
409 
410         @Override
invalidate()411         public void invalidate() {
412             mController = null;
413         }
414     }
415 
customizeStatusBarAppearance(AppearanceRegion appearance)416     private void customizeStatusBarAppearance(AppearanceRegion appearance) {
417         if (mCustomizer != null) {
418             mCustomizer.customizeStatusBarAppearance(appearance);
419         }
420     }
421 
registerAnimation(@ackNavigationInfo.BackTargetType int type, @NonNull BackAnimationRunner runner)422     void registerAnimation(@BackNavigationInfo.BackTargetType int type,
423             @NonNull BackAnimationRunner runner) {
424         mShellBackAnimationRegistry.registerAnimation(type, runner);
425     }
426 
unregisterAnimation(@ackNavigationInfo.BackTargetType int type)427     void unregisterAnimation(@BackNavigationInfo.BackTargetType int type) {
428         mShellBackAnimationRegistry.unregisterAnimation(type);
429     }
430 
getActiveTracker()431     private BackTouchTracker getActiveTracker() {
432         if (mCurrentTracker.isActive()) return mCurrentTracker;
433         if (mQueuedTracker.isActive()) return mQueuedTracker;
434         return null;
435     }
436 
437     @VisibleForTesting
onThresholdCrossed()438     public void onThresholdCrossed() {
439         mThresholdCrossed = true;
440         BackTouchTracker activeTracker = getActiveTracker();
441         if (predictiveBackDelayWmTransition() && activeTracker != null && mActiveCallback == null
442                 && mBackGestureStarted) {
443             startBackNavigation(activeTracker);
444         }
445         // There was no focus window when calling startBackNavigation, still pilfer pointers so
446         // the next focus window won't receive motion events.
447         if (mBackNavigationInfo == null && mReceivedNullNavigationInfo) {
448             tryPilferPointers();
449             return;
450         }
451         // Dispatch onBackStarted, only to app callbacks.
452         // System callbacks will receive onBackStarted when the remote animation starts.
453         final boolean shouldDispatchToAnimator = shouldDispatchToAnimator();
454         if (!shouldDispatchToAnimator && mActiveCallback != null) {
455             mCurrentTracker.updateStartLocation();
456             tryDispatchOnBackStarted(mActiveCallback, mCurrentTracker.createStartEvent(null));
457             if (mBackNavigationInfo != null && !isAppProgressGenerationAllowed()) {
458                 tryPilferPointers();
459             }
460         } else if (shouldDispatchToAnimator) {
461             tryPilferPointers();
462         }
463     }
464 
isAppProgressGenerationAllowed()465     private boolean isAppProgressGenerationAllowed() {
466         return mBackNavigationInfo.isAppProgressGenerationAllowed()
467                 && mBackNavigationInfo.getTouchableRegion().equals(mTouchableArea);
468     }
469 
470     /**
471      * Called when a new motion event needs to be transferred to this
472      * {@link BackAnimationController}
473      */
onMotionEvent( float touchX, float touchY, int keyAction, @BackEvent.SwipeEdge int swipeEdge)474     public void onMotionEvent(
475             float touchX,
476             float touchY,
477             int keyAction,
478             @BackEvent.SwipeEdge int swipeEdge) {
479 
480         BackTouchTracker activeTouchTracker = getActiveTracker();
481         if (activeTouchTracker != null) {
482             activeTouchTracker.update(touchX, touchY);
483         }
484 
485         // two gestures are waiting to be processed at the moment, skip any further user touches
486         if (mCurrentTracker.isFinished() && mQueuedTracker.isFinished()) {
487             ProtoLog.d(WM_SHELL_BACK_PREVIEW,
488                     "Ignoring MotionEvent because two gestures are already being queued.");
489             return;
490         } else if (mBackGestureStarted && mCurrentTracker.isInInitialState()
491                 && mQueuedTracker.isInInitialState()) {
492             ProtoLog.e(WM_SHELL_BACK_PREVIEW,
493                     "Both touch trackers in initial state and mBackGestureStarted=true");
494             mBackGestureStarted = false;
495         }
496 
497         if (keyAction == MotionEvent.ACTION_DOWN) {
498             if (!mBackGestureStarted) {
499                 if (swipeEdge == EDGE_NONE) {
500                     // start animation immediately for non-gestural sources (without ACTION_MOVE
501                     // events)
502                     if (!predictiveBackDelayWmTransition()) {
503                         mThresholdCrossed = true;
504                     }
505                     mPointersPilfered = true;
506                     onGestureStarted(touchX, touchY, swipeEdge);
507                     if (predictiveBackDelayWmTransition()) {
508                         onThresholdCrossed();
509                     }
510                     mShouldStartOnNextMoveEvent = false;
511                 } else {
512                     if (predictiveBackDelayWmTransition()) {
513                         onGestureStarted(touchX, touchY, swipeEdge);
514                     } else {
515                         mShouldStartOnNextMoveEvent = true;
516                     }
517                 }
518             }
519         } else if (keyAction == MotionEvent.ACTION_MOVE) {
520             if (!mBackGestureStarted && mShouldStartOnNextMoveEvent) {
521                 // Let the animation initialized here to make sure the onPointerDownOutsideFocus
522                 // could be happened when ACTION_DOWN, it may change the current focus that we
523                 // would access it when startBackNavigation.
524                 onGestureStarted(touchX, touchY, swipeEdge);
525                 mShouldStartOnNextMoveEvent = false;
526             }
527             onMove();
528         } else if (keyAction == MotionEvent.ACTION_UP || keyAction == MotionEvent.ACTION_CANCEL) {
529             ProtoLog.d(WM_SHELL_BACK_PREVIEW,
530                     "Finishing gesture with event action: %d", keyAction);
531             if (keyAction == MotionEvent.ACTION_CANCEL) {
532                 setTriggerBack(false);
533             }
534             onGestureFinished();
535         }
536     }
537 
onGestureStarted(float touchX, float touchY, @BackEvent.SwipeEdge int swipeEdge)538     private void onGestureStarted(float touchX, float touchY, @BackEvent.SwipeEdge int swipeEdge) {
539         boolean interruptCancelPostCommitAnimation = mPostCommitAnimationInProgress
540                 && mCurrentTracker.isFinished() && !mCurrentTracker.getTriggerBack()
541                 && mQueuedTracker.isInInitialState();
542         if (interruptCancelPostCommitAnimation) {
543             // If a system animation is currently in the post-commit phase animating an
544             // onBackCancelled event, let's interrupt it and start animating a new back gesture
545             resetTouchTracker();
546         }
547         BackTouchTracker touchTracker;
548         if (mCurrentTracker.isInInitialState()) {
549             touchTracker = mCurrentTracker;
550         } else if (mQueuedTracker.isInInitialState()) {
551             touchTracker = mQueuedTracker;
552         } else {
553             ProtoLog.w(WM_SHELL_BACK_PREVIEW,
554                     "Cannot start tracking new gesture with neither tracker in initial state.");
555             return;
556         }
557         touchTracker.setGestureStartLocation(touchX, touchY, swipeEdge);
558         touchTracker.setState(BackTouchTracker.TouchTrackerState.ACTIVE);
559         mBackGestureStarted = true;
560 
561         if (interruptCancelPostCommitAnimation) {
562             // post-commit cancel is currently running. let's interrupt it and dispatch a new
563             // onBackStarted event.
564             mPostCommitAnimationInProgress = false;
565             mShellExecutor.removeCallbacks(mAnimationTimeoutRunnable);
566             startSystemAnimation();
567         } else if (!predictiveBackDelayWmTransition()) {
568             startBackNavigation(touchTracker);
569         }
570     }
571 
startBackNavigation(@onNull BackTouchTracker touchTracker)572     private void startBackNavigation(@NonNull BackTouchTracker touchTracker) {
573         if (touchTracker != mCurrentTracker) {
574             // Only start the back navigation if no other gesture is being processed. Otherwise,
575             // the back navigation will fall back to legacy back event injection.
576             return;
577         }
578         try {
579             startLatencyTracking();
580             if (mBackAnimationAdapter != null
581                     && mShellBackAnimationRegistry.hasSupportedAnimatorsChanged()) {
582                 mBackAnimationAdapter.updateSupportedAnimators(
583                         mShellBackAnimationRegistry.getSupportedAnimators());
584             }
585             mBackNavigationInfo = mActivityTaskManager.startBackNavigation(
586                     mNavigationObserver, mBackAnimationAdapter);
587             onBackNavigationInfoReceived(mBackNavigationInfo, touchTracker);
588         } catch (RemoteException remoteException) {
589             Log.e(TAG, "Failed to initAnimation", remoteException);
590             finishBackNavigation(touchTracker.getTriggerBack());
591         }
592     }
593 
onBackNavigationInfoReceived(@ullable BackNavigationInfo backNavigationInfo, @NonNull BackTouchTracker touchTracker)594     private void onBackNavigationInfoReceived(@Nullable BackNavigationInfo backNavigationInfo,
595             @NonNull BackTouchTracker touchTracker) {
596         ProtoLog.d(WM_SHELL_BACK_PREVIEW, "Received backNavigationInfo:%s", backNavigationInfo);
597         if (backNavigationInfo == null) {
598             ProtoLog.e(WM_SHELL_BACK_PREVIEW, "Received BackNavigationInfo is null.");
599             mReceivedNullNavigationInfo = true;
600             cancelLatencyTracking();
601             tryPilferPointers();
602             return;
603         }
604         final int backType = backNavigationInfo.getType();
605         final boolean shouldDispatchToAnimator = shouldDispatchToAnimator();
606         if (shouldDispatchToAnimator) {
607             if (!mShellBackAnimationRegistry.startGesture(backType)) {
608                 mActiveCallback = null;
609             }
610             requestTopUi(true, backType);
611             tryPilferPointers();
612         } else {
613             mActiveCallback = mBackNavigationInfo.getOnBackInvokedCallback();
614             // App is handling back animation. Cancel system animation latency tracking.
615             cancelLatencyTracking();
616             tryDispatchOnBackStarted(mActiveCallback, touchTracker.createStartEvent(null));
617             if (!isAppProgressGenerationAllowed()) {
618                 tryPilferPointers();
619             }
620         }
621     }
622 
onMove()623     private void onMove() {
624         if (!mBackGestureStarted
625                 || mBackNavigationInfo == null
626                 || mActiveCallback == null
627                 || !mOnBackStartDispatched) {
628             return;
629         }
630         // Skip dispatching if the move corresponds to the queued instead of the current gesture
631         if (mQueuedTracker.isActive()) return;
632         final BackMotionEvent backEvent = mCurrentTracker.createProgressEvent();
633         dispatchOnBackProgressed(mActiveCallback, backEvent);
634     }
635 
injectBackKey()636     private void injectBackKey() {
637         ProtoLog.d(WM_SHELL_BACK_PREVIEW, "injectBackKey");
638         sendBackEvent(KeyEvent.ACTION_DOWN);
639         sendBackEvent(KeyEvent.ACTION_UP);
640     }
641 
642     @SuppressLint("MissingPermission")
sendBackEvent(int action)643     private void sendBackEvent(int action) {
644         final long when = SystemClock.uptimeMillis();
645         final KeyEvent ev = new KeyEvent(when, when, action, KeyEvent.KEYCODE_BACK, 0 /* repeat */,
646                 0 /* metaState */, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /* scancode */,
647                 KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
648                 InputDevice.SOURCE_KEYBOARD);
649 
650         ev.setDisplayId(mContext.getDisplay().getDisplayId());
651         if (!mContext.getSystemService(InputManager.class)
652                 .injectInputEvent(ev, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC)) {
653             ProtoLog.e(WM_SHELL_BACK_PREVIEW, "Inject input event fail");
654         }
655     }
656 
shouldDispatchToAnimator()657     private boolean shouldDispatchToAnimator() {
658         return mBackNavigationInfo != null && mBackNavigationInfo.isPrepareRemoteAnimation();
659     }
660 
tryPilferPointers()661     private void tryPilferPointers() {
662         if (mPointersPilfered || !mThresholdCrossed) {
663             return;
664         }
665         if (mPilferPointerCallback != null) {
666             mPilferPointerCallback.run();
667         }
668         mPointersPilfered = true;
669     }
670 
tryDispatchOnBackStarted( IOnBackInvokedCallback callback, BackMotionEvent backEvent)671     private void tryDispatchOnBackStarted(
672             IOnBackInvokedCallback callback,
673             BackMotionEvent backEvent) {
674         if (mOnBackStartDispatched
675                 || callback == null
676                 || (!mThresholdCrossed && mRequirePointerPilfer)) {
677             return;
678         }
679         dispatchOnBackStarted(callback, backEvent);
680     }
681 
dispatchOnBackStarted( IOnBackInvokedCallback callback, BackMotionEvent backEvent)682     private void dispatchOnBackStarted(
683             IOnBackInvokedCallback callback,
684             BackMotionEvent backEvent) {
685         if (callback == null) {
686             return;
687         }
688         try {
689             callback.onBackStarted(backEvent);
690             if (mBackTransitionHandler.canHandOffAnimation()) {
691                 callback.setHandoffHandler(mHandoffHandler);
692             } else {
693                 callback.setHandoffHandler(null);
694             }
695             mOnBackStartDispatched = true;
696         } catch (RemoteException e) {
697             Log.e(TAG, "dispatchOnBackStarted error: ", e);
698         }
699     }
700 
dispatchOnBackInvoked(IOnBackInvokedCallback callback)701     private void dispatchOnBackInvoked(IOnBackInvokedCallback callback) {
702         if (callback == null) {
703             return;
704         }
705         try {
706             callback.onBackInvoked();
707         } catch (RemoteException e) {
708             Log.e(TAG, "dispatchOnBackInvoked error: ", e);
709         }
710     }
711 
tryDispatchOnBackCancelled(IOnBackInvokedCallback callback)712     private void tryDispatchOnBackCancelled(IOnBackInvokedCallback callback) {
713         if (!mOnBackStartDispatched) {
714             Log.d(TAG, "Skipping dispatching onBackCancelled. Start was never dispatched.");
715             return;
716         }
717         if (callback == null) {
718             return;
719         }
720         try {
721             callback.onBackCancelled();
722         } catch (RemoteException e) {
723             Log.e(TAG, "dispatchOnBackCancelled error: ", e);
724         }
725     }
726 
dispatchOnBackProgressed(IOnBackInvokedCallback callback, BackMotionEvent backEvent)727     private void dispatchOnBackProgressed(IOnBackInvokedCallback callback,
728             BackMotionEvent backEvent) {
729         if (callback == null || (!shouldDispatchToAnimator() && mBackNavigationInfo != null
730                 && isAppProgressGenerationAllowed())) {
731             return;
732         }
733         try {
734             callback.onBackProgressed(backEvent);
735         } catch (RemoteException e) {
736             Log.e(TAG, "dispatchOnBackProgressed error: ", e);
737         }
738     }
739 
740     /**
741      * @return Latest task id which back gesture has occurred on it.
742      */
getLatestTriggerBackTask()743     public int getLatestTriggerBackTask() {
744         return mBackTransitionObserver.mFocusedTaskId;
745     }
746 
747     /**
748      * Sets to true when the back gesture has passed the triggering threshold, false otherwise.
749      */
setTriggerBack(boolean triggerBack)750     public void setTriggerBack(boolean triggerBack) {
751         if (mActiveCallback != null) {
752             try {
753                 mActiveCallback.setTriggerBack(triggerBack);
754             } catch (RemoteException e) {
755                 Log.e(TAG, "remote setTriggerBack error: ", e);
756             }
757         }
758         BackTouchTracker activeBackGestureInfo = getActiveTracker();
759         if (activeBackGestureInfo != null) {
760             activeBackGestureInfo.setTriggerBack(triggerBack);
761         }
762     }
763 
setSwipeThresholds( float linearDistance, float maxDistance, float nonLinearFactor)764     private void setSwipeThresholds(
765             float linearDistance,
766             float maxDistance,
767             float nonLinearFactor) {
768         mCurrentTracker.setProgressThresholds(linearDistance, maxDistance, nonLinearFactor);
769         mQueuedTracker.setProgressThresholds(linearDistance, maxDistance, nonLinearFactor);
770     }
771 
invokeOrCancelBack(@onNull BackTouchTracker touchTracker)772     private void invokeOrCancelBack(@NonNull BackTouchTracker touchTracker) {
773         // Make a synchronized call to core before dispatch back event to client side.
774         // If the close transition happens before the core receives onAnimationFinished, there will
775         // play a second close animation for that transition.
776         if (mBackAnimationFinishedCallback != null) {
777             try {
778                 mBackAnimationFinishedCallback.onAnimationFinished(touchTracker.getTriggerBack());
779             } catch (RemoteException e) {
780                 Log.e(TAG, "Failed call IBackAnimationFinishedCallback", e);
781             }
782             mBackAnimationFinishedCallback = null;
783         }
784 
785         if (mBackNavigationInfo != null && !mRealCallbackInvoked) {
786             final IOnBackInvokedCallback callback = mBackNavigationInfo.getOnBackInvokedCallback();
787             if (touchTracker.getTriggerBack()) {
788                 dispatchOnBackInvoked(callback);
789             } else {
790                 tryDispatchOnBackCancelled(callback);
791             }
792         }
793         mRealCallbackInvoked = false;
794         finishBackNavigation(touchTracker.getTriggerBack());
795     }
796 
797     /**
798      * Called when the gesture is released, then it could start the post commit animation.
799      */
onGestureFinished()800     private void onGestureFinished() {
801         BackTouchTracker activeTouchTracker = getActiveTracker();
802         if (!mBackGestureStarted || activeTouchTracker == null) {
803             // This can happen when an unfinished gesture has been reset in resetTouchTracker
804             ProtoLog.d(WM_SHELL_BACK_PREVIEW,
805                     "onGestureFinished called while no gesture is started");
806             return;
807         }
808         boolean triggerBack = activeTouchTracker.getTriggerBack();
809         ProtoLog.d(WM_SHELL_BACK_PREVIEW, "onGestureFinished() mTriggerBack == %s", triggerBack);
810 
811         if (triggerBack) {
812             mBackTransitionObserver.update(mBackNavigationInfo != null
813                             ? mBackNavigationInfo.getFocusedTaskId()
814                             : INVALID_TASK_ID);
815         }
816         // Reset gesture states.
817         mThresholdCrossed = false;
818         mPointersPilfered = false;
819         mBackGestureStarted = false;
820         activeTouchTracker.setState(BackTouchTracker.TouchTrackerState.FINISHED);
821 
822         if (mPostCommitAnimationInProgress) {
823             ProtoLog.w(WM_SHELL_BACK_PREVIEW, "Animation is still running");
824             return;
825         }
826 
827         if (mBackNavigationInfo == null) {
828             // No focus window found or core are running recents animation, inject back key as
829             // legacy behavior, or new back gesture was started while previous has not finished yet
830             if (!mQueuedTracker.isInInitialState()) {
831                 ProtoLog.e(WM_SHELL_BACK_PREVIEW, "mBackNavigationInfo is null AND there is "
832                         + "another back animation in progress");
833             }
834             mCurrentTracker.reset();
835             if (triggerBack) {
836                 injectBackKey();
837             }
838             finishBackNavigation(triggerBack);
839             return;
840         }
841 
842         final int backType = mBackNavigationInfo.getType();
843         // Simply trigger and finish back navigation when no animator defined.
844         if (!shouldDispatchToAnimator()
845                 || mShellBackAnimationRegistry.isAnimationCancelledOrNull(backType)) {
846             ProtoLog.d(WM_SHELL_BACK_PREVIEW, "Trigger back without dispatching to animator.");
847             invokeOrCancelBack(mCurrentTracker);
848             mCurrentTracker.reset();
849             return;
850         } else if (mShellBackAnimationRegistry.isWaitingAnimation(backType)) {
851             ProtoLog.w(WM_SHELL_BACK_PREVIEW, "Gesture released, but animation didn't ready.");
852             // Supposed it is in post commit animation state, and start the timeout to watch
853             // if the animation is ready.
854             mShellExecutor.executeDelayed(mAnimationTimeoutRunnable, MAX_ANIMATION_DURATION);
855             return;
856         }
857         startPostCommitAnimation();
858     }
859 
860     /**
861      * Start the phase 2 animation when gesture is released.
862      * Callback to {@link #onBackAnimationFinished} when it is finished or timeout.
863      */
startPostCommitAnimation()864     private void startPostCommitAnimation() {
865         if (mPostCommitAnimationInProgress) {
866             return;
867         }
868 
869         mShellExecutor.removeCallbacks(mAnimationTimeoutRunnable);
870         ProtoLog.d(WM_SHELL_BACK_PREVIEW, "BackAnimationController: startPostCommitAnimation()");
871         mPostCommitAnimationInProgress = true;
872         mShellExecutor.executeDelayed(mAnimationTimeoutRunnable, MAX_ANIMATION_DURATION);
873 
874         // The next callback should be {@link #onBackAnimationFinished}.
875         if (mCurrentTracker.getTriggerBack()) {
876             // notify core gesture is commit
877             if (shouldTriggerCloseTransition()) {
878                 mBackTransitionHandler.mCloseTransitionRequested = true;
879                 final IOnBackInvokedCallback callback =
880                         mBackNavigationInfo.getOnBackInvokedCallback();
881                 // invoked client side onBackInvoked
882                 dispatchOnBackInvoked(callback);
883                 mRealCallbackInvoked = true;
884             }
885             // start post animation
886             dispatchOnBackInvoked(mActiveCallback);
887         } else {
888             tryDispatchOnBackCancelled(mActiveCallback);
889         }
890     }
891 
892     // Close window won't create any transition
shouldTriggerCloseTransition()893     private boolean shouldTriggerCloseTransition() {
894         if (mBackNavigationInfo == null) {
895             return false;
896         }
897         int type = mBackNavigationInfo.getType();
898         return type == BackNavigationInfo.TYPE_RETURN_TO_HOME
899                 || type == BackNavigationInfo.TYPE_CROSS_TASK
900                 || type == BackNavigationInfo.TYPE_CROSS_ACTIVITY;
901     }
902     /**
903      * Called when the post commit animation is completed or timeout.
904      * This will trigger the real {@link IOnBackInvokedCallback} behavior.
905      */
906     @VisibleForTesting
onBackAnimationFinished()907     void onBackAnimationFinished() {
908         if (!mPostCommitAnimationInProgress) {
909             // This can happen when a post-commit cancel animation was interrupted by a new back
910             // gesture but the timing of interruption was bad such that the back-callback
911             // implementation finished in between the time of the new gesture having started and
912             // the time of the back-callback receiving the new onBackStarted event. Due to the
913             // asynchronous APIs this isn't an unlikely case. To handle this, let's return early.
914             // The back-callback implementation will call onBackAnimationFinished again when it is
915             // done with animating the second gesture.
916             return;
917         }
918         finishBackAnimation();
919     }
920 
finishBackAnimation()921     private void finishBackAnimation() {
922         // Stop timeout runner.
923         mShellExecutor.removeCallbacks(mAnimationTimeoutRunnable);
924         mPostCommitAnimationInProgress = false;
925 
926         ProtoLog.d(WM_SHELL_BACK_PREVIEW, "BackAnimationController: onBackAnimationFinished()");
927 
928         if (mCurrentTracker.isActive() || mCurrentTracker.isFinished()) {
929             // Trigger the real back.
930             invokeOrCancelBack(mCurrentTracker);
931         } else {
932             ProtoLog.d(WM_SHELL_BACK_PREVIEW,
933                     "mCurrentBackGestureInfo was null when back animation finished");
934         }
935         resetTouchTracker();
936         mBackTransitionHandler.onAnimationFinished();
937     }
938 
939     /**
940      * Resets the BackTouchTracker and potentially starts a new back navigation in case one
941      * is queued.
942      */
resetTouchTracker()943     private void resetTouchTracker() {
944         BackTouchTracker temp = mCurrentTracker;
945         mCurrentTracker = mQueuedTracker;
946         temp.reset();
947         mQueuedTracker = temp;
948 
949         if (mCurrentTracker.isInInitialState()) {
950             if (mBackGestureStarted) {
951                 mBackGestureStarted = false;
952                 tryDispatchOnBackCancelled(mActiveCallback);
953                 finishBackNavigation(false);
954                 ProtoLog.d(WM_SHELL_BACK_PREVIEW,
955                         "resetTouchTracker -> reset an unfinished gesture");
956             } else {
957                 ProtoLog.d(WM_SHELL_BACK_PREVIEW, "resetTouchTracker -> no queued gesture");
958             }
959             return;
960         }
961 
962         if (mCurrentTracker.isFinished() && mCurrentTracker.getTriggerBack()) {
963             ProtoLog.d(WM_SHELL_BACK_PREVIEW, "resetTouchTracker -> start queued back navigation "
964                     + "AND post commit animation");
965             injectBackKey();
966             finishBackNavigation(true);
967             mCurrentTracker.reset();
968         } else if (!mCurrentTracker.isFinished()) {
969             ProtoLog.d(WM_SHELL_BACK_PREVIEW,
970                     "resetTouchTracker -> queued gesture not finished; do nothing");
971         } else {
972             ProtoLog.d(WM_SHELL_BACK_PREVIEW, "resetTouchTracker -> reset queued gesture");
973             mCurrentTracker.reset();
974         }
975     }
976 
977     /**
978      * This should be called after the whole back navigation is completed.
979      */
980     @VisibleForTesting
finishBackNavigation(boolean triggerBack)981     void finishBackNavigation(boolean triggerBack) {
982         ProtoLog.d(WM_SHELL_BACK_PREVIEW, "BackAnimationController: finishBackNavigation()");
983         mActiveCallback = null;
984         mApps = null;
985         mOnBackStartDispatched = false;
986         mThresholdCrossed = false;
987         mPointersPilfered = false;
988         mShellBackAnimationRegistry.resetDefaultCrossActivity();
989         cancelLatencyTracking();
990         mReceivedNullNavigationInfo = false;
991         if (mBackNavigationInfo != null) {
992             mPreviousNavigationType = mBackNavigationInfo.getType();
993             mBackNavigationInfo.onBackNavigationFinished(triggerBack);
994             mBackNavigationInfo = null;
995             requestTopUi(false, mPreviousNavigationType);
996         }
997     }
998 
startLatencyTracking()999     private void startLatencyTracking() {
1000         if (mTrackingLatency) {
1001             cancelLatencyTracking();
1002         }
1003         mLatencyTracker.onActionStart(LatencyTracker.ACTION_BACK_SYSTEM_ANIMATION);
1004         mTrackingLatency = true;
1005     }
1006 
cancelLatencyTracking()1007     private void cancelLatencyTracking() {
1008         if (!mTrackingLatency) {
1009             return;
1010         }
1011         mLatencyTracker.onActionCancel(LatencyTracker.ACTION_BACK_SYSTEM_ANIMATION);
1012         mTrackingLatency = false;
1013     }
1014 
endLatencyTracking()1015     private void endLatencyTracking() {
1016         if (!mTrackingLatency) {
1017             return;
1018         }
1019         mLatencyTracker.onActionEnd(LatencyTracker.ACTION_BACK_SYSTEM_ANIMATION);
1020         mTrackingLatency = false;
1021     }
1022 
startSystemAnimation()1023     private void startSystemAnimation() {
1024         if (mBackNavigationInfo == null) {
1025             ProtoLog.e(WM_SHELL_BACK_PREVIEW, "Lack of navigation info to start animation.");
1026             return;
1027         }
1028         if (!validateAnimationTargets(mApps)) {
1029             ProtoLog.w(WM_SHELL_BACK_PREVIEW, "Not starting animation due to mApps being null.");
1030             return;
1031         }
1032 
1033         final BackAnimationRunner runner =
1034                 mShellBackAnimationRegistry.getAnimationRunnerAndInit(mBackNavigationInfo);
1035         if (runner == null) {
1036             if (mBackAnimationFinishedCallback != null) {
1037                 try {
1038                     mBackAnimationFinishedCallback.onAnimationFinished(false);
1039                 } catch (RemoteException e) {
1040                     Log.w(TAG, "Failed call IBackNaviAnimationController", e);
1041                 }
1042             }
1043             return;
1044         }
1045         mActiveCallback = runner.getCallback();
1046 
1047         ProtoLog.d(WM_SHELL_BACK_PREVIEW, "BackAnimationController: startAnimation()");
1048 
1049         runner.startAnimation(mApps, /*wallpapers*/ null, /*nonApps*/ null,
1050                 () -> mShellExecutor.execute(this::onBackAnimationFinished));
1051 
1052         if (mApps.length >= 1) {
1053             BackMotionEvent startEvent = mCurrentTracker.createStartEvent(
1054                     Flags.removeDepartTargetFromMotion() ? null : mApps[0]);
1055             dispatchOnBackStarted(mActiveCallback, startEvent);
1056             if (startEvent.getSwipeEdge() == EDGE_NONE) {
1057                 // TODO(b/373544911): onBackStarted is dispatched here so that
1058                 //  WindowOnBackInvokedDispatcher knows about the back navigation and intercepts
1059                 //  touch events while it's active. It would be cleaner and safer to disable
1060                 //  multitouch altogether (same as in gesture-nav).
1061                 dispatchOnBackStarted(mBackNavigationInfo.getOnBackInvokedCallback(), startEvent);
1062             }
1063         }
1064     }
1065 
requestTopUi(boolean hasTopUi, int backType)1066     private void requestTopUi(boolean hasTopUi, int backType) {
1067         if (mRequestTopUiCallback != null && (backType == BackNavigationInfo.TYPE_CROSS_TASK
1068                 || backType == BackNavigationInfo.TYPE_CROSS_ACTIVITY)) {
1069             mRequestTopUiCallback.requestTopUi(hasTopUi, TAG);
1070         }
1071     }
1072 
1073     /**
1074      * Validate animation targets.
1075      */
validateAnimationTargets(RemoteAnimationTarget[] apps)1076     static boolean validateAnimationTargets(RemoteAnimationTarget[] apps) {
1077         if (apps == null || apps.length == 0) {
1078             return false;
1079         }
1080         for (int i = apps.length - 1; i >= 0; --i) {
1081             if (!apps[i].leash.isValid()) {
1082                 return false;
1083             }
1084         }
1085         return true;
1086     }
1087 
kickStartAnimation()1088     private void kickStartAnimation() {
1089         startSystemAnimation();
1090 
1091         // Dispatch the first progress after animation start for
1092         // smoothing the initial animation, instead of waiting for next
1093         // onMove.
1094         final BackMotionEvent backFinish = mCurrentTracker
1095                 .createProgressEvent();
1096         dispatchOnBackProgressed(mActiveCallback, backFinish);
1097         if (mCurrentTracker.isFinished()) {
1098             // if the down -> up gesture happened before animation
1099             // start, we have to trigger the uninterruptible transition
1100             // to finish the back animation.
1101             startPostCommitAnimation();
1102         }
1103     }
1104 
createAdapter()1105     private void createAdapter() {
1106         IBackAnimationRunner runner =
1107                 new IBackAnimationRunner.Stub() {
1108                     @Override
1109                     public void onAnimationStart(
1110                             RemoteAnimationTarget[] apps,
1111                             IBinder token,
1112                             IBackAnimationFinishedCallback finishedCallback) {
1113                         mShellExecutor.execute(
1114                                 () -> {
1115                                     endLatencyTracking();
1116                                     if (!validateAnimationTargets(apps)) {
1117                                         Log.e(TAG, "Invalid animation targets!");
1118                                         return;
1119                                     }
1120                                     mBackAnimationFinishedCallback = finishedCallback;
1121                                     mApps = apps;
1122                                     // app only visible after transition ready, break for now.
1123                                     if (token != null) {
1124                                         return;
1125                                     }
1126                                     kickStartAnimation();
1127                                 });
1128                     }
1129 
1130                     @Override
1131                     public void onAnimationCancelled() {
1132                         mShellExecutor.execute(
1133                                 () -> {
1134                                     if (!mShellBackAnimationRegistry.cancel(
1135                                             mBackNavigationInfo != null
1136                                                     ? mBackNavigationInfo.getType()
1137                                                     : mPreviousNavigationType)) {
1138                                         return;
1139                                     }
1140                                     if (!mBackGestureStarted) {
1141                                         invokeOrCancelBack(mCurrentTracker);
1142                                     }
1143                                 });
1144                     }
1145                 };
1146         mBackAnimationAdapter = new BackAnimationAdapter(runner);
1147     }
1148 
registerBackGestureDelegate()1149     private void registerBackGestureDelegate() {
1150         if (!Flags.delegateBackGestureToShell()) {
1151             return;
1152         }
1153         final RemoteCallback requestBackMonitor = new RemoteCallback(
1154                 new RemoteCallback.OnResultListener() {
1155                     @Override
1156                     public void onResult(@Nullable Bundle result) {
1157                             mShellExecutor.execute(() -> {
1158                                 if (mBackGestureStarted) {
1159                                     Log.w(TAG, "Back gesture is running, ignore request");
1160                                     return;
1161                                 }
1162                                 onMotionEvent(0, 0, KeyEvent.ACTION_DOWN, EDGE_NONE);
1163                                 setTriggerBack(true);
1164                                 onMotionEvent(0, 0, KeyEvent.ACTION_UP, EDGE_NONE);
1165                             });
1166                     }
1167                 });
1168         try {
1169             mActivityTaskManager.registerBackGestureDelegate(requestBackMonitor);
1170         } catch (RemoteException remoteException) {
1171             Log.w(TAG, "Failed register back gesture request ", remoteException);
1172         }
1173     }
1174 
1175     /**
1176      * Description of current BackAnimationController state.
1177      */
dump(PrintWriter pw, String prefix)1178     private void dump(PrintWriter pw, String prefix) {
1179         pw.println(prefix + "BackAnimationController state:");
1180         pw.println(prefix + "  mBackGestureStarted=" + mBackGestureStarted);
1181         pw.println(prefix + "  mPostCommitAnimationInProgress=" + mPostCommitAnimationInProgress);
1182         pw.println(prefix + "  mShouldStartOnNextMoveEvent=" + mShouldStartOnNextMoveEvent);
1183         pw.println(prefix + "  mPointerPilfered=" + mThresholdCrossed);
1184         pw.println(prefix + "  mRequirePointerPilfer=" + mRequirePointerPilfer);
1185         pw.println(prefix + "  mCurrentTracker state:");
1186         mCurrentTracker.dump(pw, prefix + "    ");
1187         pw.println(prefix + "  mQueuedTracker state:");
1188         mQueuedTracker.dump(pw, prefix + "    ");
1189     }
1190 
1191     class BackTransitionHandler implements Transitions.TransitionHandler {
1192         private final Transitions mTransitions;
1193 
1194         Runnable mOnAnimationFinishCallback;
1195         boolean mCloseTransitionRequested;
1196         SurfaceControl.Transaction mFinishOpenTransaction;
1197         Transitions.TransitionFinishCallback mFinishOpenTransitionCallback;
1198         // The Transition to make behindActivity become visible
1199         IBinder mPrepareOpenTransition;
1200         // The Transition to make behindActivity become invisible, if prepare open exist and
1201         // animation is canceled, start a close prepare transition to finish the whole transition.
1202         IBinder mClosePrepareTransition;
1203         TransitionInfo mOpenTransitionInfo;
1204         Transitions.TransitionHandler mTakeoverHandler;
1205 
BackTransitionHandler(Transitions transitions)1206         BackTransitionHandler(Transitions transitions) {
1207             mTransitions = transitions;
1208         }
1209 
onAnimationFinished()1210         void onAnimationFinished() {
1211             if (!mCloseTransitionRequested && mPrepareOpenTransition != null) {
1212                 createClosePrepareTransition();
1213             }
1214             if (mOnAnimationFinishCallback != null) {
1215                 mOnAnimationFinishCallback.run();
1216                 mOnAnimationFinishCallback = null;
1217             }
1218         }
1219 
applyFinishOpenTransition()1220         private void applyFinishOpenTransition() {
1221             if (mFinishOpenTransaction != null) {
1222                 final SurfaceControl.Transaction t = mFinishOpenTransaction;
1223                 t.apply();
1224             }
1225             if (mFinishOpenTransitionCallback != null) {
1226                 final Transitions.TransitionFinishCallback callback = mFinishOpenTransitionCallback;
1227                 callback.onTransitionFinished(null);
1228             }
1229             cleanUpInternalState();
1230         }
1231 
cleanUpInternalState()1232         private void cleanUpInternalState() {
1233             mOpenTransitionInfo = null;
1234             mPrepareOpenTransition = null;
1235             mFinishOpenTransaction = null;
1236             mFinishOpenTransitionCallback = null;
1237             mTakeoverHandler = null;
1238         }
1239 
applyAndFinish(@onNull SurfaceControl.Transaction st, @NonNull SurfaceControl.Transaction ft, @NonNull Transitions.TransitionFinishCallback finishCallback)1240         private void applyAndFinish(@NonNull SurfaceControl.Transaction st,
1241                 @NonNull SurfaceControl.Transaction ft,
1242                 @NonNull Transitions.TransitionFinishCallback finishCallback) {
1243             applyFinishOpenTransition();
1244             st.apply();
1245             ft.apply();
1246             finishCallback.onTransitionFinished(null);
1247             mCloseTransitionRequested = false;
1248         }
1249 
1250         @Override
startAnimation(@onNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction st, @NonNull SurfaceControl.Transaction ft, @NonNull Transitions.TransitionFinishCallback finishCallback)1251         public boolean startAnimation(@NonNull IBinder transition,
1252                 @NonNull TransitionInfo info,
1253                 @NonNull SurfaceControl.Transaction st,
1254                 @NonNull SurfaceControl.Transaction ft,
1255                 @NonNull Transitions.TransitionFinishCallback finishCallback) {
1256             // Both mShellExecutor and Transitions#mMainExecutor are ShellMainThread, so we don't
1257             // need to post to ShellExecutor when called.
1258             if (info.getType() == WindowManager.TRANSIT_CLOSE_PREPARE_BACK_NAVIGATION) {
1259                 // only consume it if this transition hasn't being processed.
1260                 if (mClosePrepareTransition != null) {
1261                     mClosePrepareTransition = null;
1262                     applyAndFinish(st, ft, finishCallback);
1263                     return true;
1264                 }
1265                 return false;
1266             }
1267 
1268             if (info.getType() != WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION
1269                     && isNotGestureBackTransition(info)) {
1270                 return false;
1271             }
1272 
1273             if (shouldCancelAnimation(info)) {
1274                 mPrepareOpenTransition = null;
1275                 return false;
1276             }
1277 
1278             if (mApps == null || mApps.length == 0) {
1279                 if (mCloseTransitionRequested) {
1280                     // animation never start, consume directly
1281                     applyAndFinish(st, ft, finishCallback);
1282                     return true;
1283                 } else if (mClosePrepareTransition == null
1284                         && info.getType() == WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION) {
1285                     // Gesture animation was cancelled before prepare transition ready, create
1286                     // the close prepare transition
1287                     createClosePrepareTransition();
1288                 }
1289             }
1290 
1291             if (handlePrepareTransition(transition, info, st, ft, finishCallback)) {
1292                 if (checkTakeoverFlags()) {
1293                     mTakeoverHandler = mTransitions.getHandlerForTakeover(transition, info);
1294                 }
1295                 kickStartAnimation();
1296                 return true;
1297             }
1298             return handleCloseTransition(info, st, ft, finishCallback);
1299         }
1300 
canHandOffAnimation()1301         private boolean canHandOffAnimation() {
1302             if (!checkTakeoverFlags()) {
1303                 return false;
1304             }
1305 
1306             return mTakeoverHandler != null;
1307         }
1308 
handOffAnimation( RemoteAnimationTarget[] targets, WindowAnimationState[] states)1309         private void handOffAnimation(
1310                 RemoteAnimationTarget[] targets, WindowAnimationState[] states) {
1311             if (!checkTakeoverFlags()) {
1312                 ProtoLog.e(WM_SHELL_BACK_PREVIEW,
1313                         "Trying to hand off the animation, but the required flags are disabled.");
1314                 return;
1315             } else if (mTakeoverHandler == null) {
1316                 ProtoLog.e(WM_SHELL_BACK_PREVIEW,
1317                         "Missing takeover handler when trying to hand off animation.");
1318                 return;
1319             } else if (targets.length != states.length) {
1320                 ProtoLog.e(WM_SHELL_BACK_PREVIEW,
1321                         "Targets passed for takeover don't match the window states.");
1322                 return;
1323             }
1324 
1325             // The states passed to this method are paired with the targets, but they need to be
1326             // paired with the changes inside the TransitionInfo. So for each change we find its
1327             // matching target, and leave the state for any change missing a matching target blank.
1328             WindowAnimationState[] updatedStates =
1329                     new WindowAnimationState[mOpenTransitionInfo.getChanges().size()];
1330             for (int i = 0; i < mOpenTransitionInfo.getChanges().size(); i++) {
1331                 ActivityManager.RunningTaskInfo taskInfo =
1332                         mOpenTransitionInfo.getChanges().get(i).getTaskInfo();
1333                 if (taskInfo == null) {
1334                     continue;
1335                 }
1336 
1337                 for (int j = 0; j < targets.length; j++) {
1338                     if (taskInfo.taskId == targets[j].taskId) {
1339                         updatedStates[i] = states[j];
1340                         break;
1341                     }
1342                 }
1343             }
1344 
1345             mTakeoverHandler.takeOverAnimation(
1346                     mPrepareOpenTransition, mOpenTransitionInfo, new SurfaceControl.Transaction(),
1347                     mFinishOpenTransitionCallback, updatedStates);
1348 
1349             cleanUpInternalState();
1350         }
1351 
1352         @Override
onTransitionConsumed(@onNull IBinder transition, boolean aborted, @Nullable SurfaceControl.Transaction finishTransaction)1353         public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
1354                 @Nullable SurfaceControl.Transaction finishTransaction) {
1355             if (transition == mClosePrepareTransition && aborted) {
1356                 mClosePrepareTransition = null;
1357                 applyFinishOpenTransition();
1358             } else if (!aborted && unifyBackNavigationTransition()) {
1359                 // Since the closing target participates in the predictive back transition, the
1360                 // merged transition must be applied with the first transition to ensure a seamless
1361                 // animation.
1362                 if (mFinishOpenTransaction != null && finishTransaction != null) {
1363                     mFinishOpenTransaction.merge(finishTransaction);
1364                 }
1365             }
1366         }
1367 
createClosePrepareTransition()1368         void createClosePrepareTransition() {
1369             if (mClosePrepareTransition != null) {
1370                 Log.e(TAG, "Re-create close prepare transition");
1371                 return;
1372             }
1373             final WindowContainerTransaction wct = new WindowContainerTransaction();
1374             wct.restoreBackNavi();
1375             mClosePrepareTransition = mTransitions.startTransition(
1376                     TRANSIT_CLOSE_PREPARE_BACK_NAVIGATION, wct, mBackTransitionHandler);
1377         }
mergePendingTransitions(TransitionInfo info)1378         private void mergePendingTransitions(TransitionInfo info) {
1379             if (mOpenTransitionInfo == null) {
1380                 return;
1381             }
1382             // Copy initial changes to final transition
1383             final TransitionInfo init = mOpenTransitionInfo;
1384             // Find prepare open target
1385             boolean openShowWallpaper = false;
1386             final ArrayList<SurfaceControl> openSurfaces = new ArrayList<>();
1387             int tmpSize;
1388             for (int j = init.getChanges().size() - 1; j >= 0; --j) {
1389                 final TransitionInfo.Change change = init.getChanges().get(j);
1390                 if (change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)
1391                         && TransitionUtil.isOpeningMode(change.getMode())) {
1392                     final ComponentName openComponent = findComponentName(change);
1393                     final int openTaskId = findTaskId(change);
1394                     final WindowContainerToken openToken = findToken(change);
1395                     if (openComponent == null && openTaskId == INVALID_TASK_ID
1396                             && openToken == null) {
1397                         continue;
1398                     }
1399                     openSurfaces.add(change.getLeash());
1400                     if (change.hasFlags(FLAG_SHOW_WALLPAPER)) {
1401                         openShowWallpaper = true;
1402                     }
1403                 }
1404             }
1405             if (openSurfaces.isEmpty()) {
1406                 // This shouldn't happen, but if that happen, consume the initial transition anyway.
1407                 Log.e(TAG, "Unable to merge following transition, cannot find the gesture "
1408                         + "animated target from the open transition=" + mOpenTransitionInfo);
1409                 mOpenTransitionInfo = null;
1410                 return;
1411             }
1412             // Find first non-prepare open target
1413             boolean isOpen = false;
1414             tmpSize = info.getChanges().size();
1415             for (int j = 0; j < tmpSize; ++j) {
1416                 final TransitionInfo.Change change = info.getChanges().get(j);
1417                 if (isOpenSurfaceMatched(openSurfaces, change)) {
1418                     // This is original close target, potential be close, but cannot determine
1419                     // from it.
1420                     if (change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)) {
1421                         isOpen = !TransitionUtil.isClosingMode(change.getMode());
1422                     } else {
1423                         isOpen = TransitionUtil.isOpeningMode(change.getMode());
1424                         break;
1425                     }
1426                 }
1427             }
1428             if (!isOpen) {
1429                 // Close transition, the transition info should be:
1430                 // init info(open A & wallpaper) => init info(open A & change B & wallpaper)
1431                 // current info(close B target) => current info(change A & close B)
1432                 // remove init info(open/change A target & wallpaper)
1433                 boolean moveToTop = false;
1434                 boolean excludeOpenTarget = false;
1435                 boolean mergePredictive = false;
1436                 for (int j = info.getChanges().size() - 1; j >= 0; --j) {
1437                     final TransitionInfo.Change change = info.getChanges().get(j);
1438                     if (isOpenSurfaceMatched(openSurfaces, change)) {
1439                         if (TransitionUtil.isClosingMode(change.getMode())) {
1440                             excludeOpenTarget = true;
1441                         }
1442                         moveToTop = change.hasFlags(FLAG_MOVED_TO_TOP);
1443                         info.getChanges().remove(j);
1444                     } else if ((openShowWallpaper && change.hasFlags(FLAG_IS_WALLPAPER))) {
1445                         info.getChanges().remove(j);
1446                     } else if (!mergePredictive && TransitionUtil.isClosingMode(change.getMode())) {
1447                         mergePredictive = true;
1448                     }
1449                 }
1450                 // Ignore merge if there is no close target
1451                 if (!info.getChanges().isEmpty() && mergePredictive) {
1452                     tmpSize = init.getChanges().size();
1453                     for (int i = 0; i < tmpSize; ++i) {
1454                         final TransitionInfo.Change change = init.getChanges().get(i);
1455                         if (change.hasFlags(FLAG_IS_WALLPAPER)) {
1456                             continue;
1457                         }
1458                         if (isOpenSurfaceMatched(openSurfaces, change)) {
1459                             if (excludeOpenTarget) {
1460                                 // App has triggered another change during predictive back
1461                                 // transition, filter out predictive back target.
1462                                 continue;
1463                             }
1464                             if (moveToTop) {
1465                                 change.setFlags(change.getFlags() | FLAG_MOVED_TO_TOP);
1466                             }
1467                         } else if (Flags.unifyBackNavigationTransition()
1468                                 && change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)
1469                                 && change.getMode() == TRANSIT_CHANGE
1470                                 && isCloseChangeExist(info, change)) {
1471                             // This is the original top target, don't add it into current transition
1472                             // if it is closing.
1473                             continue;
1474                         }
1475                         info.getChanges().add(i, change);
1476                     }
1477                 }
1478             } else {
1479                 // Open transition, the transition info should be:
1480                 // init info(open A & wallpaper)
1481                 // current info(open C target + close B target + close A & wallpaper)
1482 
1483                 // If close target isn't back navigated, filter out close A & wallpaper because the
1484                 // (open C + close B) pair didn't participant prepare close
1485                 boolean nonBackOpen = false;
1486                 boolean nonBackClose = false;
1487                 tmpSize = info.getChanges().size();
1488                 for (int j = 0; j < tmpSize; ++j) {
1489                     final TransitionInfo.Change change = info.getChanges().get(j);
1490                     if (!change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)
1491                             && canBeTransitionTarget(change)) {
1492                         final int mode = change.getMode();
1493                         nonBackOpen |= TransitionUtil.isOpeningMode(mode);
1494                         nonBackClose |= TransitionUtil.isClosingMode(mode);
1495                     }
1496                 }
1497                 if (nonBackClose && nonBackOpen) {
1498                     for (int j = info.getChanges().size() - 1; j >= 0; --j) {
1499                         final TransitionInfo.Change change = info.getChanges().get(j);
1500                         if (isOpenSurfaceMatched(openSurfaces, change)) {
1501                             info.getChanges().remove(j);
1502                         } else if ((openShowWallpaper && change.hasFlags(FLAG_IS_WALLPAPER))) {
1503                             info.getChanges().remove(j);
1504                         }
1505                     }
1506                 }
1507             }
1508             ProtoLog.d(WM_SHELL_BACK_PREVIEW, "Back animation transition, merge pending "
1509                     + "transitions result=%s", info);
1510             // Only handle one merge transition request.
1511             mOpenTransitionInfo = null;
1512         }
1513 
1514         @Override
mergeAnimation(@onNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startT, @NonNull SurfaceControl.Transaction finishT, @NonNull IBinder mergeTarget, @NonNull Transitions.TransitionFinishCallback finishCallback)1515         public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
1516                 @NonNull SurfaceControl.Transaction startT,
1517                 @NonNull SurfaceControl.Transaction finishT,
1518                 @NonNull IBinder mergeTarget,
1519                 @NonNull Transitions.TransitionFinishCallback finishCallback) {
1520             if (mClosePrepareTransition == transition) {
1521                 mClosePrepareTransition = null;
1522             }
1523             // try to handle unexpected transition
1524             if (mOpenTransitionInfo != null) {
1525                 mergePendingTransitions(info);
1526             }
1527 
1528             if (info.getType() == TRANSIT_CLOSE_PREPARE_BACK_NAVIGATION
1529                     && !mCloseTransitionRequested && info.getChanges().isEmpty() && mApps == null) {
1530                 finishCallback.onTransitionFinished(null);
1531                 startT.apply();
1532                 applyFinishOpenTransition();
1533                 return;
1534             }
1535             if (isNotGestureBackTransition(info) || shouldCancelAnimation(info)
1536                     || !mCloseTransitionRequested) {
1537                 if (mPrepareOpenTransition != null) {
1538                     applyFinishOpenTransition();
1539                 }
1540                 return;
1541             }
1542             // Handle the commit transition if this handler is running the open transition.
1543             finishCallback.onTransitionFinished(null);
1544             startT.apply();
1545             if (mCloseTransitionRequested) {
1546                 if (mApps == null || mApps.length == 0) {
1547                     // animation was done
1548                     applyFinishOpenTransition();
1549                     mCloseTransitionRequested = false;
1550                 } else {
1551                     // we are animating, wait until animation finish
1552                     mOnAnimationFinishCallback = () -> {
1553                         applyFinishOpenTransition();
1554                         mCloseTransitionRequested = false;
1555                     };
1556                 }
1557             }
1558         }
1559 
1560         // Cancel close animation if something happen unexpected, let another handler to handle
shouldCancelAnimation(@onNull TransitionInfo info)1561         private boolean shouldCancelAnimation(@NonNull TransitionInfo info) {
1562             final boolean noCloseAllowed = !mCloseTransitionRequested
1563                     && info.getType() == WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION;
1564             boolean unableToHandle = false;
1565             boolean filterTargets = false;
1566             for (int i = info.getChanges().size() - 1; i >= 0; --i) {
1567                 final TransitionInfo.Change c = info.getChanges().get(i);
1568                 final boolean backGestureAnimated = c.hasFlags(FLAG_BACK_GESTURE_ANIMATED);
1569                 if (!backGestureAnimated && !c.hasFlags(FLAG_IS_WALLPAPER)) {
1570                     // something we cannot handle?
1571                     unableToHandle = true;
1572                     filterTargets = true;
1573                 } else if (noCloseAllowed && backGestureAnimated
1574                         && TransitionUtil.isClosingMode(c.getMode())) {
1575                     // Prepare back navigation shouldn't contain close change, unless top app
1576                     // request close.
1577                     unableToHandle = true;
1578                 }
1579             }
1580             if (!unableToHandle) {
1581                 return false;
1582             }
1583             if (!filterTargets) {
1584                 return true;
1585             }
1586             if (TransitionUtil.isOpeningType(info.getType())
1587                     || TransitionUtil.isClosingType(info.getType())) {
1588                 boolean removeWallpaper = false;
1589                 for (int i = info.getChanges().size() - 1; i >= 0; --i) {
1590                     final TransitionInfo.Change c = info.getChanges().get(i);
1591                     // filter out opening target, keep original closing target in this transition
1592                     if (c.hasFlags(FLAG_BACK_GESTURE_ANIMATED)
1593                             && TransitionUtil.isOpeningMode(c.getMode())) {
1594                         info.getChanges().remove(i);
1595                         removeWallpaper |= c.hasFlags(FLAG_SHOW_WALLPAPER);
1596                     }
1597                 }
1598                 if (removeWallpaper) {
1599                     for (int i = info.getChanges().size() - 1; i >= 0; --i) {
1600                         final TransitionInfo.Change c = info.getChanges().get(i);
1601                         if (c.hasFlags(FLAG_IS_WALLPAPER)) {
1602                             info.getChanges().remove(i);
1603                         }
1604                     }
1605                 }
1606             }
1607             return true;
1608         }
1609 
1610         /**
1611          * Check whether this transition is prepare for predictive back animation, which could
1612          * happen when core make an activity become visible.
1613          */
1614         @VisibleForTesting
handlePrepareTransition(@onNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction st, @NonNull SurfaceControl.Transaction ft, @NonNull Transitions.TransitionFinishCallback finishCallback)1615         boolean handlePrepareTransition(@NonNull IBinder transition,
1616                 @NonNull TransitionInfo info,
1617                 @NonNull SurfaceControl.Transaction st,
1618                 @NonNull SurfaceControl.Transaction ft,
1619                 @NonNull Transitions.TransitionFinishCallback finishCallback) {
1620             if (info.getType() != WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION) {
1621                 return false;
1622             }
1623             // Must have open target, must not have close target.
1624             if (hasAnimationInMode(info, TransitionUtil::isClosingMode)
1625                     || !hasAnimationInMode(info, TransitionUtil::isOpeningMode)) {
1626                 return false;
1627             }
1628             SurfaceControl openingLeash = null;
1629             SurfaceControl closingLeash = null;
1630             if (mApps != null) {
1631                 for (int i = mApps.length - 1; i >= 0; --i) {
1632                     if (mApps[i].mode == MODE_OPENING) {
1633                         openingLeash = mApps[i].leash;
1634                     } else if (mApps[i].mode == MODE_CLOSING) {
1635                         closingLeash = mApps[i].leash;
1636                     }
1637                 }
1638             }
1639             if (openingLeash != null && closingLeash != null) {
1640                 int rootIdx = -1;
1641                 for (int i = info.getChanges().size() - 1; i >= 0; --i) {
1642                     final TransitionInfo.Change c = info.getChanges().get(i);
1643                     if (TransitionUtil.isOpeningMode(c.getMode())) {
1644                         final Point offset = c.getEndRelOffset();
1645                         st.setPosition(c.getLeash(), offset.x, offset.y);
1646                         st.reparent(c.getLeash(), openingLeash);
1647                         st.setAlpha(c.getLeash(), 1.0f);
1648                         rootIdx = TransitionUtil.rootIndexFor(c, info);
1649                     } else if (c.hasFlags(FLAG_BACK_GESTURE_ANIMATED)
1650                             && c.getMode() == TRANSIT_CHANGE) {
1651                         st.reparent(c.getLeash(), closingLeash);
1652                     }
1653                 }
1654                 // The root leash and the leash of opening target should actually in the same level,
1655                 // but since the root leash is created after opening target, it will have higher
1656                 // layer in surface flinger. Move the root leash to lower level, so it won't affect
1657                 // the playing animation.
1658                 if (rootIdx >= 0 && info.getRootCount() > 0) {
1659                     st.setLayer(info.getRoot(rootIdx).getLeash(), -1);
1660                 }
1661             }
1662             st.apply();
1663             // In case other transition handler took the handleRequest before this class.
1664             mPrepareOpenTransition = transition;
1665             mFinishOpenTransaction = ft;
1666             mFinishOpenTransitionCallback = finishCallback;
1667             mOpenTransitionInfo = info;
1668             return true;
1669         }
1670 
1671         /**
1672          * Check whether this transition is triggered from back gesture commitment.
1673          * Reparent the transition targets to animation leashes, so the animation won't be broken.
1674          */
1675         @VisibleForTesting
handleCloseTransition(@onNull TransitionInfo info, @NonNull SurfaceControl.Transaction st, @NonNull SurfaceControl.Transaction ft, @NonNull Transitions.TransitionFinishCallback finishCallback)1676         boolean handleCloseTransition(@NonNull TransitionInfo info,
1677                 @NonNull SurfaceControl.Transaction st,
1678                 @NonNull SurfaceControl.Transaction ft,
1679                 @NonNull Transitions.TransitionFinishCallback finishCallback) {
1680             if (!mCloseTransitionRequested) {
1681                 return false;
1682             }
1683             // must have close target
1684             if (!hasAnimationInMode(info, TransitionUtil::isClosingMode)) {
1685                 return false;
1686             }
1687             if (mApps == null) {
1688                 // animation is done
1689                 applyAndFinish(st, ft, finishCallback);
1690                 return true;
1691             }
1692             SurfaceControl openingLeash = null;
1693             SurfaceControl closingLeash = null;
1694             for (int i = mApps.length - 1; i >= 0; --i) {
1695                 if (mApps[i].mode == MODE_OPENING) {
1696                     openingLeash = mApps[i].leash;
1697                 }
1698                 if (mApps[i].mode == MODE_CLOSING) {
1699                     closingLeash = mApps[i].leash;
1700                 }
1701             }
1702             if (openingLeash != null && closingLeash != null) {
1703                 for (int i = info.getChanges().size() - 1; i >= 0; --i) {
1704                     final TransitionInfo.Change c = info.getChanges().get(i);
1705                     if (c.hasFlags(FLAG_IS_WALLPAPER)) {
1706                         st.setAlpha(c.getLeash(), 1.0f);
1707                         continue;
1708                     }
1709                     if (TransitionUtil.isOpeningMode(c.getMode())) {
1710                         final Point offset = c.getEndRelOffset();
1711                         st.setPosition(c.getLeash(), offset.x, offset.y);
1712                         st.reparent(c.getLeash(), openingLeash);
1713                         st.setAlpha(c.getLeash(), 1.0f);
1714                     } else if (TransitionUtil.isClosingMode(c.getMode())) {
1715                         st.reparent(c.getLeash(), closingLeash);
1716                     }
1717                 }
1718             }
1719             st.apply();
1720             // mApps must exists
1721             mOnAnimationFinishCallback = () -> {
1722                 ft.apply();
1723                 finishCallback.onTransitionFinished(null);
1724                 mCloseTransitionRequested = false;
1725             };
1726             return true;
1727         }
1728 
1729         @Nullable
1730         @Override
handleRequest( @onNull IBinder transition, @NonNull TransitionRequestInfo request)1731         public WindowContainerTransaction handleRequest(
1732                 @NonNull IBinder transition,
1733                 @NonNull TransitionRequestInfo request) {
1734             final int type = request.getType();
1735             if (type == WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION) {
1736                 mPrepareOpenTransition = transition;
1737                 return new WindowContainerTransaction();
1738             }
1739             if (type == WindowManager.TRANSIT_CLOSE_PREPARE_BACK_NAVIGATION) {
1740                 return new WindowContainerTransaction();
1741             }
1742             if (TransitionUtil.isClosingType(request.getType()) && mCloseTransitionRequested) {
1743                 return new WindowContainerTransaction();
1744             }
1745             return null;
1746         }
1747 
checkTakeoverFlags()1748         private static boolean checkTakeoverFlags() {
1749             return TransitionAnimator.Companion.longLivedReturnAnimationsEnabled()
1750                     && Flags.unifyBackNavigationTransition();
1751         }
1752     }
1753 
isNotGestureBackTransition(@onNull TransitionInfo info)1754     private static boolean isNotGestureBackTransition(@NonNull TransitionInfo info) {
1755         return !hasAnimationInMode(info, TransitionUtil::isOpenOrCloseMode);
1756     }
1757 
hasAnimationInMode(@onNull TransitionInfo info, Predicate<Integer> mode)1758     private static boolean hasAnimationInMode(@NonNull TransitionInfo info,
1759             Predicate<Integer> mode) {
1760         for (int i = info.getChanges().size() - 1; i >= 0; --i) {
1761             final TransitionInfo.Change c = info.getChanges().get(i);
1762             if (c.hasFlags(FLAG_BACK_GESTURE_ANIMATED) && mode.test(c.getMode())) {
1763                 return true;
1764             }
1765         }
1766         return false;
1767     }
1768 
findToken(TransitionInfo.Change change)1769     private static WindowContainerToken findToken(TransitionInfo.Change change) {
1770         return change.getContainer();
1771     }
1772 
findComponentName(TransitionInfo.Change change)1773     private static ComponentName findComponentName(TransitionInfo.Change change) {
1774         final ComponentName componentName = change.getActivityComponent();
1775         if (componentName != null) {
1776             return componentName;
1777         }
1778         final TaskInfo taskInfo = change.getTaskInfo();
1779         if (taskInfo != null) {
1780             return taskInfo.topActivity;
1781         }
1782         return null;
1783     }
1784 
findTaskId(TransitionInfo.Change change)1785     private static int findTaskId(TransitionInfo.Change change) {
1786         final TaskInfo taskInfo = change.getTaskInfo();
1787         if (taskInfo != null) {
1788             return taskInfo.taskId;
1789         }
1790         return INVALID_TASK_ID;
1791     }
1792 
isOpenSurfaceMatched(@onNull ArrayList<SurfaceControl> openSurfaces, TransitionInfo.Change change)1793     static boolean isOpenSurfaceMatched(@NonNull ArrayList<SurfaceControl> openSurfaces,
1794             TransitionInfo.Change change) {
1795         for (int i = openSurfaces.size() - 1; i >= 0; --i) {
1796             if (openSurfaces.get(i).isSameSurface(change.getLeash())) {
1797                 return true;
1798             }
1799         }
1800         return false;
1801     }
1802 
canBeTransitionTarget(TransitionInfo.Change change)1803     private static boolean canBeTransitionTarget(TransitionInfo.Change change) {
1804         return findComponentName(change) != null || findTaskId(change) != INVALID_TASK_ID;
1805     }
1806 
isCloseChangeExist(TransitionInfo info, TransitionInfo.Change change)1807     private static boolean isCloseChangeExist(TransitionInfo info, TransitionInfo.Change change) {
1808         for (int j = info.getChanges().size() - 1; j >= 0; --j) {
1809             final TransitionInfo.Change current = info.getChanges().get(j);
1810             if (TransitionUtil.isClosingMode(current.getMode())
1811                     && change.getLeash().isSameSurface(current.getLeash())) {
1812                 return true;
1813             }
1814         }
1815         return false;
1816     }
1817 
1818     // Record the latest back gesture happen on which task.
1819     static class BackTransitionObserver implements Transitions.TransitionObserver {
1820         int mFocusedTaskId = INVALID_TASK_ID;
1821         IBinder mFocusTaskMonitorToken;
1822         private BackTransitionHandler mBackTransitionHandler;
setBackTransitionHandler(BackTransitionHandler handler)1823         void setBackTransitionHandler(BackTransitionHandler handler) {
1824             mBackTransitionHandler = handler;
1825         }
1826 
update(int focusedTaskId)1827         void update(int focusedTaskId) {
1828             mFocusedTaskId = focusedTaskId;
1829         }
1830 
1831         @Override
onTransitionReady(@onNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction)1832         public void onTransitionReady(@NonNull IBinder transition, @NonNull TransitionInfo info,
1833                 @NonNull SurfaceControl.Transaction startTransaction,
1834                 @NonNull SurfaceControl.Transaction finishTransaction) {
1835             if (mFocusedTaskId == INVALID_TASK_ID) {
1836                 return;
1837             }
1838             for (int i = info.getChanges().size() - 1; i >= 0; --i) {
1839                 final TransitionInfo.Change c = info.getChanges().get(i);
1840                 if (c.getTaskInfo() != null && c.getTaskInfo().taskId == mFocusedTaskId) {
1841                     mFocusTaskMonitorToken = transition;
1842                     break;
1843                 }
1844             }
1845             // Transition happen but the task isn't involved, reset.
1846             if (mFocusTaskMonitorToken == null) {
1847                 mFocusedTaskId = INVALID_TASK_ID;
1848             }
1849         }
1850 
1851         @Override
onTransitionMerged(@onNull IBinder merged, @NonNull IBinder playing)1852         public void onTransitionMerged(@NonNull IBinder merged, @NonNull IBinder playing) {
1853             if (mFocusTaskMonitorToken == merged) {
1854                 mFocusTaskMonitorToken = playing;
1855             }
1856             if (mBackTransitionHandler.mClosePrepareTransition == merged) {
1857                 mBackTransitionHandler.mClosePrepareTransition = null;
1858             }
1859         }
1860 
1861         @Override
onTransitionFinished(@onNull IBinder transition, boolean aborted)1862         public void onTransitionFinished(@NonNull IBinder transition, boolean aborted) {
1863             if (mFocusTaskMonitorToken == transition) {
1864                 mFocusedTaskId = INVALID_TASK_ID;
1865             }
1866             if (mBackTransitionHandler.mClosePrepareTransition == transition) {
1867                 mBackTransitionHandler.mClosePrepareTransition = null;
1868             }
1869         }
1870     }
1871 }
1872