1 /*
2  * Copyright (C) 2018 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.view.WindowManager.TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
20 
21 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_REMOTE_ANIMATIONS;
22 import static com.android.server.wm.AnimationAdapterProto.REMOTE;
23 import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET;
24 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
25 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
26 
27 import android.annotation.ColorInt;
28 import android.annotation.NonNull;
29 import android.annotation.Nullable;
30 import android.graphics.Point;
31 import android.graphics.Rect;
32 import android.os.Binder;
33 import android.os.Handler;
34 import android.os.IBinder.DeathRecipient;
35 import android.os.RemoteException;
36 import android.os.SystemClock;
37 import android.util.Slog;
38 import android.util.proto.ProtoOutputStream;
39 import android.view.IRemoteAnimationFinishedCallback;
40 import android.view.RemoteAnimationAdapter;
41 import android.view.RemoteAnimationTarget;
42 import android.view.SurfaceControl;
43 import android.view.SurfaceControl.Transaction;
44 import android.view.WindowManager;
45 
46 import com.android.internal.annotations.VisibleForTesting;
47 import com.android.internal.protolog.common.LogLevel;
48 import com.android.internal.protolog.common.ProtoLog;
49 import com.android.internal.util.FastPrintWriter;
50 import com.android.server.wm.SurfaceAnimator.AnimationType;
51 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
52 
53 import java.io.PrintWriter;
54 import java.io.StringWriter;
55 import java.util.ArrayList;
56 import java.util.function.Consumer;
57 
58 /**
59  * Helper class to run app animations in a remote process.
60  */
61 class RemoteAnimationController implements DeathRecipient {
62     private static final String TAG = TAG_WITH_CLASS_NAME
63             ? "RemoteAnimationController" : TAG_WM;
64     private static final long TIMEOUT_MS = 10000;
65 
66     private final WindowManagerService mService;
67     private final DisplayContent mDisplayContent;
68     private final RemoteAnimationAdapter mRemoteAnimationAdapter;
69     private final ArrayList<RemoteAnimationRecord> mPendingAnimations = new ArrayList<>();
70     private final ArrayList<WallpaperAnimationAdapter> mPendingWallpaperAnimations =
71             new ArrayList<>();
72     @VisibleForTesting
73     final ArrayList<NonAppWindowAnimationAdapter> mPendingNonAppAnimations = new ArrayList<>();
74     private final Handler mHandler;
75     private final Runnable mTimeoutRunnable = () -> cancelAnimation("timeoutRunnable");
76     private boolean mIsFinishing;
77 
78     private FinishedCallback mFinishedCallback;
79     private final boolean mIsActivityEmbedding;
80     private boolean mCanceled;
81     private boolean mLinkedToDeathOfRunner;
82     @Nullable
83     private Runnable mOnRemoteAnimationReady;
84 
RemoteAnimationController(WindowManagerService service, DisplayContent displayContent, RemoteAnimationAdapter remoteAnimationAdapter, Handler handler, boolean isActivityEmbedding)85     RemoteAnimationController(WindowManagerService service, DisplayContent displayContent,
86             RemoteAnimationAdapter remoteAnimationAdapter, Handler handler,
87             boolean isActivityEmbedding) {
88         mService = service;
89         mDisplayContent = displayContent;
90         mRemoteAnimationAdapter = remoteAnimationAdapter;
91         mHandler = handler;
92         mIsActivityEmbedding = isActivityEmbedding;
93     }
94 
95     /**
96      * Creates an animation record for each individual {@link WindowContainer}.
97      *
98      * @param windowContainer The windows to animate.
99      * @param position        The position app bounds relative to its parent.
100      * @param localBounds     The bounds of the app relative to its parent.
101      * @param endBounds       The end bounds after the transition, in screen coordinates.
102      * @param startBounds     The start bounds before the transition, in screen coordinates.
103      * @param showBackdrop    To show background behind a window during animation.
104      * @return The record representing animation(s) to run on the app.
105      */
createRemoteAnimationRecord(WindowContainer windowContainer, Point position, Rect localBounds, Rect endBounds, Rect startBounds, boolean showBackdrop)106     RemoteAnimationRecord createRemoteAnimationRecord(WindowContainer windowContainer,
107             Point position, Rect localBounds, Rect endBounds, Rect startBounds,
108             boolean showBackdrop) {
109         return createRemoteAnimationRecord(windowContainer, position, localBounds, endBounds,
110                 startBounds, showBackdrop, startBounds != null /* shouldCreateSnapshot */);
111     }
112 
113     /**
114      * Creates an animation record for each individual {@link WindowContainer}.
115      *
116      * @param windowContainer The windows to animate.
117      * @param position        The position app bounds relative to its parent.
118      * @param localBounds     The bounds of the app relative to its parent.
119      * @param endBounds       The end bounds after the transition, in screen coordinates.
120      * @param startBounds     The start bounds before the transition, in screen coordinates.
121      * @param showBackdrop    To show background behind a window during animation.
122      * @param shouldCreateSnapshot   Whether this target should create a snapshot animation.
123      * @return The record representing animation(s) to run on the app.
124      */
createRemoteAnimationRecord(WindowContainer windowContainer, Point position, Rect localBounds, Rect endBounds, Rect startBounds, boolean showBackdrop, boolean shouldCreateSnapshot)125     RemoteAnimationRecord createRemoteAnimationRecord(WindowContainer windowContainer,
126             Point position, Rect localBounds, Rect endBounds, Rect startBounds,
127             boolean showBackdrop, boolean shouldCreateSnapshot) {
128         ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "createAnimationAdapter(): container=%s",
129                 windowContainer);
130         final RemoteAnimationRecord adapters = new RemoteAnimationRecord(windowContainer, position,
131                 localBounds, endBounds, startBounds, showBackdrop, shouldCreateSnapshot);
132         mPendingAnimations.add(adapters);
133         return adapters;
134     }
135 
136     /** Sets callback to run before starting remote animation. */
setOnRemoteAnimationReady(@ullable Runnable onRemoteAnimationReady)137     void setOnRemoteAnimationReady(@Nullable Runnable onRemoteAnimationReady) {
138         mOnRemoteAnimationReady = onRemoteAnimationReady;
139     }
140 
141     /**
142      * We use isFromActivityEmbedding() in the server process to tell if we're running an
143      * Activity Embedding type remote animation, where animations are driven by the client.
144      * This is currently supporting features like showBackdrop where we need to load App XML.
145      */
isFromActivityEmbedding()146     public boolean isFromActivityEmbedding() {
147         return mIsActivityEmbedding;
148     }
149 
150     /**
151      * Called when the transition is ready to be started, and all leashes have been set up.
152      */
goodToGo(@indowManager.TransitionOldType int transit)153     void goodToGo(@WindowManager.TransitionOldType int transit) {
154         ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "goodToGo()");
155         if (mCanceled) {
156             ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS,
157                     "goodToGo(): Animation canceled already");
158             onAnimationFinished();
159             invokeAnimationCancelled("already_cancelled");
160             return;
161         }
162 
163         // Scale the timeout with the animator scale the controlling app is using.
164         mHandler.postDelayed(mTimeoutRunnable,
165                 (long) (TIMEOUT_MS * mService.getCurrentAnimatorScale()));
166         mFinishedCallback = new FinishedCallback(this);
167 
168         // Create the app targets
169         final RemoteAnimationTarget[] appTargets = createAppAnimations();
170         if (appTargets.length == 0 && !AppTransition.isKeyguardOccludeTransitOld(transit)) {
171             // Keyguard occlude transition can be executed before the occluding activity becomes
172             // visible. Even in this case, KeyguardService expects to receive binder call, so we
173             // don't cancel remote animation.
174             ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS,
175                     "goodToGo(): No apps to animate, mPendingAnimations=%d",
176                     mPendingAnimations.size());
177             onAnimationFinished();
178             invokeAnimationCancelled("no_app_targets");
179             return;
180         }
181 
182         if (mOnRemoteAnimationReady != null) {
183             mOnRemoteAnimationReady.run();
184             mOnRemoteAnimationReady = null;
185         }
186 
187         // Create the remote wallpaper animation targets (if any)
188         final RemoteAnimationTarget[] wallpaperTargets = createWallpaperAnimations();
189 
190         // Create the remote non app animation targets (if any)
191         final RemoteAnimationTarget[] nonAppTargets = createNonAppWindowAnimations(transit);
192 
193         mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
194             try {
195                 linkToDeathOfRunner();
196                 ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "goodToGo(): onAnimationStart,"
197                                 + " transit=%s, apps=%d, wallpapers=%d, nonApps=%d",
198                         AppTransition.appTransitionOldToString(transit), appTargets.length,
199                         wallpaperTargets.length, nonAppTargets.length);
200                 if (AppTransition.isKeyguardOccludeTransitOld(transit)) {
201                     EventLogTags.writeWmSetKeyguardOccluded(
202                             transit == TRANSIT_OLD_KEYGUARD_UNOCCLUDE ? 0 : 1,
203                             1 /* animate */,
204                             transit,
205                             "onAnimationStart");
206                 }
207                 mRemoteAnimationAdapter.getRunner().onAnimationStart(transit, appTargets,
208                         wallpaperTargets, nonAppTargets, mFinishedCallback);
209             } catch (RemoteException e) {
210                 Slog.e(TAG, "Failed to start remote animation", e);
211                 onAnimationFinished();
212             }
213             if (ProtoLog.isEnabled(WM_DEBUG_REMOTE_ANIMATIONS, LogLevel.DEBUG)) {
214                 ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "startAnimation(): Notify animation start:");
215                 writeStartDebugStatement();
216             }
217         });
218         setRunningRemoteAnimation(true);
219     }
220 
cancelAnimation(String reason)221     void cancelAnimation(String reason) {
222         ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "cancelAnimation(): reason=%s", reason);
223         synchronized (mService.getWindowManagerLock()) {
224             if (mCanceled) {
225                 return;
226             }
227             mCanceled = true;
228         }
229         onAnimationFinished();
230         invokeAnimationCancelled(reason);
231     }
232 
writeStartDebugStatement()233     private void writeStartDebugStatement() {
234         ProtoLog.i(WM_DEBUG_REMOTE_ANIMATIONS, "Starting remote animation");
235         final StringWriter sw = new StringWriter();
236         final FastPrintWriter pw = new FastPrintWriter(sw);
237         for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
238             mPendingAnimations.get(i).mAdapter.dump(pw, "");
239         }
240         pw.close();
241         ProtoLog.i(WM_DEBUG_REMOTE_ANIMATIONS, "%s", sw.toString());
242     }
243 
createAppAnimations()244     private RemoteAnimationTarget[] createAppAnimations() {
245         ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "createAppAnimations()");
246         final ArrayList<RemoteAnimationTarget> targets = new ArrayList<>();
247         for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
248             final RemoteAnimationRecord wrappers = mPendingAnimations.get(i);
249             final RemoteAnimationTarget target = wrappers.createRemoteAnimationTarget();
250             if (target != null) {
251                 ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "\tAdd container=%s",
252                         wrappers.mWindowContainer);
253                 targets.add(target);
254             } else {
255                 ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "\tRemove container=%s",
256                         wrappers.mWindowContainer);
257 
258                 // We can't really start an animation but we still need to make sure to finish the
259                 // pending animation that was started by SurfaceAnimator
260                 if (wrappers.mAdapter != null
261                         && wrappers.mAdapter.mCapturedFinishCallback != null) {
262                     wrappers.mAdapter.mCapturedFinishCallback
263                             .onAnimationFinished(wrappers.mAdapter.mAnimationType,
264                                     wrappers.mAdapter);
265                 }
266                 if (wrappers.mThumbnailAdapter != null
267                         && wrappers.mThumbnailAdapter.mCapturedFinishCallback != null) {
268                     wrappers.mThumbnailAdapter.mCapturedFinishCallback
269                             .onAnimationFinished(wrappers.mThumbnailAdapter.mAnimationType,
270                                     wrappers.mThumbnailAdapter);
271                 }
272                 mPendingAnimations.remove(i);
273             }
274         }
275         return targets.toArray(new RemoteAnimationTarget[targets.size()]);
276     }
277 
createWallpaperAnimations()278     private RemoteAnimationTarget[] createWallpaperAnimations() {
279         ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "createWallpaperAnimations()");
280         return WallpaperAnimationAdapter.startWallpaperAnimations(mDisplayContent,
281                 mRemoteAnimationAdapter.getDuration(),
282                 mRemoteAnimationAdapter.getStatusBarTransitionDelay(),
283                 adapter -> {
284                     synchronized (mService.mGlobalLock) {
285                         // If the wallpaper animation is canceled, continue with the app animation
286                         mPendingWallpaperAnimations.remove(adapter);
287                     }
288                 }, mPendingWallpaperAnimations);
289     }
290 
291     private RemoteAnimationTarget[] createNonAppWindowAnimations(
292             @WindowManager.TransitionOldType int transit) {
293         ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "createNonAppWindowAnimations()");
294         return NonAppWindowAnimationAdapter.startNonAppWindowAnimations(mService,
295                 mDisplayContent,
296                 transit,
297                 mRemoteAnimationAdapter.getDuration(),
298                 mRemoteAnimationAdapter.getStatusBarTransitionDelay(),
299                 mPendingNonAppAnimations);
300     }
301 
302     private void onAnimationFinished() {
303         ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "onAnimationFinished(): mPendingAnimations=%d",
304                 mPendingAnimations.size());
305         mHandler.removeCallbacks(mTimeoutRunnable);
306         synchronized (mService.mGlobalLock) {
307             mIsFinishing = true;
308             unlinkToDeathOfRunner();
309             releaseFinishedCallback();
310             try {
311                 ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS,
312                         "onAnimationFinished(): Notify animation finished:");
313                 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
314                     final RemoteAnimationRecord adapters = mPendingAnimations.get(i);
315                     if (adapters.mAdapter != null) {
316                         adapters.mAdapter.mCapturedFinishCallback
317                                 .onAnimationFinished(adapters.mAdapter.mAnimationType,
318                                         adapters.mAdapter);
319                     }
320                     if (adapters.mThumbnailAdapter != null) {
321                         adapters.mThumbnailAdapter.mCapturedFinishCallback
322                                 .onAnimationFinished(adapters.mThumbnailAdapter.mAnimationType,
323                                         adapters.mThumbnailAdapter);
324                     }
325                     mPendingAnimations.remove(i);
326                     ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "\tcontainer=%s",
327                             adapters.mWindowContainer);
328                 }
329 
330                 for (int i = mPendingWallpaperAnimations.size() - 1; i >= 0; i--) {
331                     final WallpaperAnimationAdapter adapter = mPendingWallpaperAnimations.get(i);
332                     adapter.getLeashFinishedCallback().onAnimationFinished(
333                             adapter.getLastAnimationType(), adapter);
334                     mPendingWallpaperAnimations.remove(i);
335                     ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "\twallpaper=%s", adapter.getToken());
336                 }
337 
338                 for (int i = mPendingNonAppAnimations.size() - 1; i >= 0; i--) {
339                     final NonAppWindowAnimationAdapter adapter = mPendingNonAppAnimations.get(i);
340                     adapter.getLeashFinishedCallback().onAnimationFinished(
341                             adapter.getLastAnimationType(), adapter);
342                     mPendingNonAppAnimations.remove(i);
343                     ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "\tnonApp=%s",
344                             adapter.getWindowContainer());
345                 }
346             } catch (Exception e) {
347                 Slog.e(TAG, "Failed to finish remote animation", e);
348                 throw e;
349             } finally {
350                 mIsFinishing = false;
351             }
352             // Reset input for all activities when the remote animation is finished.
353             final Consumer<ActivityRecord> updateActivities =
354                     activity -> activity.setDropInputForAnimation(false);
355             mDisplayContent.forAllActivities(updateActivities);
356         }
357         setRunningRemoteAnimation(false);
358         ProtoLog.i(WM_DEBUG_REMOTE_ANIMATIONS, "Finishing remote animation");
359     }
360 
361     private void invokeAnimationCancelled(String reason) {
362         ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "cancelAnimation(): reason=%s", reason);
363         try {
364             mRemoteAnimationAdapter.getRunner().onAnimationCancelled();
365         } catch (RemoteException e) {
366             Slog.e(TAG, "Failed to notify cancel", e);
367         }
368         mOnRemoteAnimationReady = null;
369     }
370 
371     private void releaseFinishedCallback() {
372         if (mFinishedCallback != null) {
373             mFinishedCallback.release();
374             mFinishedCallback = null;
375         }
376     }
377 
378     private void setRunningRemoteAnimation(boolean running) {
379         final int pid = mRemoteAnimationAdapter.getCallingPid();
380         final int uid = mRemoteAnimationAdapter.getCallingUid();
381 
382         if (pid == 0) {
383             throw new RuntimeException("Calling pid of remote animation was null");
384         }
385         final WindowProcessController wpc = mService.mAtmService.getProcessController(pid, uid);
386         if (wpc == null) {
387             Slog.w(TAG, "Unable to find process with pid=" + pid + " uid=" + uid);
388             return;
389         }
390         wpc.setRunningRemoteAnimation(running);
391     }
392 
393     private void linkToDeathOfRunner() throws RemoteException {
394         if (!mLinkedToDeathOfRunner) {
395             mRemoteAnimationAdapter.getRunner().asBinder().linkToDeath(this, 0);
396             mLinkedToDeathOfRunner = true;
397         }
398     }
399 
400     private void unlinkToDeathOfRunner() {
401         if (mLinkedToDeathOfRunner) {
402             mRemoteAnimationAdapter.getRunner().asBinder().unlinkToDeath(this, 0);
403             mLinkedToDeathOfRunner = false;
404         }
405     }
406 
407     @Override
408     public void binderDied() {
409         cancelAnimation("binderDied");
410     }
411 
412     private static final class FinishedCallback extends IRemoteAnimationFinishedCallback.Stub {
413 
414         RemoteAnimationController mOuter;
415 
416         FinishedCallback(RemoteAnimationController outer) {
417             mOuter = outer;
418         }
419 
420         @Override
421         public void onAnimationFinished() throws RemoteException {
422             ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "app-onAnimationFinished(): mOuter=%s", mOuter);
423             final long token = Binder.clearCallingIdentity();
424             try {
425                 if (mOuter != null) {
426                     mOuter.onAnimationFinished();
427 
428                     // In case the client holds on to the finish callback, make sure we don't leak
429                     // RemoteAnimationController which in turn would leak the runner on the client.
430                     mOuter = null;
431                 }
432             } finally {
433                 Binder.restoreCallingIdentity(token);
434             }
435         }
436 
437         /**
438          * Marks this callback as not be used anymore by releasing the reference to the outer class
439          * to prevent memory leak.
440          */
441         void release() {
442             ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "app-release(): mOuter=%s", mOuter);
443             mOuter = null;
444         }
445     };
446 
447     /**
448      * Contains information about a remote-animation for one WindowContainer. This keeps track of,
449      * potentially, multiple animating surfaces (AdapterWrappers) associated with one
450      * Window/Transition. For example, a change transition has an adapter controller for the
451      * main window and an adapter controlling the start-state snapshot.
452      * <p>
453      * This can be thought of as a bridge between the information that the remote animator sees (via
454      * {@link RemoteAnimationTarget}) and what the server sees (the
455      * {@link RemoteAnimationAdapterWrapper}(s) interfacing with the moving surfaces).
456      */
457     public class RemoteAnimationRecord {
458         RemoteAnimationAdapterWrapper mAdapter;
459         RemoteAnimationAdapterWrapper mThumbnailAdapter = null;
460         RemoteAnimationTarget mTarget;
461         final WindowContainer mWindowContainer;
462         final Rect mStartBounds;
463         final boolean mShowBackdrop;
464         @ColorInt int mBackdropColor = 0;
465         private @RemoteAnimationTarget.Mode int mMode = RemoteAnimationTarget.MODE_CHANGING;
466 
467         RemoteAnimationRecord(WindowContainer windowContainer, Point endPos, Rect localBounds,
468                 Rect endBounds, @Nullable Rect startBounds, boolean showBackdrop,
469                 boolean shouldCreateSnapshot) {
470             mWindowContainer = windowContainer;
471             mShowBackdrop = showBackdrop;
472             if (startBounds != null) {
473                 mStartBounds = new Rect(startBounds);
474                 mAdapter = new RemoteAnimationAdapterWrapper(this, endPos, localBounds, endBounds,
475                         mStartBounds, mShowBackdrop);
476                 if (shouldCreateSnapshot && mRemoteAnimationAdapter.getChangeNeedsSnapshot()) {
477                     final Rect thumbnailLocalBounds = new Rect(startBounds);
478                     thumbnailLocalBounds.offsetTo(0, 0);
479                     // Snapshot is located at (0,0) of the animation leash. It doesn't have size
480                     // change, so the startBounds is its end bounds, and no start bounds for it.
481                     mThumbnailAdapter = new RemoteAnimationAdapterWrapper(this, new Point(0, 0),
482                             thumbnailLocalBounds, startBounds, new Rect(), mShowBackdrop);
483                 }
484             } else {
485                 mAdapter = new RemoteAnimationAdapterWrapper(this, endPos, localBounds, endBounds,
486                         new Rect(), mShowBackdrop);
487                 mStartBounds = null;
488             }
489         }
490 
491         void setBackDropColor(@ColorInt int backdropColor) {
492             mBackdropColor = backdropColor;
493         }
494 
495         RemoteAnimationTarget createRemoteAnimationTarget() {
496             if (mAdapter == null
497                     || mAdapter.mCapturedFinishCallback == null
498                     || mAdapter.mCapturedLeash == null) {
499                 return null;
500             }
501             mTarget = mWindowContainer.createRemoteAnimationTarget(this);
502             return mTarget;
503         }
504 
505         void setMode(@RemoteAnimationTarget.Mode int mode) {
506             mMode = mode;
507         }
508 
509         int getMode() {
510             return mMode;
511         }
512 
513         /** Whether its parent is also an animation target in the same transition. */
514         boolean hasAnimatingParent() {
515             // mOpeningApps and mClosingApps are only activities, so only need to check
516             // mChangingContainers.
517             for (int i = mDisplayContent.mChangingContainers.size() - 1; i >= 0; i--) {
518                 if (mWindowContainer.isDescendantOf(
519                         mDisplayContent.mChangingContainers.valueAt(i))) {
520                     return true;
521                 }
522             }
523             return false;
524         }
525     }
526 
527     class RemoteAnimationAdapterWrapper implements AnimationAdapter {
528         private final RemoteAnimationRecord mRecord;
529         SurfaceControl mCapturedLeash;
530         private OnAnimationFinishedCallback mCapturedFinishCallback;
531         private @AnimationType int mAnimationType;
532         final Point mPosition = new Point();
533         final Rect mLocalBounds;
534         final Rect mEndBounds = new Rect();
535         final Rect mStartBounds = new Rect();
536         final boolean mShowBackdrop;
537 
538         RemoteAnimationAdapterWrapper(RemoteAnimationRecord record, Point position,
539                 Rect localBounds, Rect endBounds, Rect startBounds, boolean showBackdrop) {
540             mRecord = record;
541             mPosition.set(position.x, position.y);
542             mLocalBounds = localBounds;
543             mEndBounds.set(endBounds);
544             mStartBounds.set(startBounds);
545             mShowBackdrop = showBackdrop;
546         }
547 
548         @Override
549         @ColorInt
550         public int getBackgroundColor() {
551             return mRecord.mBackdropColor;
552         }
553 
554         @Override
555         public boolean getShowBackground() {
556             return mShowBackdrop;
557         }
558 
559         @Override
560         public boolean getShowWallpaper() {
561             return false;
562         }
563 
564         @Override
565         public void startAnimation(SurfaceControl animationLeash, Transaction t,
566                 @AnimationType int type, @NonNull OnAnimationFinishedCallback finishCallback) {
567             ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "startAnimation");
568 
569             if (mStartBounds.isEmpty()) {
570                 // Restore position and stack crop until client has a chance to modify it.
571                 t.setPosition(animationLeash, mPosition.x, mPosition.y);
572                 t.setWindowCrop(animationLeash, mEndBounds.width(), mEndBounds.height());
573             } else {
574                 // Offset the change animation leash to the relative start position in parent.
575                 // (mPosition) is the relative end position in parent container.
576                 // (mStartBounds - mEndBounds) is the position difference between start and end.
577                 // (mPosition + mStartBounds - mEndBounds) will be the relative start position.
578                 t.setPosition(animationLeash, mPosition.x + mStartBounds.left - mEndBounds.left,
579                         mPosition.y + mStartBounds.top - mEndBounds.top);
580                 t.setWindowCrop(animationLeash, mStartBounds.width(), mStartBounds.height());
581             }
582             mCapturedLeash = animationLeash;
583             mCapturedFinishCallback = finishCallback;
584             mAnimationType = type;
585         }
586 
587         @Override
588         public void onAnimationCancelled(SurfaceControl animationLeash) {
589             if (mIsFinishing) {
590                 return;
591             }
592             if (mRecord.mAdapter == this) {
593                 mRecord.mAdapter = null;
594             } else {
595                 mRecord.mThumbnailAdapter = null;
596             }
597             if (mRecord.mAdapter == null && mRecord.mThumbnailAdapter == null) {
598                 mPendingAnimations.remove(mRecord);
599             }
600             if (mPendingAnimations.isEmpty()) {
601                 cancelAnimation("allAppAnimationsCanceled");
602             }
603         }
604 
605         @Override
606         public long getDurationHint() {
607             return mRemoteAnimationAdapter.getDuration();
608         }
609 
610         @Override
611         public long getStatusBarTransitionsStartTime() {
612             return SystemClock.uptimeMillis()
613                     + mRemoteAnimationAdapter.getStatusBarTransitionDelay();
614         }
615 
616         @Override
617         public void dump(PrintWriter pw, String prefix) {
618             pw.print(prefix); pw.print("container="); pw.println(mRecord.mWindowContainer);
619             if (mRecord.mTarget != null) {
620                 pw.print(prefix); pw.println("Target:");
621                 mRecord.mTarget.dump(pw, prefix + "  ");
622             } else {
623                 pw.print(prefix); pw.println("Target: null");
624             }
625         }
626 
627         @Override
628         public void dumpDebug(ProtoOutputStream proto) {
629             final long token = proto.start(REMOTE);
630             if (mRecord.mTarget != null) {
631                 mRecord.mTarget.dumpDebug(proto, TARGET);
632             }
633             proto.end(token);
634         }
635     }
636 }
637