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.ProtoLogImpl;
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 (ProtoLogImpl.isEnabled(WM_DEBUG_REMOTE_ANIMATIONS)) {
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             mService.openSurfaceTransaction();
311             try {
312                 ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS,
313                         "onAnimationFinished(): Notify animation finished:");
314                 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
315                     final RemoteAnimationRecord adapters = mPendingAnimations.get(i);
316                     if (adapters.mAdapter != null) {
317                         adapters.mAdapter.mCapturedFinishCallback
318                                 .onAnimationFinished(adapters.mAdapter.mAnimationType,
319                                         adapters.mAdapter);
320                     }
321                     if (adapters.mThumbnailAdapter != null) {
322                         adapters.mThumbnailAdapter.mCapturedFinishCallback
323                                 .onAnimationFinished(adapters.mThumbnailAdapter.mAnimationType,
324                                         adapters.mThumbnailAdapter);
325                     }
326                     mPendingAnimations.remove(i);
327                     ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "\tcontainer=%s",
328                             adapters.mWindowContainer);
329                 }
330 
331                 for (int i = mPendingWallpaperAnimations.size() - 1; i >= 0; i--) {
332                     final WallpaperAnimationAdapter adapter = mPendingWallpaperAnimations.get(i);
333                     adapter.getLeashFinishedCallback().onAnimationFinished(
334                             adapter.getLastAnimationType(), adapter);
335                     mPendingWallpaperAnimations.remove(i);
336                     ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "\twallpaper=%s", adapter.getToken());
337                 }
338 
339                 for (int i = mPendingNonAppAnimations.size() - 1; i >= 0; i--) {
340                     final NonAppWindowAnimationAdapter adapter = mPendingNonAppAnimations.get(i);
341                     adapter.getLeashFinishedCallback().onAnimationFinished(
342                             adapter.getLastAnimationType(), adapter);
343                     mPendingNonAppAnimations.remove(i);
344                     ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "\tnonApp=%s",
345                             adapter.getWindowContainer());
346                 }
347             } catch (Exception e) {
348                 Slog.e(TAG, "Failed to finish remote animation", e);
349                 throw e;
350             } finally {
351                 mService.closeSurfaceTransaction("RemoteAnimationController#finished");
352                 mIsFinishing = false;
353             }
354             // Reset input for all activities when the remote animation is finished.
355             final Consumer<ActivityRecord> updateActivities =
356                     activity -> activity.setDropInputForAnimation(false);
357             mDisplayContent.forAllActivities(updateActivities);
358         }
359         setRunningRemoteAnimation(false);
360         ProtoLog.i(WM_DEBUG_REMOTE_ANIMATIONS, "Finishing remote animation");
361     }
362 
363     private void invokeAnimationCancelled(String reason) {
364         ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "cancelAnimation(): reason=%s", reason);
365         try {
366             mRemoteAnimationAdapter.getRunner().onAnimationCancelled();
367         } catch (RemoteException e) {
368             Slog.e(TAG, "Failed to notify cancel", e);
369         }
370         mOnRemoteAnimationReady = null;
371     }
372 
373     private void releaseFinishedCallback() {
374         if (mFinishedCallback != null) {
375             mFinishedCallback.release();
376             mFinishedCallback = null;
377         }
378     }
379 
380     private void setRunningRemoteAnimation(boolean running) {
381         final int pid = mRemoteAnimationAdapter.getCallingPid();
382         final int uid = mRemoteAnimationAdapter.getCallingUid();
383 
384         if (pid == 0) {
385             throw new RuntimeException("Calling pid of remote animation was null");
386         }
387         final WindowProcessController wpc = mService.mAtmService.getProcessController(pid, uid);
388         if (wpc == null) {
389             Slog.w(TAG, "Unable to find process with pid=" + pid + " uid=" + uid);
390             return;
391         }
392         wpc.setRunningRemoteAnimation(running);
393     }
394 
395     private void linkToDeathOfRunner() throws RemoteException {
396         if (!mLinkedToDeathOfRunner) {
397             mRemoteAnimationAdapter.getRunner().asBinder().linkToDeath(this, 0);
398             mLinkedToDeathOfRunner = true;
399         }
400     }
401 
402     private void unlinkToDeathOfRunner() {
403         if (mLinkedToDeathOfRunner) {
404             mRemoteAnimationAdapter.getRunner().asBinder().unlinkToDeath(this, 0);
405             mLinkedToDeathOfRunner = false;
406         }
407     }
408 
409     @Override
410     public void binderDied() {
411         cancelAnimation("binderDied");
412     }
413 
414     private static final class FinishedCallback extends IRemoteAnimationFinishedCallback.Stub {
415 
416         RemoteAnimationController mOuter;
417 
418         FinishedCallback(RemoteAnimationController outer) {
419             mOuter = outer;
420         }
421 
422         @Override
423         public void onAnimationFinished() throws RemoteException {
424             ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "app-onAnimationFinished(): mOuter=%s", mOuter);
425             final long token = Binder.clearCallingIdentity();
426             try {
427                 if (mOuter != null) {
428                     mOuter.onAnimationFinished();
429 
430                     // In case the client holds on to the finish callback, make sure we don't leak
431                     // RemoteAnimationController which in turn would leak the runner on the client.
432                     mOuter = null;
433                 }
434             } finally {
435                 Binder.restoreCallingIdentity(token);
436             }
437         }
438 
439         /**
440          * Marks this callback as not be used anymore by releasing the reference to the outer class
441          * to prevent memory leak.
442          */
443         void release() {
444             ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "app-release(): mOuter=%s", mOuter);
445             mOuter = null;
446         }
447     };
448 
449     /**
450      * Contains information about a remote-animation for one WindowContainer. This keeps track of,
451      * potentially, multiple animating surfaces (AdapterWrappers) associated with one
452      * Window/Transition. For example, a change transition has an adapter controller for the
453      * main window and an adapter controlling the start-state snapshot.
454      * <p>
455      * This can be thought of as a bridge between the information that the remote animator sees (via
456      * {@link RemoteAnimationTarget}) and what the server sees (the
457      * {@link RemoteAnimationAdapterWrapper}(s) interfacing with the moving surfaces).
458      */
459     public class RemoteAnimationRecord {
460         RemoteAnimationAdapterWrapper mAdapter;
461         RemoteAnimationAdapterWrapper mThumbnailAdapter = null;
462         RemoteAnimationTarget mTarget;
463         final WindowContainer mWindowContainer;
464         final Rect mStartBounds;
465         final boolean mShowBackdrop;
466         @ColorInt int mBackdropColor = 0;
467         private @RemoteAnimationTarget.Mode int mMode = RemoteAnimationTarget.MODE_CHANGING;
468 
469         RemoteAnimationRecord(WindowContainer windowContainer, Point endPos, Rect localBounds,
470                 Rect endBounds, @Nullable Rect startBounds, boolean showBackdrop,
471                 boolean shouldCreateSnapshot) {
472             mWindowContainer = windowContainer;
473             mShowBackdrop = showBackdrop;
474             if (startBounds != null) {
475                 mStartBounds = new Rect(startBounds);
476                 mAdapter = new RemoteAnimationAdapterWrapper(this, endPos, localBounds, endBounds,
477                         mStartBounds, mShowBackdrop);
478                 if (shouldCreateSnapshot && mRemoteAnimationAdapter.getChangeNeedsSnapshot()) {
479                     final Rect thumbnailLocalBounds = new Rect(startBounds);
480                     thumbnailLocalBounds.offsetTo(0, 0);
481                     // Snapshot is located at (0,0) of the animation leash. It doesn't have size
482                     // change, so the startBounds is its end bounds, and no start bounds for it.
483                     mThumbnailAdapter = new RemoteAnimationAdapterWrapper(this, new Point(0, 0),
484                             thumbnailLocalBounds, startBounds, new Rect(), mShowBackdrop);
485                 }
486             } else {
487                 mAdapter = new RemoteAnimationAdapterWrapper(this, endPos, localBounds, endBounds,
488                         new Rect(), mShowBackdrop);
489                 mStartBounds = null;
490             }
491         }
492 
493         void setBackDropColor(@ColorInt int backdropColor) {
494             mBackdropColor = backdropColor;
495         }
496 
497         RemoteAnimationTarget createRemoteAnimationTarget() {
498             if (mAdapter == null
499                     || mAdapter.mCapturedFinishCallback == null
500                     || mAdapter.mCapturedLeash == null) {
501                 return null;
502             }
503             mTarget = mWindowContainer.createRemoteAnimationTarget(this);
504             return mTarget;
505         }
506 
507         void setMode(@RemoteAnimationTarget.Mode int mode) {
508             mMode = mode;
509         }
510 
511         int getMode() {
512             return mMode;
513         }
514 
515         /** Whether its parent is also an animation target in the same transition. */
516         boolean hasAnimatingParent() {
517             // mOpeningApps and mClosingApps are only activities, so only need to check
518             // mChangingContainers.
519             for (int i = mDisplayContent.mChangingContainers.size() - 1; i >= 0; i--) {
520                 if (mWindowContainer.isDescendantOf(
521                         mDisplayContent.mChangingContainers.valueAt(i))) {
522                     return true;
523                 }
524             }
525             return false;
526         }
527     }
528 
529     class RemoteAnimationAdapterWrapper implements AnimationAdapter {
530         private final RemoteAnimationRecord mRecord;
531         SurfaceControl mCapturedLeash;
532         private OnAnimationFinishedCallback mCapturedFinishCallback;
533         private @AnimationType int mAnimationType;
534         final Point mPosition = new Point();
535         final Rect mLocalBounds;
536         final Rect mEndBounds = new Rect();
537         final Rect mStartBounds = new Rect();
538         final boolean mShowBackdrop;
539 
540         RemoteAnimationAdapterWrapper(RemoteAnimationRecord record, Point position,
541                 Rect localBounds, Rect endBounds, Rect startBounds, boolean showBackdrop) {
542             mRecord = record;
543             mPosition.set(position.x, position.y);
544             mLocalBounds = localBounds;
545             mEndBounds.set(endBounds);
546             mStartBounds.set(startBounds);
547             mShowBackdrop = showBackdrop;
548         }
549 
550         @Override
551         @ColorInt
552         public int getBackgroundColor() {
553             return mRecord.mBackdropColor;
554         }
555 
556         @Override
557         public boolean getShowBackground() {
558             return mShowBackdrop;
559         }
560 
561         @Override
562         public boolean getShowWallpaper() {
563             return false;
564         }
565 
566         @Override
567         public void startAnimation(SurfaceControl animationLeash, Transaction t,
568                 @AnimationType int type, @NonNull OnAnimationFinishedCallback finishCallback) {
569             ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "startAnimation");
570 
571             if (mStartBounds.isEmpty()) {
572                 // Restore position and stack crop until client has a chance to modify it.
573                 t.setPosition(animationLeash, mPosition.x, mPosition.y);
574                 t.setWindowCrop(animationLeash, mEndBounds.width(), mEndBounds.height());
575             } else {
576                 // Offset the change animation leash to the relative start position in parent.
577                 // (mPosition) is the relative end position in parent container.
578                 // (mStartBounds - mEndBounds) is the position difference between start and end.
579                 // (mPosition + mStartBounds - mEndBounds) will be the relative start position.
580                 t.setPosition(animationLeash, mPosition.x + mStartBounds.left - mEndBounds.left,
581                         mPosition.y + mStartBounds.top - mEndBounds.top);
582                 t.setWindowCrop(animationLeash, mStartBounds.width(), mStartBounds.height());
583             }
584             mCapturedLeash = animationLeash;
585             mCapturedFinishCallback = finishCallback;
586             mAnimationType = type;
587         }
588 
589         @Override
590         public void onAnimationCancelled(SurfaceControl animationLeash) {
591             if (mIsFinishing) {
592                 return;
593             }
594             if (mRecord.mAdapter == this) {
595                 mRecord.mAdapter = null;
596             } else {
597                 mRecord.mThumbnailAdapter = null;
598             }
599             if (mRecord.mAdapter == null && mRecord.mThumbnailAdapter == null) {
600                 mPendingAnimations.remove(mRecord);
601             }
602             if (mPendingAnimations.isEmpty()) {
603                 cancelAnimation("allAppAnimationsCanceled");
604             }
605         }
606 
607         @Override
608         public long getDurationHint() {
609             return mRemoteAnimationAdapter.getDuration();
610         }
611 
612         @Override
613         public long getStatusBarTransitionsStartTime() {
614             return SystemClock.uptimeMillis()
615                     + mRemoteAnimationAdapter.getStatusBarTransitionDelay();
616         }
617 
618         @Override
619         public void dump(PrintWriter pw, String prefix) {
620             pw.print(prefix); pw.print("container="); pw.println(mRecord.mWindowContainer);
621             if (mRecord.mTarget != null) {
622                 pw.print(prefix); pw.println("Target:");
623                 mRecord.mTarget.dump(pw, prefix + "  ");
624             } else {
625                 pw.print(prefix); pw.println("Target: null");
626             }
627         }
628 
629         @Override
630         public void dumpDebug(ProtoOutputStream proto) {
631             final long token = proto.start(REMOTE);
632             if (mRecord.mTarget != null) {
633                 mRecord.mTarget.dumpDebug(proto, TARGET);
634             }
635             proto.end(token);
636         }
637     }
638 }
639