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