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