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.ActivityManagerInternal.APP_TRANSITION_RECENTS_ANIM;
20 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
21 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
22 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
23 import static android.view.RemoteAnimationTarget.MODE_CLOSING;
24 import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
25 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
26 import static com.android.server.wm.AnimationAdapterProto.REMOTE;
27 import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET;
28 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_RECENTS_ANIMATIONS;
29 import static com.android.server.wm.WindowManagerService.H.NOTIFY_APP_TRANSITION_STARTING;
30 
31 import android.annotation.IntDef;
32 import android.app.ActivityManager.TaskSnapshot;
33 import android.app.WindowConfiguration;
34 import android.graphics.Point;
35 import android.graphics.Rect;
36 import android.os.Binder;
37 import android.os.IBinder.DeathRecipient;
38 import android.os.RemoteException;
39 import android.os.SystemClock;
40 import android.util.ArraySet;
41 import android.util.Slog;
42 import android.util.SparseBooleanArray;
43 import android.util.SparseIntArray;
44 import android.util.proto.ProtoOutputStream;
45 import android.view.IRecentsAnimationController;
46 import android.view.IRecentsAnimationRunner;
47 import android.view.RemoteAnimationTarget;
48 import android.view.SurfaceControl;
49 import android.view.SurfaceControl.Transaction;
50 import android.view.inputmethod.InputMethodManagerInternal;
51 import com.android.internal.annotations.VisibleForTesting;
52 import com.android.server.LocalServices;
53 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
54 import com.android.server.wm.utils.InsetUtils;
55 import com.google.android.collect.Sets;
56 import java.io.PrintWriter;
57 import java.util.ArrayList;
58 
59 /**
60  * Controls a single instance of the remote driven recents animation. In particular, this allows
61  * the calling SystemUI to animate the visible task windows as a part of the transition. The remote
62  * runner is provided an animation controller which allows it to take screenshots and to notify
63  * window manager when the animation is completed. In addition, window manager may also notify the
64  * app if it requires the animation to be canceled at any time (ie. due to timeout, etc.)
65  */
66 public class RecentsAnimationController implements DeathRecipient {
67     private static final String TAG = RecentsAnimationController.class.getSimpleName();
68     private static final long FAILSAFE_DELAY = 1000;
69 
70     public static final int REORDER_KEEP_IN_PLACE = 0;
71     public static final int REORDER_MOVE_TO_TOP = 1;
72     public static final int REORDER_MOVE_TO_ORIGINAL_POSITION = 2;
73 
74     @IntDef(prefix = { "REORDER_MODE_" }, value = {
75             REORDER_KEEP_IN_PLACE,
76             REORDER_MOVE_TO_TOP,
77             REORDER_MOVE_TO_ORIGINAL_POSITION
78     })
79     public @interface ReorderMode {}
80 
81     private final WindowManagerService mService;
82     private IRecentsAnimationRunner mRunner;
83     private final RecentsAnimationCallbacks mCallbacks;
84     private final ArrayList<TaskAnimationAdapter> mPendingAnimations = new ArrayList<>();
85     private final int mDisplayId;
86     private final Runnable mFailsafeRunnable = () ->
87             cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "failSafeRunnable");
88 
89     // The recents component app token that is shown behind the visibile tasks
90     private AppWindowToken mTargetAppToken;
91     private Rect mMinimizedHomeBounds = new Rect();
92 
93     // We start the RecentsAnimationController in a pending-start state since we need to wait for
94     // the wallpaper/activity to draw before we can give control to the handler to start animating
95     // the visible task surfaces
96     private boolean mPendingStart = true;
97 
98     // Set when the animation has been canceled
99     private boolean mCanceled;
100 
101     // Whether or not the input consumer is enabled. The input consumer must be both registered and
102     // enabled for it to start intercepting touch events.
103     private boolean mInputConsumerEnabled;
104 
105     // Whether or not the recents animation should cause the primary split-screen stack to be
106     // minimized
107     private boolean mSplitScreenMinimized;
108 
109     private final Rect mTmpRect = new Rect();
110 
111     private boolean mLinkedToDeathOfRunner;
112 
113     public interface RecentsAnimationCallbacks {
onAnimationFinished(@eorderMode int reorderMode, boolean runSychronously)114         void onAnimationFinished(@ReorderMode int reorderMode, boolean runSychronously);
115     }
116 
117     private final IRecentsAnimationController mController =
118             new IRecentsAnimationController.Stub() {
119 
120         @Override
121         public TaskSnapshot screenshotTask(int taskId) {
122             if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "screenshotTask(" + taskId + "):"
123                     + " mCanceled=" + mCanceled);
124             final long token = Binder.clearCallingIdentity();
125             try {
126                 synchronized (mService.getWindowManagerLock()) {
127                     if (mCanceled) {
128                         return null;
129                     }
130                     for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
131                         final TaskAnimationAdapter adapter = mPendingAnimations.get(i);
132                         final Task task = adapter.mTask;
133                         if (task.mTaskId == taskId) {
134                             final TaskSnapshotController snapshotController =
135                                     mService.mTaskSnapshotController;
136                             final ArraySet<Task> tasks = Sets.newArraySet(task);
137                             snapshotController.snapshotTasks(tasks);
138                             snapshotController.addSkipClosingAppSnapshotTasks(tasks);
139                             return snapshotController.getSnapshot(taskId, 0 /* userId */,
140                                     false /* restoreFromDisk */, false /* reducedResolution */);
141                         }
142                     }
143                     return null;
144                 }
145             } finally {
146                 Binder.restoreCallingIdentity(token);
147             }
148         }
149 
150         @Override
151         public void finish(boolean moveHomeToTop) {
152             if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "finish(" + moveHomeToTop + "):"
153                     + " mCanceled=" + mCanceled);
154             final long token = Binder.clearCallingIdentity();
155             try {
156                 synchronized (mService.getWindowManagerLock()) {
157                     if (mCanceled) {
158                         return;
159                     }
160                 }
161 
162                 // Note, the callback will handle its own synchronization, do not lock on WM lock
163                 // prior to calling the callback
164                 mCallbacks.onAnimationFinished(moveHomeToTop
165                         ? REORDER_MOVE_TO_TOP
166                         : REORDER_MOVE_TO_ORIGINAL_POSITION,
167                         true /* runSynchronously */);
168             } finally {
169                 Binder.restoreCallingIdentity(token);
170             }
171         }
172 
173         @Override
174         public void setAnimationTargetsBehindSystemBars(boolean behindSystemBars)
175                 throws RemoteException {
176             final long token = Binder.clearCallingIdentity();
177             try {
178                 synchronized (mService.getWindowManagerLock()) {
179                     for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
180                         mPendingAnimations.get(i).mTask.setCanAffectSystemUiFlags(behindSystemBars);
181                     }
182                     mService.mWindowPlacerLocked.requestTraversal();
183                 }
184             } finally {
185                 Binder.restoreCallingIdentity(token);
186             }
187         }
188 
189         @Override
190         public void setInputConsumerEnabled(boolean enabled) {
191             if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "setInputConsumerEnabled(" + enabled + "):"
192                     + " mCanceled=" + mCanceled);
193             final long token = Binder.clearCallingIdentity();
194             try {
195                 synchronized (mService.getWindowManagerLock()) {
196                     if (mCanceled) {
197                         return;
198                     }
199 
200                     mInputConsumerEnabled = enabled;
201                     mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
202                     mService.scheduleAnimationLocked();
203                 }
204             } finally {
205                 Binder.restoreCallingIdentity(token);
206             }
207         }
208 
209         @Override
210         public void setSplitScreenMinimized(boolean minimized) {
211             final long token = Binder.clearCallingIdentity();
212             try {
213                 synchronized (mService.getWindowManagerLock()) {
214                     if (mCanceled) {
215                         return;
216                     }
217 
218                     mSplitScreenMinimized = minimized;
219                     mService.checkSplitScreenMinimizedChanged(true /* animate */);
220                 }
221             } finally {
222                 Binder.restoreCallingIdentity(token);
223             }
224         }
225 
226         @Override
227         public void hideCurrentInputMethod() {
228             final long token = Binder.clearCallingIdentity();
229             try {
230                 final InputMethodManagerInternal inputMethodManagerInternal =
231                         LocalServices.getService(InputMethodManagerInternal.class);
232                 if (inputMethodManagerInternal != null) {
233                     inputMethodManagerInternal.hideCurrentInputMethod();
234                 }
235             } finally {
236                 Binder.restoreCallingIdentity(token);
237             }
238         }
239     };
240 
241     /**
242      * @param remoteAnimationRunner The remote runner which should be notified when the animation is
243      *                              ready to start or has been canceled
244      * @param callbacks Callbacks to be made when the animation finishes
245      */
RecentsAnimationController(WindowManagerService service, IRecentsAnimationRunner remoteAnimationRunner, RecentsAnimationCallbacks callbacks, int displayId)246     RecentsAnimationController(WindowManagerService service,
247             IRecentsAnimationRunner remoteAnimationRunner, RecentsAnimationCallbacks callbacks,
248             int displayId) {
249         mService = service;
250         mRunner = remoteAnimationRunner;
251         mCallbacks = callbacks;
252         mDisplayId = displayId;
253     }
254 
255     /**
256      * Initializes the recents animation controller. This is a separate call from the constructor
257      * because it may call cancelAnimation() which needs to properly clean up the controller
258      * in the window manager.
259      */
initialize(int targetActivityType, SparseBooleanArray recentTaskIds)260     public void initialize(int targetActivityType, SparseBooleanArray recentTaskIds) {
261         // Make leashes for each of the visible tasks and add it to the recents animation to be
262         // started
263         final DisplayContent dc = mService.mRoot.getDisplayContent(mDisplayId);
264         final ArrayList<Task> visibleTasks = dc.getVisibleTasks();
265         final int taskCount = visibleTasks.size();
266         for (int i = 0; i < taskCount; i++) {
267             final Task task = visibleTasks.get(i);
268             final WindowConfiguration config = task.getWindowConfiguration();
269             if (config.tasksAreFloating()
270                     || config.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
271                     || config.getActivityType() == targetActivityType) {
272                 continue;
273             }
274             addAnimation(task, !recentTaskIds.get(task.mTaskId));
275         }
276 
277         // Skip the animation if there is nothing to animate
278         if (mPendingAnimations.isEmpty()) {
279             cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "initialize-noVisibleTasks");
280             return;
281         }
282 
283         try {
284             linkToDeathOfRunner();
285         } catch (RemoteException e) {
286             cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "initialize-failedToLinkToDeath");
287             return;
288         }
289 
290         // Adjust the wallpaper visibility for the showing target activity
291         final AppWindowToken recentsComponentAppToken = dc.getStack(WINDOWING_MODE_UNDEFINED,
292                 targetActivityType).getTopChild().getTopFullscreenAppToken();
293         if (recentsComponentAppToken != null) {
294             if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "setHomeApp("
295                     + recentsComponentAppToken.getName() + ")");
296             mTargetAppToken = recentsComponentAppToken;
297             if (recentsComponentAppToken.windowsCanBeWallpaperTarget()) {
298                 dc.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
299                 dc.setLayoutNeeded();
300             }
301         }
302 
303         // Save the minimized home height
304         dc.getDockedDividerController().getHomeStackBoundsInDockedMode(mMinimizedHomeBounds);
305 
306         mService.mWindowPlacerLocked.performSurfacePlacement();
307     }
308 
309     @VisibleForTesting
addAnimation(Task task, boolean isRecentTaskInvisible)310     AnimationAdapter addAnimation(Task task, boolean isRecentTaskInvisible) {
311         if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "addAnimation(" + task.getName() + ")");
312         // TODO: Refactor this to use the task's animator
313         final SurfaceAnimator anim = new SurfaceAnimator(task, null /* animationFinishedCallback */,
314                 mService);
315         final TaskAnimationAdapter taskAdapter = new TaskAnimationAdapter(task,
316                 isRecentTaskInvisible);
317         anim.startAnimation(task.getPendingTransaction(), taskAdapter, false /* hidden */);
318         task.commitPendingTransaction();
319         mPendingAnimations.add(taskAdapter);
320         return taskAdapter;
321     }
322 
323     @VisibleForTesting
removeAnimation(TaskAnimationAdapter taskAdapter)324     void removeAnimation(TaskAnimationAdapter taskAdapter) {
325         if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "removeAnimation("
326                 + taskAdapter.mTask.mTaskId + ")");
327         taskAdapter.mTask.setCanAffectSystemUiFlags(true);
328         taskAdapter.mCapturedFinishCallback.onAnimationFinished(taskAdapter);
329         mPendingAnimations.remove(taskAdapter);
330     }
331 
startAnimation()332     void startAnimation() {
333         if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "startAnimation(): mPendingStart=" + mPendingStart
334                 + " mCanceled=" + mCanceled);
335         if (!mPendingStart || mCanceled) {
336             // Skip starting if we've already started or canceled the animation
337             return;
338         }
339         try {
340             final ArrayList<RemoteAnimationTarget> appAnimations = new ArrayList<>();
341             for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
342                 final TaskAnimationAdapter taskAdapter = mPendingAnimations.get(i);
343                 final RemoteAnimationTarget target = taskAdapter.createRemoteAnimationApp();
344                 if (target != null) {
345                     appAnimations.add(target);
346                 } else {
347                     removeAnimation(taskAdapter);
348                 }
349             }
350 
351             // Skip the animation if there is nothing to animate
352             if (appAnimations.isEmpty()) {
353                 cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "startAnimation-noAppWindows");
354                 return;
355             }
356 
357             final RemoteAnimationTarget[] appTargets = appAnimations.toArray(
358                     new RemoteAnimationTarget[appAnimations.size()]);
359             mPendingStart = false;
360 
361             final Rect minimizedHomeBounds = mTargetAppToken != null
362                     && mTargetAppToken.inSplitScreenSecondaryWindowingMode()
363                             ? mMinimizedHomeBounds
364                             : null;
365             final Rect contentInsets = mTargetAppToken != null
366                     && mTargetAppToken.findMainWindow() != null
367                             ? mTargetAppToken.findMainWindow().mContentInsets
368                             : null;
369             mRunner.onAnimationStart(mController, appTargets, contentInsets,
370                     minimizedHomeBounds);
371             if (DEBUG_RECENTS_ANIMATIONS) {
372                 Slog.d(TAG, "startAnimation(): Notify animation start:");
373                 for (int i = 0; i < mPendingAnimations.size(); i++) {
374                     final Task task = mPendingAnimations.get(i).mTask;
375                     Slog.d(TAG, "\t" + task.mTaskId);
376                 }
377             }
378         } catch (RemoteException e) {
379             Slog.e(TAG, "Failed to start recents animation", e);
380         }
381         final SparseIntArray reasons = new SparseIntArray();
382         reasons.put(WINDOWING_MODE_FULLSCREEN, APP_TRANSITION_RECENTS_ANIM);
383         mService.mH.obtainMessage(NOTIFY_APP_TRANSITION_STARTING,
384                 reasons).sendToTarget();
385     }
386 
cancelAnimation(@eorderMode int reorderMode, String reason)387     void cancelAnimation(@ReorderMode int reorderMode, String reason) {
388         cancelAnimation(reorderMode, false /* runSynchronously */, reason);
389     }
390 
cancelAnimationSynchronously(@eorderMode int reorderMode, String reason)391     void cancelAnimationSynchronously(@ReorderMode int reorderMode, String reason) {
392         cancelAnimation(reorderMode, true /* runSynchronously */, reason);
393     }
394 
cancelAnimation(@eorderMode int reorderMode, boolean runSynchronously, String reason)395     private void cancelAnimation(@ReorderMode int reorderMode, boolean runSynchronously,
396             String reason) {
397         if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG, "cancelAnimation(): reason=" + reason
398                 + " runSynchronously=" + runSynchronously);
399         synchronized (mService.getWindowManagerLock()) {
400             if (mCanceled) {
401                 // We've already canceled the animation
402                 return;
403             }
404             mService.mH.removeCallbacks(mFailsafeRunnable);
405             mCanceled = true;
406             try {
407                 mRunner.onAnimationCanceled();
408             } catch (RemoteException e) {
409                 Slog.e(TAG, "Failed to cancel recents animation", e);
410             }
411         }
412 
413         // Clean up and return to the previous app
414         mCallbacks.onAnimationFinished(reorderMode, runSynchronously);
415     }
416 
cleanupAnimation(@eorderMode int reorderMode)417     void cleanupAnimation(@ReorderMode int reorderMode) {
418         if (DEBUG_RECENTS_ANIMATIONS) Slog.d(TAG,
419                 "cleanupAnimation(): Notify animation finished mPendingAnimations="
420                         + mPendingAnimations.size() + " reorderMode=" + reorderMode);
421         for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
422             final TaskAnimationAdapter taskAdapter = mPendingAnimations.get(i);
423             if (reorderMode == REORDER_MOVE_TO_TOP || reorderMode == REORDER_KEEP_IN_PLACE) {
424                 taskAdapter.mTask.dontAnimateDimExit();
425             }
426             removeAnimation(taskAdapter);
427         }
428 
429         // Clear any pending failsafe runnables
430         mService.mH.removeCallbacks(mFailsafeRunnable);
431 
432         // Clear references to the runner
433         unlinkToDeathOfRunner();
434         mRunner = null;
435         mCanceled = true;
436 
437         // Clear associated input consumers
438         mService.mInputMonitor.updateInputWindowsLw(true /*force*/);
439         mService.destroyInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION);
440 
441         // We have deferred all notifications to the target app as a part of the recents animation,
442         // so if we are actually transitioning there, notify again here
443         if (mTargetAppToken != null) {
444             if (reorderMode == REORDER_MOVE_TO_TOP || reorderMode == REORDER_KEEP_IN_PLACE) {
445                 mService.mAppTransition.notifyAppTransitionFinishedLocked(mTargetAppToken.token);
446             }
447         }
448     }
449 
scheduleFailsafe()450     void scheduleFailsafe() {
451         mService.mH.postDelayed(mFailsafeRunnable, FAILSAFE_DELAY);
452     }
453 
linkToDeathOfRunner()454     private void linkToDeathOfRunner() throws RemoteException {
455         if (!mLinkedToDeathOfRunner) {
456             mRunner.asBinder().linkToDeath(this, 0);
457             mLinkedToDeathOfRunner = true;
458         }
459     }
460 
unlinkToDeathOfRunner()461     private void unlinkToDeathOfRunner() {
462         if (mLinkedToDeathOfRunner) {
463             mRunner.asBinder().unlinkToDeath(this, 0);
464             mLinkedToDeathOfRunner = false;
465         }
466     }
467 
468     @Override
binderDied()469     public void binderDied() {
470         cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "binderDied");
471     }
472 
checkAnimationReady(WallpaperController wallpaperController)473     void checkAnimationReady(WallpaperController wallpaperController) {
474         if (mPendingStart) {
475             final boolean wallpaperReady = !isTargetOverWallpaper()
476                     || (wallpaperController.getWallpaperTarget() != null
477                             && wallpaperController.wallpaperTransitionReady());
478             if (wallpaperReady) {
479                 mService.getRecentsAnimationController().startAnimation();
480             }
481         }
482     }
483 
isSplitScreenMinimized()484     boolean isSplitScreenMinimized() {
485         return mSplitScreenMinimized;
486     }
487 
isWallpaperVisible(WindowState w)488     boolean isWallpaperVisible(WindowState w) {
489         return w != null && w.mAppToken != null && mTargetAppToken == w.mAppToken
490                 && isTargetOverWallpaper();
491     }
492 
hasInputConsumerForApp(AppWindowToken appToken)493     boolean hasInputConsumerForApp(AppWindowToken appToken) {
494         return mInputConsumerEnabled && isAnimatingApp(appToken);
495     }
496 
updateInputConsumerForApp(InputConsumerImpl recentsAnimationInputConsumer, boolean hasFocus)497     boolean updateInputConsumerForApp(InputConsumerImpl recentsAnimationInputConsumer,
498             boolean hasFocus) {
499         // Update the input consumer touchable region to match the target app main window
500         final WindowState targetAppMainWindow = mTargetAppToken != null
501                 ? mTargetAppToken.findMainWindow()
502                 : null;
503         if (targetAppMainWindow != null) {
504             targetAppMainWindow.getBounds(mTmpRect);
505             recentsAnimationInputConsumer.mWindowHandle.hasFocus = hasFocus;
506             recentsAnimationInputConsumer.mWindowHandle.touchableRegion.set(mTmpRect);
507             return true;
508         }
509         return false;
510     }
511 
isTargetApp(AppWindowToken token)512     boolean isTargetApp(AppWindowToken token) {
513         return mTargetAppToken != null && token == mTargetAppToken;
514     }
515 
isTargetOverWallpaper()516     private boolean isTargetOverWallpaper() {
517         if (mTargetAppToken == null) {
518             return false;
519         }
520         return mTargetAppToken.windowsCanBeWallpaperTarget();
521     }
522 
isAnimatingTask(Task task)523     boolean isAnimatingTask(Task task) {
524         for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
525             if (task == mPendingAnimations.get(i).mTask) {
526                 return true;
527             }
528         }
529         return false;
530     }
531 
isAnimatingApp(AppWindowToken appToken)532     private boolean isAnimatingApp(AppWindowToken appToken) {
533         for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
534             final Task task = mPendingAnimations.get(i).mTask;
535             for (int j = task.getChildCount() - 1; j >= 0; j--) {
536                 final AppWindowToken app = task.getChildAt(j);
537                 if (app == appToken) {
538                     return true;
539                 }
540             }
541         }
542         return false;
543     }
544 
545     @VisibleForTesting
546     class TaskAnimationAdapter implements AnimationAdapter {
547 
548         private final Task mTask;
549         private SurfaceControl mCapturedLeash;
550         private OnAnimationFinishedCallback mCapturedFinishCallback;
551         private final boolean mIsRecentTaskInvisible;
552         private RemoteAnimationTarget mTarget;
553         private final Point mPosition = new Point();
554         private final Rect mBounds = new Rect();
555 
TaskAnimationAdapter(Task task, boolean isRecentTaskInvisible)556         TaskAnimationAdapter(Task task, boolean isRecentTaskInvisible) {
557             mTask = task;
558             mIsRecentTaskInvisible = isRecentTaskInvisible;
559             final WindowContainer container = mTask.getParent();
560             container.getRelativePosition(mPosition);
561             container.getBounds(mBounds);
562         }
563 
createRemoteAnimationApp()564         RemoteAnimationTarget createRemoteAnimationApp() {
565             final AppWindowToken topApp = mTask.getTopVisibleAppToken();
566             final WindowState mainWindow = topApp != null
567                     ? topApp.findMainWindow()
568                     : null;
569             if (mainWindow == null) {
570                 return null;
571             }
572             final Rect insets = new Rect(mainWindow.mContentInsets);
573             InsetUtils.addInsets(insets, mainWindow.mAppToken.getLetterboxInsets());
574             mTarget = new RemoteAnimationTarget(mTask.mTaskId, MODE_CLOSING, mCapturedLeash,
575                     !topApp.fillsParent(), mainWindow.mWinAnimator.mLastClipRect,
576                     insets, mTask.getPrefixOrderIndex(), mPosition, mBounds,
577                     mTask.getWindowConfiguration(), mIsRecentTaskInvisible);
578             return mTarget;
579         }
580 
581         @Override
getDetachWallpaper()582         public boolean getDetachWallpaper() {
583             return false;
584         }
585 
586         @Override
getShowWallpaper()587         public boolean getShowWallpaper() {
588             return false;
589         }
590 
591         @Override
getBackgroundColor()592         public int getBackgroundColor() {
593             return 0;
594         }
595 
596         @Override
startAnimation(SurfaceControl animationLeash, Transaction t, OnAnimationFinishedCallback finishCallback)597         public void startAnimation(SurfaceControl animationLeash, Transaction t,
598                 OnAnimationFinishedCallback finishCallback) {
599             // Restore z-layering, position and stack crop until client has a chance to modify it.
600             t.setLayer(animationLeash, mTask.getPrefixOrderIndex());
601             t.setPosition(animationLeash, mPosition.x, mPosition.y);
602             mTmpRect.set(mBounds);
603             mTmpRect.offsetTo(0, 0);
604             t.setWindowCrop(animationLeash, mTmpRect);
605             mCapturedLeash = animationLeash;
606             mCapturedFinishCallback = finishCallback;
607         }
608 
609         @Override
onAnimationCancelled(SurfaceControl animationLeash)610         public void onAnimationCancelled(SurfaceControl animationLeash) {
611             cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "taskAnimationAdapterCanceled");
612         }
613 
614         @Override
getDurationHint()615         public long getDurationHint() {
616             return 0;
617         }
618 
619         @Override
getStatusBarTransitionsStartTime()620         public long getStatusBarTransitionsStartTime() {
621             return SystemClock.uptimeMillis();
622         }
623 
624         @Override
dump(PrintWriter pw, String prefix)625         public void dump(PrintWriter pw, String prefix) {
626             pw.print(prefix); pw.println("task=" + mTask);
627             if (mTarget != null) {
628                 pw.print(prefix); pw.println("Target:");
629                 mTarget.dump(pw, prefix + "  ");
630             } else {
631                 pw.print(prefix); pw.println("Target: null");
632             }
633             pw.println("mIsRecentTaskInvisible=" + mIsRecentTaskInvisible);
634             pw.println("mPosition=" + mPosition);
635             pw.println("mBounds=" + mBounds);
636             pw.println("mIsRecentTaskInvisible=" + mIsRecentTaskInvisible);
637         }
638 
639         @Override
writeToProto(ProtoOutputStream proto)640         public void writeToProto(ProtoOutputStream proto) {
641             final long token = proto.start(REMOTE);
642             if (mTarget != null) {
643                 mTarget.writeToProto(proto, TARGET);
644             }
645             proto.end(token);
646         }
647     }
648 
dump(PrintWriter pw, String prefix)649     public void dump(PrintWriter pw, String prefix) {
650         final String innerPrefix = prefix + "  ";
651         pw.print(prefix); pw.println(RecentsAnimationController.class.getSimpleName() + ":");
652         pw.print(innerPrefix); pw.println("mPendingStart=" + mPendingStart);
653         pw.print(innerPrefix); pw.println("mCanceled=" + mCanceled);
654         pw.print(innerPrefix); pw.println("mInputConsumerEnabled=" + mInputConsumerEnabled);
655         pw.print(innerPrefix); pw.println("mSplitScreenMinimized=" + mSplitScreenMinimized);
656         pw.print(innerPrefix); pw.println("mTargetAppToken=" + mTargetAppToken);
657         pw.print(innerPrefix); pw.println("isTargetOverWallpaper=" + isTargetOverWallpaper());
658     }
659 }
660