Home | History | Annotate | Download | only in wm
      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 {
    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      */
    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      */
    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
    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
    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 
    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 
    387     void cancelAnimation(@ReorderMode int reorderMode, String reason) {
    388         cancelAnimation(reorderMode, false /* runSynchronously */, reason);
    389     }
    390 
    391     void cancelAnimationSynchronously(@ReorderMode int reorderMode, String reason) {
    392         cancelAnimation(reorderMode, true /* runSynchronously */, reason);
    393     }
    394 
    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 
    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 
    450     void scheduleFailsafe() {
    451         mService.mH.postDelayed(mFailsafeRunnable, FAILSAFE_DELAY);
    452     }
    453 
    454     private void linkToDeathOfRunner() throws RemoteException {
    455         if (!mLinkedToDeathOfRunner) {
    456             mRunner.asBinder().linkToDeath(this, 0);
    457             mLinkedToDeathOfRunner = true;
    458         }
    459     }
    460 
    461     private void unlinkToDeathOfRunner() {
    462         if (mLinkedToDeathOfRunner) {
    463             mRunner.asBinder().unlinkToDeath(this, 0);
    464             mLinkedToDeathOfRunner = false;
    465         }
    466     }
    467 
    468     @Override
    469     public void binderDied() {
    470         cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "binderDied");
    471     }
    472 
    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 
    484     boolean isSplitScreenMinimized() {
    485         return mSplitScreenMinimized;
    486     }
    487 
    488     boolean isWallpaperVisible(WindowState w) {
    489         return w != null && w.mAppToken != null && mTargetAppToken == w.mAppToken
    490                 && isTargetOverWallpaper();
    491     }
    492 
    493     boolean hasInputConsumerForApp(AppWindowToken appToken) {
    494         return mInputConsumerEnabled && isAnimatingApp(appToken);
    495     }
    496 
    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 
    512     boolean isTargetApp(AppWindowToken token) {
    513         return mTargetAppToken != null && token == mTargetAppToken;
    514     }
    515 
    516     private boolean isTargetOverWallpaper() {
    517         if (mTargetAppToken == null) {
    518             return false;
    519         }
    520         return mTargetAppToken.windowsCanBeWallpaperTarget();
    521     }
    522 
    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 
    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 
    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 
    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
    582         public boolean getDetachWallpaper() {
    583             return false;
    584         }
    585 
    586         @Override
    587         public boolean getShowWallpaper() {
    588             return false;
    589         }
    590 
    591         @Override
    592         public int getBackgroundColor() {
    593             return 0;
    594         }
    595 
    596         @Override
    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
    610         public void onAnimationCancelled(SurfaceControl animationLeash) {
    611             cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "taskAnimationAdapterCanceled");
    612         }
    613 
    614         @Override
    615         public long getDurationHint() {
    616             return 0;
    617         }
    618 
    619         @Override
    620         public long getStatusBarTransitionsStartTime() {
    621             return SystemClock.uptimeMillis();
    622         }
    623 
    624         @Override
    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
    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 
    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