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