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