• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 com.android.server.wm.AnimationAdapterProto.REMOTE;
20 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_REMOTE_ANIMATIONS;
21 import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET;
22 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
23 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
24 
25 import android.graphics.Point;
26 import android.graphics.Rect;
27 import android.os.Binder;
28 import android.os.Handler;
29 import android.os.IBinder.DeathRecipient;
30 import android.os.RemoteException;
31 import android.os.SystemClock;
32 import android.util.Slog;
33 import android.util.proto.ProtoOutputStream;
34 import android.view.IRemoteAnimationFinishedCallback;
35 import android.view.RemoteAnimationAdapter;
36 import android.view.RemoteAnimationTarget;
37 import android.view.SurfaceControl;
38 import android.view.SurfaceControl.Transaction;
39 
40 import com.android.internal.util.FastPrintWriter;
41 import com.android.server.protolog.ProtoLogImpl;
42 import com.android.server.protolog.common.ProtoLog;
43 import com.android.server.wm.SurfaceAnimator.AnimationType;
44 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
45 
46 import java.io.PrintWriter;
47 import java.io.StringWriter;
48 import java.util.ArrayList;
49 
50 /**
51  * Helper class to run app animations in a remote process.
52  */
53 class RemoteAnimationController implements DeathRecipient {
54     private static final String TAG = TAG_WITH_CLASS_NAME
55                     ? "RemoteAnimationController" : TAG_WM;
56     private static final long TIMEOUT_MS = 2000;
57 
58     private final WindowManagerService mService;
59     private final RemoteAnimationAdapter mRemoteAnimationAdapter;
60     private final ArrayList<RemoteAnimationRecord> mPendingAnimations = new ArrayList<>();
61     private final ArrayList<WallpaperAnimationAdapter> mPendingWallpaperAnimations =
62             new ArrayList<>();
63     private final Rect mTmpRect = new Rect();
64     private final Handler mHandler;
65     private final Runnable mTimeoutRunnable = () -> cancelAnimation("timeoutRunnable");
66 
67     private FinishedCallback mFinishedCallback;
68     private boolean mCanceled;
69     private boolean mLinkedToDeathOfRunner;
70 
RemoteAnimationController(WindowManagerService service, RemoteAnimationAdapter remoteAnimationAdapter, Handler handler)71     RemoteAnimationController(WindowManagerService service,
72             RemoteAnimationAdapter remoteAnimationAdapter, Handler handler) {
73         mService = service;
74         mRemoteAnimationAdapter = remoteAnimationAdapter;
75         mHandler = handler;
76     }
77 
78     /**
79      * Creates an animation record for each individual {@link WindowContainer}.
80      *
81      * @param windowContainer The windows to animate.
82      * @param position The position app bounds, in screen coordinates.
83      * @param localBounds The bounds of the app relative to its parent.
84      * @param stackBounds The stack bounds of the app relative to position.
85      * @param startBounds The stack bounds before the transition, in screen coordinates
86      * @return The record representing animation(s) to run on the app.
87      */
createRemoteAnimationRecord(WindowContainer windowContainer, Point position, Rect localBounds, Rect stackBounds, Rect startBounds)88     RemoteAnimationRecord createRemoteAnimationRecord(WindowContainer windowContainer,
89             Point position, Rect localBounds, Rect stackBounds, Rect startBounds) {
90         ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "createAnimationAdapter(): container=%s",
91                 windowContainer);
92         final RemoteAnimationRecord adapters = new RemoteAnimationRecord(windowContainer, position,
93                 localBounds, stackBounds, startBounds);
94         mPendingAnimations.add(adapters);
95         return adapters;
96     }
97 
98     /**
99      * Called when the transition is ready to be started, and all leashes have been set up.
100      */
goodToGo()101     void goodToGo() {
102         ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "goodToGo()");
103         if (mPendingAnimations.isEmpty() || mCanceled) {
104             ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS,
105                     "goodToGo(): Animation finished already, canceled=%s mPendingAnimations=%d",
106                     mCanceled, mPendingAnimations.size());
107             onAnimationFinished();
108             return;
109         }
110 
111         // Scale the timeout with the animator scale the controlling app is using.
112         mHandler.postDelayed(mTimeoutRunnable,
113                 (long) (TIMEOUT_MS * mService.getCurrentAnimatorScale()));
114         mFinishedCallback = new FinishedCallback(this);
115 
116         // Create the app targets
117         final RemoteAnimationTarget[] appTargets = createAppAnimations();
118         if (appTargets.length == 0) {
119             ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "goodToGo(): No apps to animate");
120             onAnimationFinished();
121             return;
122         }
123 
124         // Create the remote wallpaper animation targets (if any)
125         final RemoteAnimationTarget[] wallpaperTargets = createWallpaperAnimations();
126         mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
127             try {
128                 linkToDeathOfRunner();
129                 mRemoteAnimationAdapter.getRunner().onAnimationStart(appTargets, wallpaperTargets,
130                         mFinishedCallback);
131             } catch (RemoteException e) {
132                 Slog.e(TAG, "Failed to start remote animation", e);
133                 onAnimationFinished();
134             }
135             if (ProtoLogImpl.isEnabled(WM_DEBUG_REMOTE_ANIMATIONS)) {
136                 ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "startAnimation(): Notify animation start:");
137                 writeStartDebugStatement();
138             }
139         });
140         setRunningRemoteAnimation(true);
141     }
142 
cancelAnimation(String reason)143     void cancelAnimation(String reason) {
144         ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "cancelAnimation(): reason=%s", reason);
145         synchronized (mService.getWindowManagerLock()) {
146             if (mCanceled) {
147                 return;
148             }
149             mCanceled = true;
150         }
151         onAnimationFinished();
152         invokeAnimationCancelled();
153     }
154 
writeStartDebugStatement()155     private void writeStartDebugStatement() {
156         ProtoLog.i(WM_DEBUG_REMOTE_ANIMATIONS, "Starting remote animation");
157         final StringWriter sw = new StringWriter();
158         final FastPrintWriter pw = new FastPrintWriter(sw);
159         for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
160             mPendingAnimations.get(i).mAdapter.dump(pw, "");
161         }
162         pw.close();
163         ProtoLog.i(WM_DEBUG_REMOTE_ANIMATIONS, "%s", sw.toString());
164     }
165 
createAppAnimations()166     private RemoteAnimationTarget[] createAppAnimations() {
167         ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "createAppAnimations()");
168         final ArrayList<RemoteAnimationTarget> targets = new ArrayList<>();
169         for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
170             final RemoteAnimationRecord wrappers = mPendingAnimations.get(i);
171             final RemoteAnimationTarget target = wrappers.createRemoteAnimationTarget();
172             if (target != null) {
173                 ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "\tAdd container=%s",
174                         wrappers.mWindowContainer);
175                 targets.add(target);
176             } else {
177                 ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "\tRemove container=%s",
178                         wrappers.mWindowContainer);
179 
180                 // We can't really start an animation but we still need to make sure to finish the
181                 // pending animation that was started by SurfaceAnimator
182                 if (wrappers.mAdapter != null
183                         && wrappers.mAdapter.mCapturedFinishCallback != null) {
184                     wrappers.mAdapter.mCapturedFinishCallback
185                             .onAnimationFinished(wrappers.mAdapter.mAnimationType,
186                                     wrappers.mAdapter);
187                 }
188                 if (wrappers.mThumbnailAdapter != null
189                         && wrappers.mThumbnailAdapter.mCapturedFinishCallback != null) {
190                     wrappers.mThumbnailAdapter.mCapturedFinishCallback
191                             .onAnimationFinished(wrappers.mAdapter.mAnimationType,
192                                     wrappers.mThumbnailAdapter);
193                 }
194                 mPendingAnimations.remove(i);
195             }
196         }
197         return targets.toArray(new RemoteAnimationTarget[targets.size()]);
198     }
199 
createWallpaperAnimations()200     private RemoteAnimationTarget[] createWallpaperAnimations() {
201         ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "createWallpaperAnimations()");
202         return WallpaperAnimationAdapter.startWallpaperAnimations(mService,
203                 mRemoteAnimationAdapter.getDuration(),
204                 mRemoteAnimationAdapter.getStatusBarTransitionDelay(),
205                 adapter -> {
206                     synchronized (mService.mGlobalLock) {
207                         // If the wallpaper animation is canceled, continue with the app animation
208                         mPendingWallpaperAnimations.remove(adapter);
209                     }
210                 }, mPendingWallpaperAnimations);
211     }
212 
213     private void onAnimationFinished() {
214         ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "onAnimationFinished(): mPendingAnimations=%d",
215                 mPendingAnimations.size());
216         mHandler.removeCallbacks(mTimeoutRunnable);
217         synchronized (mService.mGlobalLock) {
218             unlinkToDeathOfRunner();
219             releaseFinishedCallback();
220             mService.openSurfaceTransaction();
221             try {
222                 ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS,
223                         "onAnimationFinished(): Notify animation finished:");
224                 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
225                     final RemoteAnimationRecord adapters = mPendingAnimations.get(i);
226                     if (adapters.mAdapter != null) {
227                         adapters.mAdapter.mCapturedFinishCallback
228                                 .onAnimationFinished(adapters.mAdapter.mAnimationType,
229                                         adapters.mAdapter);
230                     }
231                     if (adapters.mThumbnailAdapter != null) {
232                         adapters.mThumbnailAdapter.mCapturedFinishCallback
233                                 .onAnimationFinished(adapters.mAdapter.mAnimationType,
234                                         adapters.mThumbnailAdapter);
235                     }
236                     mPendingAnimations.remove(i);
237                     ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "\tcontainer=%s",
238                             adapters.mWindowContainer);
239                 }
240 
241                 for (int i = mPendingWallpaperAnimations.size() - 1; i >= 0; i--) {
242                     final WallpaperAnimationAdapter adapter = mPendingWallpaperAnimations.get(i);
243                     adapter.getLeashFinishedCallback().onAnimationFinished(
244                             adapter.getLastAnimationType(), adapter);
245                     mPendingWallpaperAnimations.remove(i);
246                     ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "\twallpaper=%s", adapter.getToken());
247                 }
248             } catch (Exception e) {
249                 Slog.e(TAG, "Failed to finish remote animation", e);
250                 throw e;
251             } finally {
252                 mService.closeSurfaceTransaction("RemoteAnimationController#finished");
253             }
254         }
255         setRunningRemoteAnimation(false);
256         ProtoLog.i(WM_DEBUG_REMOTE_ANIMATIONS, "Finishing remote animation");
257     }
258 
259     private void invokeAnimationCancelled() {
260         try {
261             mRemoteAnimationAdapter.getRunner().onAnimationCancelled();
262         } catch (RemoteException e) {
263             Slog.e(TAG, "Failed to notify cancel", e);
264         }
265     }
266 
267     private void releaseFinishedCallback() {
268         if (mFinishedCallback != null) {
269             mFinishedCallback.release();
270             mFinishedCallback = null;
271         }
272     }
273 
274     private void setRunningRemoteAnimation(boolean running) {
275         final int pid = mRemoteAnimationAdapter.getCallingPid();
276         final int uid = mRemoteAnimationAdapter.getCallingUid();
277         if (pid == 0) {
278             throw new RuntimeException("Calling pid of remote animation was null");
279         }
280         final WindowProcessController wpc = mService.mAtmService.getProcessController(pid, uid);
281         if (wpc == null) {
282             Slog.w(TAG, "Unable to find process with pid=" + pid + " uid=" + uid);
283             return;
284         }
285         wpc.setRunningRemoteAnimation(running);
286     }
287 
288     private void linkToDeathOfRunner() throws RemoteException {
289         if (!mLinkedToDeathOfRunner) {
290             mRemoteAnimationAdapter.getRunner().asBinder().linkToDeath(this, 0);
291             mLinkedToDeathOfRunner = true;
292         }
293     }
294 
295     private void unlinkToDeathOfRunner() {
296         if (mLinkedToDeathOfRunner) {
297             mRemoteAnimationAdapter.getRunner().asBinder().unlinkToDeath(this, 0);
298             mLinkedToDeathOfRunner = false;
299         }
300     }
301 
302     @Override
303     public void binderDied() {
304         cancelAnimation("binderDied");
305     }
306 
307     private static final class FinishedCallback extends IRemoteAnimationFinishedCallback.Stub {
308 
309         RemoteAnimationController mOuter;
310 
311         FinishedCallback(RemoteAnimationController outer) {
312             mOuter = outer;
313         }
314 
315         @Override
316         public void onAnimationFinished() throws RemoteException {
317             ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "app-onAnimationFinished(): mOuter=%s", mOuter);
318             final long token = Binder.clearCallingIdentity();
319             try {
320                 if (mOuter != null) {
321                     mOuter.onAnimationFinished();
322 
323                     // In case the client holds on to the finish callback, make sure we don't leak
324                     // RemoteAnimationController which in turn would leak the runner on the client.
325                     mOuter = null;
326                 }
327             } finally {
328                 Binder.restoreCallingIdentity(token);
329             }
330         }
331 
332         /**
333          * Marks this callback as not be used anymore by releasing the reference to the outer class
334          * to prevent memory leak.
335          */
336         void release() {
337             ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "app-release(): mOuter=%s", mOuter);
338             mOuter = null;
339         }
340     };
341 
342     /**
343      * Contains information about a remote-animation for one WindowContainer. This keeps track of,
344      * potentially, multiple animating surfaces (AdapterWrappers) associated with one
345      * Window/Transition. For example, a change transition has an adapter controller for the
346      * main window and an adapter controlling the start-state snapshot.
347      * <p>
348      * This can be thought of as a bridge between the information that the remote animator sees (via
349      * {@link RemoteAnimationTarget}) and what the server sees (the
350      * {@link RemoteAnimationAdapterWrapper}(s) interfacing with the moving surfaces).
351      */
352     public class RemoteAnimationRecord {
353         RemoteAnimationAdapterWrapper mAdapter;
354         RemoteAnimationAdapterWrapper mThumbnailAdapter = null;
355         RemoteAnimationTarget mTarget;
356         final WindowContainer mWindowContainer;
357         final Rect mStartBounds;
358 
359         RemoteAnimationRecord(WindowContainer windowContainer, Point endPos, Rect localBounds,
360                 Rect endBounds, Rect startBounds) {
361             mWindowContainer = windowContainer;
362             mAdapter = new RemoteAnimationAdapterWrapper(this, endPos, localBounds, endBounds);
363             if (startBounds != null) {
364                 mStartBounds = new Rect(startBounds);
365                 mTmpRect.set(startBounds);
366                 mTmpRect.offsetTo(0, 0);
367                 if (mRemoteAnimationAdapter.getChangeNeedsSnapshot()) {
368                     mThumbnailAdapter =
369                             new RemoteAnimationAdapterWrapper(this, new Point(0, 0), localBounds,
370                                     mTmpRect);
371                 }
372             } else {
373                 mStartBounds = null;
374             }
375         }
376 
377         RemoteAnimationTarget createRemoteAnimationTarget() {
378             if (mAdapter == null
379                     || mAdapter.mCapturedFinishCallback == null
380                     || mAdapter.mCapturedLeash == null) {
381                 return null;
382             }
383             mTarget = mWindowContainer.createRemoteAnimationTarget(this);
384             return mTarget;
385         }
386 
387         int getMode() {
388             final DisplayContent dc = mWindowContainer.getDisplayContent();
389             final ActivityRecord topActivity = mWindowContainer.getTopMostActivity();
390             // Note that opening/closing transitions are per-activity while changing transitions
391             // are per-task.
392             if (dc.mOpeningApps.contains(topActivity)) {
393                 return RemoteAnimationTarget.MODE_OPENING;
394             } else if (dc.mChangingContainers.contains(mWindowContainer)) {
395                 return RemoteAnimationTarget.MODE_CHANGING;
396             } else {
397                 return RemoteAnimationTarget.MODE_CLOSING;
398             }
399         }
400     }
401 
402     class RemoteAnimationAdapterWrapper implements AnimationAdapter {
403         private final RemoteAnimationRecord mRecord;
404         SurfaceControl mCapturedLeash;
405         private OnAnimationFinishedCallback mCapturedFinishCallback;
406         private @AnimationType int mAnimationType;
407         final Point mPosition = new Point();
408         final Rect mLocalBounds;
409         final Rect mStackBounds = new Rect();
410 
411         RemoteAnimationAdapterWrapper(RemoteAnimationRecord record, Point position,
412                 Rect localBounds, Rect stackBounds) {
413             mRecord = record;
414             mPosition.set(position.x, position.y);
415             mLocalBounds = localBounds;
416             mStackBounds.set(stackBounds);
417         }
418 
419         @Override
420         public boolean getShowWallpaper() {
421             return false;
422         }
423 
424         @Override
425         public void startAnimation(SurfaceControl animationLeash, Transaction t,
426                 @AnimationType int type, OnAnimationFinishedCallback finishCallback) {
427             ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "startAnimation");
428 
429             // Restore position and stack crop until client has a chance to modify it.
430             if (mRecord.mStartBounds != null) {
431                 t.setPosition(animationLeash, mRecord.mStartBounds.left, mRecord.mStartBounds.top);
432                 t.setWindowCrop(animationLeash, mRecord.mStartBounds.width(),
433                         mRecord.mStartBounds.height());
434             } else {
435                 t.setPosition(animationLeash, mPosition.x, mPosition.y);
436                 t.setWindowCrop(animationLeash, mStackBounds.width(), mStackBounds.height());
437             }
438             mCapturedLeash = animationLeash;
439             mCapturedFinishCallback = finishCallback;
440             mAnimationType = type;
441         }
442 
443         @Override
444         public void onAnimationCancelled(SurfaceControl animationLeash) {
445             if (mRecord.mAdapter == this) {
446                 mRecord.mAdapter = null;
447             } else {
448                 mRecord.mThumbnailAdapter = null;
449             }
450             if (mRecord.mAdapter == null && mRecord.mThumbnailAdapter == null) {
451                 mPendingAnimations.remove(mRecord);
452             }
453             if (mPendingAnimations.isEmpty()) {
454                 cancelAnimation("allAppAnimationsCanceled");
455             }
456         }
457 
458         @Override
459         public long getDurationHint() {
460             return mRemoteAnimationAdapter.getDuration();
461         }
462 
463         @Override
464         public long getStatusBarTransitionsStartTime() {
465             return SystemClock.uptimeMillis()
466                     + mRemoteAnimationAdapter.getStatusBarTransitionDelay();
467         }
468 
469         @Override
470         public void dump(PrintWriter pw, String prefix) {
471             pw.print(prefix); pw.print("container="); pw.println(mRecord.mWindowContainer);
472             if (mRecord.mTarget != null) {
473                 pw.print(prefix); pw.println("Target:");
474                 mRecord.mTarget.dump(pw, prefix + "  ");
475             } else {
476                 pw.print(prefix); pw.println("Target: null");
477             }
478         }
479 
480         @Override
481         public void dumpDebug(ProtoOutputStream proto) {
482             final long token = proto.start(REMOTE);
483             if (mRecord.mTarget != null) {
484                 mRecord.mTarget.dumpDebug(proto, TARGET);
485             }
486             proto.end(token);
487         }
488     }
489 }
490