1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 
17 package com.android.server.wm;
18 
19 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
20 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
21 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
22 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
23 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
24 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
25 import static android.view.RemoteAnimationTarget.MODE_CLOSING;
26 import static android.view.RemoteAnimationTarget.MODE_OPENING;
27 import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
28 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
29 
30 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS;
31 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
32 import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_RECENTS_ANIM;
33 import static com.android.server.wm.AnimationAdapterProto.REMOTE;
34 import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET;
35 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS;
36 import static com.android.server.wm.WindowManagerInternal.AppTransitionListener;
37 
38 import android.annotation.IntDef;
39 import android.annotation.NonNull;
40 import android.app.WindowConfiguration;
41 import android.graphics.GraphicBuffer;
42 import android.graphics.Point;
43 import android.graphics.Rect;
44 import android.hardware.HardwareBuffer;
45 import android.os.Binder;
46 import android.os.IBinder.DeathRecipient;
47 import android.os.RemoteException;
48 import android.os.SystemClock;
49 import android.util.ArrayMap;
50 import android.util.ArraySet;
51 import android.util.IntArray;
52 import android.util.Slog;
53 import android.util.SparseBooleanArray;
54 import android.util.proto.ProtoOutputStream;
55 import android.view.IRecentsAnimationController;
56 import android.view.IRecentsAnimationRunner;
57 import android.view.InputWindowHandle;
58 import android.view.RemoteAnimationTarget;
59 import android.view.SurfaceControl;
60 import android.view.SurfaceControl.Transaction;
61 import android.view.SurfaceSession;
62 import android.view.WindowInsets.Type;
63 import android.window.PictureInPictureSurfaceTransaction;
64 import android.window.TaskSnapshot;
65 
66 import com.android.internal.annotations.VisibleForTesting;
67 import com.android.internal.inputmethod.SoftInputShowHideReason;
68 import com.android.internal.os.BackgroundThread;
69 import com.android.internal.protolog.common.ProtoLog;
70 import com.android.internal.util.LatencyTracker;
71 import com.android.internal.util.function.pooled.PooledConsumer;
72 import com.android.internal.util.function.pooled.PooledFunction;
73 import com.android.internal.util.function.pooled.PooledLambda;
74 import com.android.server.LocalServices;
75 import com.android.server.inputmethod.InputMethodManagerInternal;
76 import com.android.server.statusbar.StatusBarManagerInternal;
77 import com.android.server.wm.SurfaceAnimator.AnimationType;
78 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
79 import com.android.server.wm.utils.InsetUtils;
80 
81 import com.google.android.collect.Sets;
82 
83 import java.io.PrintWriter;
84 import java.util.ArrayList;
85 import java.util.stream.Collectors;
86 
87 /**
88  * Controls a single instance of the remote driven recents animation. In particular, this allows
89  * the calling SystemUI to animate the visible task windows as a part of the transition. The remote
90  * runner is provided an animation controller which allows it to take screenshots and to notify
91  * window manager when the animation is completed. In addition, window manager may also notify the
92  * app if it requires the animation to be canceled at any time (ie. due to timeout, etc.)
93  */
94 public class RecentsAnimationController implements DeathRecipient {
95     private static final String TAG = RecentsAnimationController.class.getSimpleName();
96     private static final long FAILSAFE_DELAY = 1000;
97     /**
98      * If the recents animation is canceled before the delay since the window drawn, do not log the
99      * action because the duration is too small that may be just a mistouch.
100      */
101     private static final long LATENCY_TRACKER_LOG_DELAY_MS = 300;
102 
103     public static final int REORDER_KEEP_IN_PLACE = 0;
104     public static final int REORDER_MOVE_TO_TOP = 1;
105     public static final int REORDER_MOVE_TO_ORIGINAL_POSITION = 2;
106 
107     @IntDef(prefix = { "REORDER_MODE_" }, value = {
108             REORDER_KEEP_IN_PLACE,
109             REORDER_MOVE_TO_TOP,
110             REORDER_MOVE_TO_ORIGINAL_POSITION
111     })
112     public @interface ReorderMode {}
113 
114     private final WindowManagerService mService;
115     @VisibleForTesting
116     final StatusBarManagerInternal mStatusBar;
117     private IRecentsAnimationRunner mRunner;
118     private final RecentsAnimationCallbacks mCallbacks;
119     private final ArrayList<TaskAnimationAdapter> mPendingAnimations = new ArrayList<>();
120     private final IntArray mPendingNewTaskTargets = new IntArray(0);
121 
122     private final ArrayList<WallpaperAnimationAdapter> mPendingWallpaperAnimations =
123             new ArrayList<>();
124     private final int mDisplayId;
125     private boolean mWillFinishToHome = false;
126     private final Runnable mFailsafeRunnable = this::onFailsafe;
127     private Runnable mCheckRotationAfterCleanup;
128 
129     // The recents component app token that is shown behind the visibile tasks
130     private ActivityRecord mTargetActivityRecord;
131     private DisplayContent mDisplayContent;
132     private int mTargetActivityType;
133     private Rect mMinimizedHomeBounds = new Rect();
134 
135     // We start the RecentsAnimationController in a pending-start state since we need to wait for
136     // the wallpaper/activity to draw before we can give control to the handler to start animating
137     // the visible task surfaces
138     private boolean mPendingStart = true;
139 
140     // Set when the animation has been canceled
141     private volatile boolean mCanceled;
142 
143     // Whether or not the input consumer is enabled. The input consumer must be both registered and
144     // enabled for it to start intercepting touch events.
145     private boolean mInputConsumerEnabled;
146 
147     private final Rect mTmpRect = new Rect();
148 
149     private boolean mLinkedToDeathOfRunner;
150 
151     // Whether to try to defer canceling from a root task order change until the next transition
152     private boolean mRequestDeferCancelUntilNextTransition;
153     // Whether to actually defer canceling until the next transition
154     private boolean mCancelOnNextTransitionStart;
155     // Whether to take a screenshot when handling a deferred cancel
156     private boolean mCancelDeferredWithScreenshot;
157     // The reorder mode to apply after the cleanupScreenshot() callback
158     private int mPendingCancelWithScreenshotReorderMode = REORDER_MOVE_TO_ORIGINAL_POSITION;
159 
160     @VisibleForTesting
161     boolean mIsAddingTaskToTargets;
162     @VisibleForTesting
163     boolean mShouldAttachNavBarToAppDuringTransition;
164     private boolean mNavigationBarAttachedToApp;
165     private ActivityRecord mNavBarAttachedApp;
166 
167     private final ArrayList<RemoteAnimationTarget> mPendingTaskAppears = new ArrayList<>();
168 
169     /**
170      * An app transition listener to cancel the recents animation only after the app transition
171      * starts or is canceled.
172      */
173     final AppTransitionListener mAppTransitionListener = new AppTransitionListener() {
174         @Override
175         public int onAppTransitionStartingLocked(boolean keyguardGoingAway,
176                 boolean keyguardOccluding, long duration, long statusBarAnimationStartTime,
177                 long statusBarAnimationDuration) {
178             continueDeferredCancel();
179             return 0;
180         }
181 
182         @Override
183         public void onAppTransitionCancelledLocked(boolean keyguardGoingAway) {
184             continueDeferredCancel();
185         }
186 
187         private void continueDeferredCancel() {
188             mDisplayContent.mAppTransition.unregisterListener(this);
189             if (mCanceled) {
190                 return;
191             }
192 
193             if (mCancelOnNextTransitionStart) {
194                 mCancelOnNextTransitionStart = false;
195                 cancelAnimationWithScreenshot(mCancelDeferredWithScreenshot);
196             }
197         }
198     };
199 
200     public interface RecentsAnimationCallbacks {
201         /** Callback when recents animation is finished. */
onAnimationFinished(@eorderMode int reorderMode, boolean sendUserLeaveHint)202         void onAnimationFinished(@ReorderMode int reorderMode, boolean sendUserLeaveHint);
203     }
204 
205     private final IRecentsAnimationController mController =
206             new IRecentsAnimationController.Stub() {
207 
208         @Override
209         public TaskSnapshot screenshotTask(int taskId) {
210             ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
211                     "screenshotTask(%d): mCanceled=%b", taskId, mCanceled);
212             final long token = Binder.clearCallingIdentity();
213             try {
214                 synchronized (mService.getWindowManagerLock()) {
215                     if (mCanceled) {
216                         return null;
217                     }
218                     for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
219                         final TaskAnimationAdapter adapter = mPendingAnimations.get(i);
220                         final Task task = adapter.mTask;
221                         if (task.mTaskId == taskId) {
222                             final TaskSnapshotController snapshotController =
223                                     mService.mTaskSnapshotController;
224                             final ArraySet<Task> tasks = Sets.newArraySet(task);
225                             snapshotController.snapshotTasks(tasks);
226                             snapshotController.addSkipClosingAppSnapshotTasks(tasks);
227                             return snapshotController.getSnapshot(taskId, task.mUserId,
228                                     false /* restoreFromDisk */, false /* isLowResolution */);
229                         }
230                     }
231                     return null;
232                 }
233             } finally {
234                 Binder.restoreCallingIdentity(token);
235             }
236         }
237 
238         @Override
239         public void setFinishTaskTransaction(int taskId,
240                 PictureInPictureSurfaceTransaction finishTransaction,
241                 SurfaceControl overlay) {
242             ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
243                     "setFinishTaskTransaction(%d): transaction=%s", taskId, finishTransaction);
244             final long token = Binder.clearCallingIdentity();
245             try {
246                 synchronized (mService.getWindowManagerLock()) {
247                     for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
248                         final TaskAnimationAdapter taskAdapter = mPendingAnimations.get(i);
249                         if (taskAdapter.mTask.mTaskId == taskId) {
250                             taskAdapter.mFinishTransaction = finishTransaction;
251                             taskAdapter.mFinishOverlay = overlay;
252                             break;
253                         }
254                     }
255                 }
256             } finally {
257                 Binder.restoreCallingIdentity(token);
258             }
259         }
260 
261         @Override
262         public void finish(boolean moveHomeToTop, boolean sendUserLeaveHint) {
263             ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
264                     "finish(%b): mCanceled=%b", moveHomeToTop, mCanceled);
265             final long token = Binder.clearCallingIdentity();
266             try {
267                 // Note, the callback will handle its own synchronization, do not lock on WM lock
268                 // prior to calling the callback
269                 mCallbacks.onAnimationFinished(moveHomeToTop
270                         ? REORDER_MOVE_TO_TOP
271                         : REORDER_MOVE_TO_ORIGINAL_POSITION, sendUserLeaveHint);
272             } finally {
273                 Binder.restoreCallingIdentity(token);
274             }
275         }
276 
277         @Override
278         public void setAnimationTargetsBehindSystemBars(boolean behindSystemBars)
279                 throws RemoteException {
280             final long token = Binder.clearCallingIdentity();
281             try {
282                 synchronized (mService.getWindowManagerLock()) {
283                     for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
284                         final Task task = mPendingAnimations.get(i).mTask;
285                         if (task.getActivityType() != mTargetActivityType) {
286                             task.setCanAffectSystemUiFlags(behindSystemBars);
287                         }
288                     }
289                     if (!behindSystemBars) {
290                         // Hiding IME if IME window is not attached to app.
291                         // Since some windowing mode is not proper to snapshot Task with IME window
292                         // while the app transitioning to the next task (e.g. split-screen mode)
293                         if (!mDisplayContent.isImeAttachedToApp()) {
294                             final InputMethodManagerInternal inputMethodManagerInternal =
295                                     LocalServices.getService(InputMethodManagerInternal.class);
296                             if (inputMethodManagerInternal != null) {
297                                 inputMethodManagerInternal.hideCurrentInputMethod(
298                                         SoftInputShowHideReason.HIDE_RECENTS_ANIMATION);
299                             }
300                         } else {
301                             // Disable IME icon explicitly when IME attached to the app in case
302                             // IME icon might flickering while swiping to the next app task still
303                             // in animating before the next app window focused, or IME icon
304                             // persists on the bottom when swiping the task to recents.
305                             InputMethodManagerInternal.get().updateImeWindowStatus(
306                                     true /* disableImeIcon */);
307                         }
308                     }
309                     mService.mWindowPlacerLocked.requestTraversal();
310                 }
311             } finally {
312                 Binder.restoreCallingIdentity(token);
313             }
314         }
315 
316         @Override
317         public void setInputConsumerEnabled(boolean enabled) {
318             ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
319                     "setInputConsumerEnabled(%s): mCanceled=%b", enabled, mCanceled);
320             final long token = Binder.clearCallingIdentity();
321             try {
322                 synchronized (mService.getWindowManagerLock()) {
323                     if (mCanceled) {
324                         return;
325                     }
326                     mInputConsumerEnabled = enabled;
327                     final InputMonitor inputMonitor = mDisplayContent.getInputMonitor();
328                     inputMonitor.updateInputWindowsLw(true /*force*/);
329                     mService.scheduleAnimationLocked();
330                 }
331             } finally {
332                 Binder.restoreCallingIdentity(token);
333             }
334         }
335 
336         // TODO(b/166736352): Remove this method without the need to expose to launcher.
337         @Override
338         public void hideCurrentInputMethod() { }
339 
340         @Override
341         public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) {
342             synchronized (mService.mGlobalLock) {
343                 setDeferredCancel(defer, screenshot);
344             }
345         }
346 
347         @Override
348         public void cleanupScreenshot() {
349             final long token = Binder.clearCallingIdentity();
350             try {
351                 // Note, the callback will handle its own synchronization, do not lock on WM lock
352                 // prior to calling the callback
353                 continueDeferredCancelAnimation();
354             } finally {
355                 Binder.restoreCallingIdentity(token);
356             }
357         }
358 
359         @Override
360         public void setWillFinishToHome(boolean willFinishToHome) {
361             synchronized (mService.getWindowManagerLock()) {
362                 RecentsAnimationController.this.setWillFinishToHome(willFinishToHome);
363             }
364         }
365 
366         @Override
367         public boolean removeTask(int taskId) {
368             final long token = Binder.clearCallingIdentity();
369             try {
370                 synchronized (mService.getWindowManagerLock()) {
371                     return removeTaskInternal(taskId);
372                 }
373             } finally {
374                 Binder.restoreCallingIdentity(token);
375             }
376         }
377 
378         @Override
379         public void detachNavigationBarFromApp(boolean moveHomeToTop) {
380             final long token = Binder.clearCallingIdentity();
381             try {
382                 synchronized (mService.getWindowManagerLock()) {
383                     restoreNavigationBarFromApp(
384                             moveHomeToTop || mIsAddingTaskToTargets /* animate */);
385                     mService.mWindowPlacerLocked.requestTraversal();
386                 }
387             } finally {
388                 Binder.restoreCallingIdentity(token);
389             }
390         }
391 
392         @Override
393         public void animateNavigationBarToApp(long duration) {
394             final long token = Binder.clearCallingIdentity();
395             try {
396                 synchronized (mService.getWindowManagerLock()) {
397                     animateNavigationBarForAppLaunch(duration);
398                 }
399             } finally {
400                 Binder.restoreCallingIdentity(token);
401             }
402         }
403     };
404 
405     /**
406      * @param remoteAnimationRunner The remote runner which should be notified when the animation is
407      *                              ready to start or has been canceled
408      * @param callbacks Callbacks to be made when the animation finishes
409      */
RecentsAnimationController(WindowManagerService service, IRecentsAnimationRunner remoteAnimationRunner, RecentsAnimationCallbacks callbacks, int displayId)410     RecentsAnimationController(WindowManagerService service,
411             IRecentsAnimationRunner remoteAnimationRunner, RecentsAnimationCallbacks callbacks,
412             int displayId) {
413         mService = service;
414         mRunner = remoteAnimationRunner;
415         mCallbacks = callbacks;
416         mDisplayId = displayId;
417         mStatusBar = LocalServices.getService(StatusBarManagerInternal.class);
418         mDisplayContent = service.mRoot.getDisplayContent(displayId);
419         mShouldAttachNavBarToAppDuringTransition =
420                 mDisplayContent.getDisplayPolicy().shouldAttachNavBarToAppDuringTransition();
421     }
422 
423     /**
424      * Initializes the recents animation controller. This is a separate call from the constructor
425      * because it may call cancelAnimation() which needs to properly clean up the controller
426      * in the window manager.
427      */
initialize(int targetActivityType, SparseBooleanArray recentTaskIds, ActivityRecord targetActivity)428     public void initialize(int targetActivityType, SparseBooleanArray recentTaskIds,
429             ActivityRecord targetActivity) {
430         mTargetActivityType = targetActivityType;
431         mDisplayContent.mAppTransition.registerListenerLocked(mAppTransitionListener);
432 
433         // Make leashes for each of the visible/target tasks and add it to the recents animation to
434         // be started
435         // TODO(b/153090560): Support Recents on multiple task display areas
436         final ArrayList<Task> visibleTasks = mDisplayContent.getDefaultTaskDisplayArea()
437                 .getVisibleTasks();
438         final Task targetRootTask = mDisplayContent.getDefaultTaskDisplayArea()
439                 .getRootTask(WINDOWING_MODE_UNDEFINED, targetActivityType);
440         if (targetRootTask != null) {
441             final PooledConsumer c = PooledLambda.obtainConsumer((t, outList) ->
442 	            { if (!outList.contains(t)) outList.add(t); }, PooledLambda.__(Task.class),
443                     visibleTasks);
444             targetRootTask.forAllLeafTasks(c, true /* traverseTopToBottom */);
445             c.recycle();
446         }
447 
448         final int taskCount = visibleTasks.size();
449         for (int i = 0; i < taskCount; i++) {
450             final Task task = visibleTasks.get(i);
451             if (skipAnimation(task)) {
452                 continue;
453             }
454             addAnimation(task, !recentTaskIds.get(task.mTaskId), false /* hidden */,
455                     (type, anim) -> task.forAllWindows(win -> {
456                         win.onAnimationFinished(type, anim);
457                     }, true /* traverseTopToBottom */));
458         }
459 
460         // Skip the animation if there is nothing to animate
461         if (mPendingAnimations.isEmpty()) {
462             cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "initialize-noVisibleTasks");
463             return;
464         }
465 
466         try {
467             linkToDeathOfRunner();
468         } catch (RemoteException e) {
469             cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "initialize-failedToLinkToDeath");
470             return;
471         }
472 
473         attachNavigationBarToApp();
474 
475         // Adjust the wallpaper visibility for the showing target activity
476         ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
477                 "setHomeApp(%s)", targetActivity.getName());
478         mTargetActivityRecord = targetActivity;
479         if (targetActivity.windowsCanBeWallpaperTarget()) {
480             mDisplayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
481             mDisplayContent.setLayoutNeeded();
482         }
483 
484         // Save the minimized home height
485         final Task rootHomeTask =
486                 mDisplayContent.getDefaultTaskDisplayArea().getRootHomeTask();
487         mMinimizedHomeBounds = rootHomeTask != null ? rootHomeTask.getBounds() : null;
488 
489         mService.mWindowPlacerLocked.performSurfacePlacement();
490 
491         mDisplayContent.mFixedRotationTransitionListener.onStartRecentsAnimation(targetActivity);
492 
493         // Notify that the animation has started
494         if (mStatusBar != null) {
495             mStatusBar.onRecentsAnimationStateChanged(true /* running */);
496         }
497     }
498 
499 
500     /**
501      * Whether a task should be filtered from the recents animation. This can be true for tasks
502      * being displayed outside of recents.
503      */
skipAnimation(Task task)504     private boolean skipAnimation(Task task) {
505         final WindowConfiguration config = task.getWindowConfiguration();
506         return task.isAlwaysOnTop()
507                 || config.tasksAreFloating()
508                 || config.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
509     }
510 
511     @VisibleForTesting
addAnimation(Task task, boolean isRecentTaskInvisible)512     TaskAnimationAdapter addAnimation(Task task, boolean isRecentTaskInvisible) {
513         return addAnimation(task, isRecentTaskInvisible, false /* hidden */,
514                 null /* finishedCallback */);
515     }
516 
517     @VisibleForTesting
addAnimation(Task task, boolean isRecentTaskInvisible, boolean hidden, OnAnimationFinishedCallback finishedCallback)518     TaskAnimationAdapter addAnimation(Task task, boolean isRecentTaskInvisible, boolean hidden,
519             OnAnimationFinishedCallback finishedCallback) {
520         ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "addAnimation(%s)", task.getName());
521         final TaskAnimationAdapter taskAdapter = new TaskAnimationAdapter(task,
522                 isRecentTaskInvisible);
523         task.startAnimation(task.getPendingTransaction(), taskAdapter, hidden,
524                 ANIMATION_TYPE_RECENTS, finishedCallback);
525         task.commitPendingTransaction();
526         mPendingAnimations.add(taskAdapter);
527         return taskAdapter;
528     }
529 
530     @VisibleForTesting
removeAnimation(TaskAnimationAdapter taskAdapter)531     void removeAnimation(TaskAnimationAdapter taskAdapter) {
532         ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
533                 "removeAnimation(%d)", taskAdapter.mTask.mTaskId);
534         taskAdapter.onRemove();
535         mPendingAnimations.remove(taskAdapter);
536     }
537 
538     @VisibleForTesting
removeWallpaperAnimation(WallpaperAnimationAdapter wallpaperAdapter)539     void removeWallpaperAnimation(WallpaperAnimationAdapter wallpaperAdapter) {
540         ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "removeWallpaperAnimation()");
541         wallpaperAdapter.getLeashFinishedCallback().onAnimationFinished(
542                 wallpaperAdapter.getLastAnimationType(), wallpaperAdapter);
543         mPendingWallpaperAnimations.remove(wallpaperAdapter);
544     }
545 
startAnimation()546     void startAnimation() {
547         ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
548                 "startAnimation(): mPendingStart=%b mCanceled=%b", mPendingStart, mCanceled);
549         if (!mPendingStart || mCanceled) {
550             // Skip starting if we've already started or canceled the animation
551             return;
552         }
553         try {
554             // Create the app targets
555             final RemoteAnimationTarget[] appTargets = createAppAnimations();
556 
557             // Skip the animation if there is nothing to animate
558             if (appTargets.length == 0) {
559                 cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "startAnimation-noAppWindows");
560                 return;
561             }
562 
563             // Create the wallpaper targets
564             final RemoteAnimationTarget[] wallpaperTargets = createWallpaperAnimations();
565 
566             mPendingStart = false;
567 
568             // Perform layout if it was scheduled before to make sure that we get correct content
569             // insets for the target app window after a rotation
570             mDisplayContent.performLayout(false /* initial */, false /* updateInputWindows */);
571 
572             final Rect minimizedHomeBounds = mTargetActivityRecord != null
573                     && mTargetActivityRecord.inSplitScreenSecondaryWindowingMode()
574                             ? mMinimizedHomeBounds
575                             : null;
576             final Rect contentInsets;
577             final WindowState targetAppMainWindow = getTargetAppMainWindow();
578             if (targetAppMainWindow != null) {
579                 contentInsets = targetAppMainWindow
580                         .getInsetsStateWithVisibilityOverride()
581                         .calculateInsets(mTargetActivityRecord.getBounds(), Type.systemBars(),
582                                 false /* ignoreVisibility */).toRect();
583             } else {
584                 // If the window for the activity had not yet been created, use the display insets.
585                 mService.getStableInsets(mDisplayId, mTmpRect);
586                 contentInsets = mTmpRect;
587             }
588             mRunner.onAnimationStart(mController, appTargets, wallpaperTargets, contentInsets,
589                     minimizedHomeBounds);
590             ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
591                     "startAnimation(): Notify animation start: %s",
592                     mPendingAnimations.stream()
593                             .map(anim->anim.mTask.mTaskId).collect(Collectors.toList()));
594         } catch (RemoteException e) {
595             Slog.e(TAG, "Failed to start recents animation", e);
596         }
597 
598         if (mTargetActivityRecord != null) {
599             final ArrayMap<WindowContainer, Integer> reasons = new ArrayMap<>(1);
600             reasons.put(mTargetActivityRecord, APP_TRANSITION_RECENTS_ANIM);
601             mService.mAtmService.mTaskSupervisor.getActivityMetricsLogger()
602                     .notifyTransitionStarting(reasons);
603         }
604     }
605 
isNavigationBarAttachedToApp()606     boolean isNavigationBarAttachedToApp() {
607         return mNavigationBarAttachedToApp;
608     }
609 
610     @VisibleForTesting
getNavigationBarWindow()611     WindowState getNavigationBarWindow() {
612         return mDisplayContent.getDisplayPolicy().getNavigationBar();
613     }
614 
attachNavigationBarToApp()615     private void attachNavigationBarToApp() {
616         if (!mShouldAttachNavBarToAppDuringTransition
617                 // Skip the case where the nav bar is controlled by fade rotation.
618                 || mDisplayContent.getFadeRotationAnimationController() != null) {
619             return;
620         }
621         boolean shouldTranslateNavBar = false;
622         final boolean isDisplayLandscape =
623                 mDisplayContent.getConfiguration().orientation == ORIENTATION_LANDSCAPE;
624         for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
625             final TaskAnimationAdapter adapter = mPendingAnimations.get(i);
626             final Task task = adapter.mTask;
627             final boolean isSplitScreenSecondary =
628                     task.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
629             if (task.isHomeOrRecentsRootTask()
630                     // TODO(b/178449492): Will need to update for the new split screen mode once
631                     // it's ready.
632                     // Skip if the task is the secondary split screen and in landscape.
633                     || (isSplitScreenSecondary && isDisplayLandscape)) {
634                 continue;
635             }
636             shouldTranslateNavBar = isSplitScreenSecondary;
637             mNavBarAttachedApp = task.getTopVisibleActivity();
638             break;
639         }
640 
641         final WindowState navWindow = getNavigationBarWindow();
642         if (mNavBarAttachedApp == null || navWindow == null || navWindow.mToken == null) {
643             return;
644         }
645         mNavigationBarAttachedToApp = true;
646         navWindow.mToken.cancelAnimation();
647         final SurfaceControl.Transaction t = navWindow.mToken.getPendingTransaction();
648         final SurfaceControl navSurfaceControl = navWindow.mToken.getSurfaceControl();
649         if (shouldTranslateNavBar) {
650             navWindow.setSurfaceTranslationY(-mNavBarAttachedApp.getBounds().top);
651         }
652         t.reparent(navSurfaceControl, mNavBarAttachedApp.getSurfaceControl());
653         t.show(navSurfaceControl);
654 
655         final WindowContainer imeContainer = mDisplayContent.getImeContainer();
656         if (imeContainer.isVisible()) {
657             t.setRelativeLayer(navSurfaceControl, imeContainer.getSurfaceControl(), 1);
658         } else {
659             // Place the nav bar on top of anything else in the top activity.
660             t.setLayer(navSurfaceControl, Integer.MAX_VALUE);
661         }
662         if (mStatusBar != null) {
663             mStatusBar.setNavigationBarLumaSamplingEnabled(mDisplayId, false);
664         }
665     }
666 
667     @VisibleForTesting
restoreNavigationBarFromApp(boolean animate)668     void restoreNavigationBarFromApp(boolean animate) {
669         if (!mNavigationBarAttachedToApp) {
670             return;
671         }
672         mNavigationBarAttachedToApp = false;
673 
674         if (mStatusBar != null) {
675             mStatusBar.setNavigationBarLumaSamplingEnabled(mDisplayId, true);
676         }
677 
678         final WindowState navWindow = getNavigationBarWindow();
679         if (navWindow == null) {
680             return;
681         }
682         navWindow.setSurfaceTranslationY(0);
683 
684         final WindowToken navToken = navWindow.mToken;
685         if (navToken == null) {
686             return;
687         }
688         final SurfaceControl.Transaction t = mDisplayContent.getPendingTransaction();
689         final WindowContainer parent = navToken.getParent();
690         t.setLayer(navToken.getSurfaceControl(), navToken.getLastLayer());
691 
692         if (animate) {
693             final NavBarFadeAnimationController controller =
694                         new NavBarFadeAnimationController(mDisplayContent);
695             controller.fadeWindowToken(true);
696         } else {
697             // Reparent the SurfaceControl of nav bar token back.
698             t.reparent(navToken.getSurfaceControl(), parent.getSurfaceControl());
699         }
700     }
701 
animateNavigationBarForAppLaunch(long duration)702     void animateNavigationBarForAppLaunch(long duration) {
703         if (!mShouldAttachNavBarToAppDuringTransition
704                 // Skip the case where the nav bar is controlled by fade rotation.
705                 || mDisplayContent.getFadeRotationAnimationController() != null
706                 || mNavigationBarAttachedToApp
707                 || mNavBarAttachedApp == null) {
708             return;
709         }
710 
711         final NavBarFadeAnimationController controller =
712                 new NavBarFadeAnimationController(mDisplayContent);
713         controller.fadeOutAndInSequentially(duration, null /* fadeOutParent */,
714                 mNavBarAttachedApp.getSurfaceControl());
715     }
716 
addTaskToTargets(Task task, OnAnimationFinishedCallback finishedCallback)717     void addTaskToTargets(Task task, OnAnimationFinishedCallback finishedCallback) {
718         if (mRunner != null) {
719             mIsAddingTaskToTargets = task != null;
720             mNavBarAttachedApp = task == null ? null : task.getTopVisibleActivity();
721             // No need to send task appeared when the task target already exists, or when the
722             // task is being managed as a multi-window mode outside of recents (e.g. bubbles).
723             if (isAnimatingTask(task) || skipAnimation(task)) {
724                 return;
725             }
726             final RemoteAnimationTarget target = createTaskRemoteAnimation(task, finishedCallback);
727             if (target == null) {
728                 return;
729             }
730             ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "addTaskToTargets, target: %s", target);
731             mPendingTaskAppears.add(target);
732         }
733     }
734 
sendTasksAppeared()735     void sendTasksAppeared() {
736         if (mPendingTaskAppears.isEmpty() || mRunner == null) return;
737         try {
738             final RemoteAnimationTarget[] targets = mPendingTaskAppears.toArray(
739                     new RemoteAnimationTarget[0]);
740             mRunner.onTasksAppeared(targets);
741             mPendingTaskAppears.clear();
742         } catch (RemoteException e) {
743             Slog.e(TAG, "Failed to report task appeared", e);
744         }
745     }
746 
createTaskRemoteAnimation(Task task, OnAnimationFinishedCallback finishedCallback)747     private RemoteAnimationTarget createTaskRemoteAnimation(Task task,
748             OnAnimationFinishedCallback finishedCallback) {
749         final SparseBooleanArray recentTaskIds =
750                 mService.mAtmService.getRecentTasks().getRecentTaskIds();
751         // The target must be built off the root task (the leaf task surface would be cropped
752         // within the root surface). However, recents only tracks leaf task ids, so we'll replace
753         // the task-id with the leaf id.
754         final Task leafTask = task.getTopLeafTask();
755         int taskId = leafTask.mTaskId;
756         TaskAnimationAdapter adapter = addAnimation(task,
757                 !recentTaskIds.get(taskId), true /* hidden */, finishedCallback);
758         mPendingNewTaskTargets.add(taskId);
759         return adapter.createRemoteAnimationTarget(taskId);
760     }
761 
logRecentsAnimationStartTime(int durationMs)762     void logRecentsAnimationStartTime(int durationMs) {
763         BackgroundThread.getHandler().postDelayed(() -> {
764             if (!mCanceled) {
765                 mService.mLatencyTracker.logAction(LatencyTracker.ACTION_START_RECENTS_ANIMATION,
766                         durationMs);
767             }
768         }, LATENCY_TRACKER_LOG_DELAY_MS);
769     }
770 
removeTaskInternal(int taskId)771     private boolean removeTaskInternal(int taskId) {
772         boolean result = false;
773         for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
774             // Only allows when task target has became visible to user, to prevent
775             // the flickering during remove animation and task visible.
776             final TaskAnimationAdapter target = mPendingAnimations.get(i);
777             if (target.mTask.mTaskId == taskId && target.mTask.isOnTop()) {
778                 removeAnimation(target);
779                 final int taskIndex = mPendingNewTaskTargets.indexOf(taskId);
780                 if (taskIndex != -1) {
781                     mPendingNewTaskTargets.remove(taskIndex);
782                 }
783                 result = true;
784                 break;
785             }
786         }
787         return result;
788     }
789 
createAppAnimations()790     private RemoteAnimationTarget[] createAppAnimations() {
791         final ArrayList<RemoteAnimationTarget> targets = new ArrayList<>();
792         for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
793             final TaskAnimationAdapter taskAdapter = mPendingAnimations.get(i);
794             final RemoteAnimationTarget target =
795                     taskAdapter.createRemoteAnimationTarget(INVALID_TASK_ID);
796             if (target != null) {
797                 targets.add(target);
798             } else {
799                 removeAnimation(taskAdapter);
800             }
801         }
802         return targets.toArray(new RemoteAnimationTarget[targets.size()]);
803     }
804 
createWallpaperAnimations()805     private RemoteAnimationTarget[] createWallpaperAnimations() {
806         ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "createWallpaperAnimations()");
807         return WallpaperAnimationAdapter.startWallpaperAnimations(mDisplayContent, 0L, 0L,
808                 adapter -> {
809                     synchronized (mService.mGlobalLock) {
810                         // If the wallpaper animation is canceled, continue with the recents
811                         // animation
812                         mPendingWallpaperAnimations.remove(adapter);
813                     }
814                 }, mPendingWallpaperAnimations);
815     }
816 
817     void forceCancelAnimation(@ReorderMode int reorderMode, String reason) {
818         if (!mCanceled) {
819             cancelAnimation(reorderMode, reason);
820         } else {
821             continueDeferredCancelAnimation();
822         }
823     }
824 
825     void cancelAnimation(@ReorderMode int reorderMode, String reason) {
826         cancelAnimation(reorderMode, false /*screenshot */, reason);
827     }
828 
829     void cancelAnimationWithScreenshot(boolean screenshot) {
830         cancelAnimation(REORDER_KEEP_IN_PLACE, screenshot, "rootTaskOrderChanged");
831     }
832 
833     /**
834      * Cancels the running animation when starting home, providing a snapshot for the runner to
835      * properly handle the cancellation. This call uses the provided hint to determine how to
836      * finish the animation.
837      */
838     public void cancelAnimationForHomeStart() {
839         final int reorderMode = mTargetActivityType == ACTIVITY_TYPE_HOME && mWillFinishToHome
840                 ? REORDER_MOVE_TO_TOP
841                 : REORDER_KEEP_IN_PLACE;
842         cancelAnimation(reorderMode, true /* screenshot */, "cancelAnimationForHomeStart");
843     }
844 
845     /**
846      * Cancels the running animation when there is a display change, providing a snapshot for the
847      * runner to properly handle the cancellation. This call uses the provided hint to determine
848      * how to finish the animation.
849      */
850     public void cancelAnimationForDisplayChange() {
851         cancelAnimation(mWillFinishToHome ? REORDER_MOVE_TO_TOP : REORDER_MOVE_TO_ORIGINAL_POSITION,
852                 true /* screenshot */, "cancelAnimationForDisplayChange");
853     }
854 
855     private void cancelAnimation(@ReorderMode int reorderMode, boolean screenshot, String reason) {
856         ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "cancelAnimation(): reason=%s", reason);
857         synchronized (mService.getWindowManagerLock()) {
858             if (mCanceled) {
859                 // We've already canceled the animation
860                 return;
861             }
862             mService.mH.removeCallbacks(mFailsafeRunnable);
863             mCanceled = true;
864 
865             if (screenshot && !mPendingAnimations.isEmpty()) {
866                 final ArrayMap<Task, TaskSnapshot> snapshotMap = screenshotRecentTasks();
867                 mPendingCancelWithScreenshotReorderMode = reorderMode;
868 
869                 if (!snapshotMap.isEmpty()) {
870                     try {
871                         int[] taskIds = new int[snapshotMap.size()];
872                         TaskSnapshot[] snapshots = new TaskSnapshot[snapshotMap.size()];
873                         for (int i = snapshotMap.size() - 1; i >= 0; i--) {
874                             taskIds[i] = snapshotMap.keyAt(i).mTaskId;
875                             snapshots[i] = snapshotMap.valueAt(i);
876                         }
877                         mRunner.onAnimationCanceled(taskIds, snapshots);
878                     } catch (RemoteException e) {
879                         Slog.e(TAG, "Failed to cancel recents animation", e);
880                     }
881                     // Schedule a new failsafe for if the runner doesn't clean up the screenshot
882                     scheduleFailsafe();
883                     return;
884                 }
885                 // Fallback to a normal cancel since we couldn't screenshot
886             }
887 
888             // Notify the runner and clean up the animation immediately
889             // Note: In the fallback case, this can trigger multiple onAnimationCancel() calls
890             // to the runner if we this actually triggers cancel twice on the caller
891             try {
892                 mRunner.onAnimationCanceled(null /* taskIds */, null /* taskSnapshots */);
893             } catch (RemoteException e) {
894                 Slog.e(TAG, "Failed to cancel recents animation", e);
895             }
896             mCallbacks.onAnimationFinished(reorderMode, false /* sendUserLeaveHint */);
897         }
898     }
899 
900     @VisibleForTesting
901     void continueDeferredCancelAnimation() {
902         mCallbacks.onAnimationFinished(mPendingCancelWithScreenshotReorderMode,
903                 false /* sendUserLeaveHint */);
904     }
905 
906     @VisibleForTesting
907     void setWillFinishToHome(boolean willFinishToHome) {
908         mWillFinishToHome = willFinishToHome;
909     }
910 
911     /**
912      * Cancel recents animation when the next app transition starts.
913      * <p>
914      * When we cancel the recents animation due to a root task order change, we can't just cancel it
915      * immediately as it would lead to a flicker in Launcher if we just remove the task from the
916      * leash. Instead we screenshot the previous task and replace the child of the leash with the
917      * screenshot, so that Launcher can still control the leash lifecycle & make the next app
918      * transition animate smoothly without flickering.
919      */
920     void setCancelOnNextTransitionStart() {
921         mCancelOnNextTransitionStart = true;
922     }
923 
924     /**
925      * Requests that we attempt to defer the cancel until the next app transition if we are
926      * canceling from a root task order change.  If {@param screenshot} is specified, then the
927      * system will replace the contents of the leash with a screenshot, which must be cleaned up
928      * when the runner calls cleanUpScreenshot().
929      */
930     void setDeferredCancel(boolean defer, boolean screenshot) {
931         mRequestDeferCancelUntilNextTransition = defer;
932         mCancelDeferredWithScreenshot = screenshot;
933     }
934 
935     /**
936      * If the display rotation change is ignored while recents animation is running, make sure that
937      * the pending rotation change will be applied after the animation finishes.
938      */
939     void setCheckRotationAfterCleanup() {
940         if (mCheckRotationAfterCleanup != null) return;
941         mCheckRotationAfterCleanup = () -> {
942             synchronized (mService.mGlobalLock) {
943                 if (mDisplayContent.getDisplayRotation()
944                         .updateRotationAndSendNewConfigIfChanged()) {
945                     if (mTargetActivityRecord != null) {
946                         mTargetActivityRecord.finishFixedRotationTransform();
947                     }
948                 }
949             }
950         };
951     }
952 
953     /**
954      * @return Whether we should defer the cancel from a root task order change until the next app
955      * transition.
956      */
957     boolean shouldDeferCancelUntilNextTransition() {
958         return mRequestDeferCancelUntilNextTransition;
959     }
960 
961     /**
962      * @return Whether we should both defer the cancel from a root task order change until the next
963      * app transition, and also that the deferred cancel should replace the contents of the leash
964      * with a screenshot.
965      */
966     boolean shouldDeferCancelWithScreenshot() {
967         return mRequestDeferCancelUntilNextTransition && mCancelDeferredWithScreenshot;
968     }
969 
970     private ArrayMap<Task, TaskSnapshot> screenshotRecentTasks() {
971         final TaskSnapshotController snapshotController = mService.mTaskSnapshotController;
972         final ArrayMap<Task, TaskSnapshot> snapshotMap = new ArrayMap<>();
973         for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
974             final TaskAnimationAdapter adapter = mPendingAnimations.get(i);
975             final Task task = adapter.mTask;
976             snapshotController.recordTaskSnapshot(task, false /* allowSnapshotHome */);
977             final TaskSnapshot snapshot = snapshotController.getSnapshot(task.mTaskId, task.mUserId,
978                     false /* restoreFromDisk */, false /* isLowResolution */);
979             if (snapshot != null) {
980                 snapshotMap.put(task, snapshot);
981                 // Defer until the runner calls back to cleanupScreenshot()
982                 adapter.setSnapshotOverlay(snapshot);
983             }
984         }
985         snapshotController.addSkipClosingAppSnapshotTasks(snapshotMap.keySet());
986         return snapshotMap;
987     }
988 
989     void cleanupAnimation(@ReorderMode int reorderMode) {
990         ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
991                         "cleanupAnimation(): Notify animation finished mPendingAnimations=%d "
992                                 + "reorderMode=%d",
993                         mPendingAnimations.size(), reorderMode);
994         if (reorderMode != REORDER_MOVE_TO_ORIGINAL_POSITION
995                 && mTargetActivityRecord != mDisplayContent.topRunningActivity()) {
996             // Notify the state at the beginning because the removeAnimation may notify the
997             // transition is finished. This is a signal that there will be a next transition.
998             mDisplayContent.mFixedRotationTransitionListener.notifyRecentsWillBeTop();
999         }
1000         for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
1001             final TaskAnimationAdapter taskAdapter = mPendingAnimations.get(i);
1002             if (reorderMode == REORDER_MOVE_TO_TOP || reorderMode == REORDER_KEEP_IN_PLACE) {
1003                 taskAdapter.mTask.dontAnimateDimExit();
1004             }
1005             removeAnimation(taskAdapter);
1006             taskAdapter.onCleanup();
1007         }
1008         // Should already be empty, but clean-up pending task-appears in-case they weren't sent.
1009         mPendingNewTaskTargets.clear();
1010         mPendingTaskAppears.clear();
1011 
1012         for (int i = mPendingWallpaperAnimations.size() - 1; i >= 0; i--) {
1013             final WallpaperAnimationAdapter wallpaperAdapter = mPendingWallpaperAnimations.get(i);
1014             removeWallpaperAnimation(wallpaperAdapter);
1015         }
1016 
1017         restoreNavigationBarFromApp(
1018                 reorderMode == REORDER_MOVE_TO_TOP || mIsAddingTaskToTargets /* animate */);
1019 
1020         // Clear any pending failsafe runnables
1021         mService.mH.removeCallbacks(mFailsafeRunnable);
1022         mDisplayContent.mAppTransition.unregisterListener(mAppTransitionListener);
1023 
1024         // Clear references to the runner
1025         unlinkToDeathOfRunner();
1026         mRunner = null;
1027         mCanceled = true;
1028 
1029         // Restore IME icon only when moving the original app task to front from recents, in case
1030         // IME icon may missing if the moving task has already been the current focused task.
1031         if (reorderMode == REORDER_MOVE_TO_ORIGINAL_POSITION && !mIsAddingTaskToTargets) {
1032             InputMethodManagerInternal.get().updateImeWindowStatus(false /* disableImeIcon */);
1033         }
1034 
1035         // Update the input windows after the animation is complete
1036         final InputMonitor inputMonitor = mDisplayContent.getInputMonitor();
1037         inputMonitor.updateInputWindowsLw(true /*force*/);
1038 
1039         // We have deferred all notifications to the target app as a part of the recents animation,
1040         // so if we are actually transitioning there, notify again here
1041         if (mTargetActivityRecord != null) {
1042             if (reorderMode == REORDER_MOVE_TO_TOP || reorderMode == REORDER_KEEP_IN_PLACE) {
1043                 mDisplayContent.mAppTransition.notifyAppTransitionFinishedLocked(
1044                         mTargetActivityRecord.token);
1045             }
1046         }
1047         mDisplayContent.mFixedRotationTransitionListener.onFinishRecentsAnimation();
1048 
1049         // Notify that the animation has ended
1050         if (mStatusBar != null) {
1051             mStatusBar.onRecentsAnimationStateChanged(false /* running */);
1052         }
1053         if (mCheckRotationAfterCleanup != null) {
1054             mService.mH.post(mCheckRotationAfterCleanup);
1055             mCheckRotationAfterCleanup = null;
1056         }
1057     }
1058 
1059     void scheduleFailsafe() {
1060         mService.mH.postDelayed(mFailsafeRunnable, FAILSAFE_DELAY);
1061     }
1062 
1063     void onFailsafe() {
1064         forceCancelAnimation(
1065                 mWillFinishToHome ? REORDER_MOVE_TO_TOP : REORDER_MOVE_TO_ORIGINAL_POSITION,
1066                 "onFailsafe");
1067     }
1068 
1069     private void linkToDeathOfRunner() throws RemoteException {
1070         if (!mLinkedToDeathOfRunner) {
1071             mRunner.asBinder().linkToDeath(this, 0);
1072             mLinkedToDeathOfRunner = true;
1073         }
1074     }
1075 
1076     private void unlinkToDeathOfRunner() {
1077         if (mLinkedToDeathOfRunner) {
1078             mRunner.asBinder().unlinkToDeath(this, 0);
1079             mLinkedToDeathOfRunner = false;
1080         }
1081     }
1082 
1083     @Override
1084     public void binderDied() {
1085         forceCancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "binderDied");
1086 
1087         synchronized (mService.getWindowManagerLock()) {
1088             // Clear associated input consumers on runner death
1089             final InputMonitor inputMonitor = mDisplayContent.getInputMonitor();
1090             inputMonitor.destroyInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION);
1091         }
1092     }
1093 
1094     void checkAnimationReady(WallpaperController wallpaperController) {
1095         if (mPendingStart) {
1096             final boolean wallpaperReady = !isTargetOverWallpaper()
1097                     || (wallpaperController.getWallpaperTarget() != null
1098                             && wallpaperController.wallpaperTransitionReady());
1099             if (wallpaperReady) {
1100                 mService.getRecentsAnimationController().startAnimation();
1101             }
1102         }
1103     }
1104 
1105     boolean isWallpaperVisible(WindowState w) {
1106         return w != null && w.mAttrs.type == TYPE_BASE_APPLICATION &&
1107                 ((w.mActivityRecord != null && mTargetActivityRecord == w.mActivityRecord)
1108                         || isAnimatingTask(w.getTask()))
1109                 && isTargetOverWallpaper() && w.isOnScreen();
1110     }
1111 
1112     /**
1113      * @return Whether to use the input consumer to override app input to route home/recents.
1114      */
1115     boolean shouldApplyInputConsumer(ActivityRecord activity) {
1116         // Only apply the input consumer if it is enabled, it is not the target (home/recents)
1117         // being revealed with the transition, and we are actively animating the app as a part of
1118         // the animation
1119         return mInputConsumerEnabled && activity != null
1120                 && !isTargetApp(activity) && isAnimatingApp(activity);
1121     }
1122 
1123     boolean updateInputConsumerForApp(InputWindowHandle inputWindowHandle) {
1124         // Update the input consumer touchable region to match the target app main window
1125         final WindowState targetAppMainWindow = getTargetAppMainWindow();
1126         if (targetAppMainWindow != null) {
1127             targetAppMainWindow.getBounds(mTmpRect);
1128             inputWindowHandle.touchableRegion.set(mTmpRect);
1129             return true;
1130         }
1131         return false;
1132     }
1133 
1134     boolean isTargetApp(ActivityRecord activity) {
1135         return mTargetActivityRecord != null && activity == mTargetActivityRecord;
1136     }
1137 
1138     private boolean isTargetOverWallpaper() {
1139         if (mTargetActivityRecord == null) {
1140             return false;
1141         }
1142         return mTargetActivityRecord.windowsCanBeWallpaperTarget();
1143     }
1144 
1145     WindowState getTargetAppMainWindow() {
1146         if (mTargetActivityRecord == null) {
1147             return null;
1148         }
1149         return mTargetActivityRecord.findMainWindow();
1150     }
1151 
1152     DisplayArea getTargetAppDisplayArea() {
1153         if (mTargetActivityRecord == null) {
1154             return null;
1155         }
1156         return mTargetActivityRecord.getDisplayArea();
1157     }
1158 
1159     boolean isAnimatingTask(Task task) {
1160         for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
1161             if (task == mPendingAnimations.get(i).mTask) {
1162                 return true;
1163             }
1164         }
1165         return false;
1166     }
1167 
1168     boolean isAnimatingWallpaper(WallpaperWindowToken token) {
1169         for (int i = mPendingWallpaperAnimations.size() - 1; i >= 0; i--) {
1170             if (token == mPendingWallpaperAnimations.get(i).getToken()) {
1171                 return true;
1172             }
1173         }
1174         return false;
1175     }
1176 
1177     private boolean isAnimatingApp(ActivityRecord activity) {
1178         for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
1179             final Task task = mPendingAnimations.get(i).mTask;
1180             final PooledFunction f = PooledLambda.obtainFunction(
1181                     (a, b) -> a == b, activity,
1182                     PooledLambda.__(ActivityRecord.class));
1183             boolean isAnimatingApp = task.forAllActivities(f);
1184             f.recycle();
1185             if (isAnimatingApp) {
1186                 return true;
1187             }
1188         }
1189         return false;
1190     }
1191 
1192     boolean shouldIgnoreForAccessibility(WindowState windowState) {
1193         final Task task = windowState.getTask();
1194         return task != null && isAnimatingTask(task) && !isTargetApp(windowState.mActivityRecord);
1195     }
1196 
1197     /**
1198      * If the animation target ActivityRecord has a fixed rotation ({@link
1199      * WindowToken#hasFixedRotationTransform()}, the provided wallpaper will be rotated accordingly.
1200      *
1201      * This avoids any screen rotation animation when animating to the Recents view.
1202      */
1203     void linkFixedRotationTransformIfNeeded(@NonNull WindowToken wallpaper) {
1204         if (mTargetActivityRecord == null) {
1205             return;
1206         }
1207         wallpaper.linkFixedRotationTransform(mTargetActivityRecord);
1208     }
1209 
1210     @VisibleForTesting
1211     class TaskAnimationAdapter implements AnimationAdapter {
1212 
1213         private final Task mTask;
1214         private SurfaceControl mCapturedLeash;
1215         private OnAnimationFinishedCallback mCapturedFinishCallback;
1216         private @AnimationType int mLastAnimationType;
1217         private final boolean mIsRecentTaskInvisible;
1218         private RemoteAnimationTarget mTarget;
1219         private final Rect mBounds = new Rect();
1220         // The bounds of the target relative to its parent.
1221         private final Rect mLocalBounds = new Rect();
1222         // The final surface transaction when animation is finished.
1223         private PictureInPictureSurfaceTransaction mFinishTransaction;
1224         // An overlay used to mask the content as an app goes into PIP
1225         private SurfaceControl mFinishOverlay;
1226         // An overlay used for canceling the animation with a screenshot
1227         private SurfaceControl mSnapshotOverlay;
1228 
1229         TaskAnimationAdapter(Task task, boolean isRecentTaskInvisible) {
1230             mTask = task;
1231             mIsRecentTaskInvisible = isRecentTaskInvisible;
1232             mBounds.set(mTask.getBounds());
1233 
1234             mLocalBounds.set(mBounds);
1235             Point tmpPos = new Point();
1236             mTask.getRelativePosition(tmpPos);
1237             mLocalBounds.offsetTo(tmpPos.x, tmpPos.y);
1238         }
1239 
1240         /**
1241          * @param overrideTaskId overrides the target's taskId. It may differ from mTaskId and thus
1242          *                       can differ from taskInfo. This mismatch is needed, however, in
1243          *                       some cases where we are animating root tasks but need need leaf
1244          *                       ids for identification. If this is INVALID (-1), then mTaskId
1245          *                       will be used.
1246          */
1247         RemoteAnimationTarget createRemoteAnimationTarget(int overrideTaskId) {
1248             final ActivityRecord topApp = mTask.getTopVisibleActivity();
1249             final WindowState mainWindow = topApp != null
1250                     ? topApp.findMainWindow()
1251                     : null;
1252             if (mainWindow == null) {
1253                 return null;
1254             }
1255             final Rect insets = mainWindow.getInsetsStateWithVisibilityOverride().calculateInsets(
1256                     mBounds, Type.systemBars(), false /* ignoreVisibility */).toRect();
1257             InsetUtils.addInsets(insets, mainWindow.mActivityRecord.getLetterboxInsets());
1258             final int mode = topApp.getActivityType() == mTargetActivityType
1259                     ? MODE_OPENING
1260                     : MODE_CLOSING;
1261             if (overrideTaskId < 0) {
1262                 overrideTaskId = mTask.mTaskId;
1263             }
1264             mTarget = new RemoteAnimationTarget(overrideTaskId, mode, mCapturedLeash,
1265                     !topApp.fillsParent(), new Rect(),
1266                     insets, mTask.getPrefixOrderIndex(), new Point(mBounds.left, mBounds.top),
1267                     mLocalBounds, mBounds, mTask.getWindowConfiguration(),
1268                     mIsRecentTaskInvisible, null, null, mTask.getTaskInfo(),
1269                     topApp.checkEnterPictureInPictureAppOpsState());
1270             return mTarget;
1271         }
1272 
1273         void setSnapshotOverlay(TaskSnapshot snapshot) {
1274             // Create a surface control for the snapshot and reparent it to the leash
1275             final HardwareBuffer buffer = snapshot.getHardwareBuffer();
1276             if (buffer == null) {
1277                 return;
1278             }
1279 
1280             final SurfaceSession session = new SurfaceSession();
1281             mSnapshotOverlay = mService.mSurfaceControlFactory.apply(session)
1282                     .setName("RecentTaskScreenshotSurface")
1283                     .setCallsite("TaskAnimationAdapter.setSnapshotOverlay")
1284                     .setFormat(buffer.getFormat())
1285                     .setParent(mCapturedLeash)
1286                     .setBLASTLayer()
1287                     .build();
1288 
1289             final float scale = 1.0f * mTask.getBounds().width() / buffer.getWidth();
1290             mTask.getPendingTransaction()
1291                     .setBuffer(mSnapshotOverlay, GraphicBuffer.createFromHardwareBuffer(buffer))
1292                     .setColorSpace(mSnapshotOverlay, snapshot.getColorSpace())
1293                     .setLayer(mSnapshotOverlay, Integer.MAX_VALUE)
1294                     .setMatrix(mSnapshotOverlay, scale, 0, 0, scale)
1295                     .show(mSnapshotOverlay)
1296                     .apply();
1297         }
1298 
1299         void onRemove() {
1300             if (mSnapshotOverlay != null) {
1301                 // Clean up the snapshot overlay if necessary
1302                 mTask.getPendingTransaction()
1303                         .remove(mSnapshotOverlay)
1304                         .apply();
1305                 mSnapshotOverlay = null;
1306             }
1307             mTask.setCanAffectSystemUiFlags(true);
1308             mCapturedFinishCallback.onAnimationFinished(mLastAnimationType, this);
1309         }
1310 
1311         void onCleanup() {
1312             final Transaction pendingTransaction = mTask.getPendingTransaction();
1313             if (mFinishTransaction != null) {
1314                 // Reparent the overlay
1315                 if (mFinishOverlay != null) {
1316                     pendingTransaction.reparent(mFinishOverlay, mTask.mSurfaceControl);
1317                 }
1318 
1319                 // Transfer the transform from the leash to the task
1320                 PictureInPictureSurfaceTransaction.apply(mFinishTransaction,
1321                         mTask.mSurfaceControl, pendingTransaction);
1322                 mTask.setLastRecentsAnimationTransaction(mFinishTransaction, mFinishOverlay);
1323                 if (mDisplayContent.isFixedRotationLaunchingApp(mTargetActivityRecord)) {
1324                     // The transaction is needed for position when rotating the display.
1325                     mDisplayContent.mPinnedTaskController.setEnterPipTransaction(
1326                             mFinishTransaction);
1327                 }
1328                 mFinishTransaction = null;
1329                 mFinishOverlay = null;
1330                 pendingTransaction.apply();
1331 
1332                 // In the case where we are transferring the transform to the task in preparation
1333                 // for entering PIP, we disable the task being able to affect sysui flags otherwise
1334                 // it may cause a flash
1335                 if (mTask.getActivityType() != mTargetActivityType) {
1336                     mTask.setCanAffectSystemUiFlags(false);
1337                 }
1338             } else if (!mTask.isAttached()) {
1339                 // Apply the task's pending transaction in case it is detached and its transaction
1340                 // is not reachable.
1341                 pendingTransaction.apply();
1342             }
1343         }
1344 
1345         @VisibleForTesting
1346         public SurfaceControl getSnapshotOverlay() {
1347             return mSnapshotOverlay;
1348         }
1349 
1350         @Override
1351         public boolean getShowWallpaper() {
1352             return false;
1353         }
1354 
1355         @Override
1356         public void startAnimation(SurfaceControl animationLeash, Transaction t,
1357                 @AnimationType int type, @NonNull OnAnimationFinishedCallback finishCallback) {
1358             // Restore position and root task crop until client has a chance to modify it.
1359             t.setPosition(animationLeash, mLocalBounds.left, mLocalBounds.top);
1360             mTmpRect.set(mLocalBounds);
1361             mTmpRect.offsetTo(0, 0);
1362             t.setWindowCrop(animationLeash, mTmpRect);
1363             mCapturedLeash = animationLeash;
1364             mCapturedFinishCallback = finishCallback;
1365             mLastAnimationType = type;
1366         }
1367 
1368         @Override
1369         public void onAnimationCancelled(SurfaceControl animationLeash) {
1370             // Cancel the animation immediately if any single task animator is canceled
1371             cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "taskAnimationAdapterCanceled");
1372         }
1373 
1374         @Override
1375         public long getDurationHint() {
1376             return 0;
1377         }
1378 
1379         @Override
1380         public long getStatusBarTransitionsStartTime() {
1381             return SystemClock.uptimeMillis();
1382         }
1383 
1384         @Override
1385         public void dump(PrintWriter pw, String prefix) {
1386             pw.print(prefix); pw.println("task=" + mTask);
1387             if (mTarget != null) {
1388                 pw.print(prefix); pw.println("Target:");
1389                 mTarget.dump(pw, prefix + "  ");
1390             } else {
1391                 pw.print(prefix); pw.println("Target: null");
1392             }
1393             pw.println("mIsRecentTaskInvisible=" + mIsRecentTaskInvisible);
1394             pw.println("mLocalBounds=" + mLocalBounds);
1395             pw.println("mFinishTransaction=" + mFinishTransaction);
1396             pw.println("mBounds=" + mBounds);
1397             pw.println("mIsRecentTaskInvisible=" + mIsRecentTaskInvisible);
1398         }
1399 
1400         @Override
1401         public void dumpDebug(ProtoOutputStream proto) {
1402             final long token = proto.start(REMOTE);
1403             if (mTarget != null) {
1404                 mTarget.dumpDebug(proto, TARGET);
1405             }
1406             proto.end(token);
1407         }
1408     }
1409 
1410     public void dump(PrintWriter pw, String prefix) {
1411         final String innerPrefix = prefix + "  ";
1412         pw.print(prefix); pw.println(RecentsAnimationController.class.getSimpleName() + ":");
1413         pw.print(innerPrefix); pw.println("mPendingStart=" + mPendingStart);
1414         pw.print(innerPrefix); pw.println("mPendingAnimations=" + mPendingAnimations.size());
1415         pw.print(innerPrefix); pw.println("mCanceled=" + mCanceled);
1416         pw.print(innerPrefix); pw.println("mInputConsumerEnabled=" + mInputConsumerEnabled);
1417         pw.print(innerPrefix); pw.println("mTargetActivityRecord=" + mTargetActivityRecord);
1418         pw.print(innerPrefix); pw.println("isTargetOverWallpaper=" + isTargetOverWallpaper());
1419         pw.print(innerPrefix); pw.println("mRequestDeferCancelUntilNextTransition="
1420                 + mRequestDeferCancelUntilNextTransition);
1421         pw.print(innerPrefix); pw.println("mCancelOnNextTransitionStart="
1422                 + mCancelOnNextTransitionStart);
1423         pw.print(innerPrefix); pw.println("mCancelDeferredWithScreenshot="
1424                 + mCancelDeferredWithScreenshot);
1425         pw.print(innerPrefix); pw.println("mPendingCancelWithScreenshotReorderMode="
1426                 + mPendingCancelWithScreenshotReorderMode);
1427     }
1428 }
1429