RemoteAnimationController.java revision d3d139ab1eb841b1713016598fdca7a31b302ed8
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
17package com.android.server.wm;
18
19import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
20import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
21
22import android.graphics.Point;
23import android.graphics.Rect;
24import android.os.Handler;
25import android.os.RemoteException;
26import android.os.SystemClock;
27import android.util.Slog;
28import android.view.IRemoteAnimationFinishedCallback;
29import android.view.IRemoteAnimationFinishedCallback.Stub;
30import android.view.RemoteAnimationAdapter;
31import android.view.RemoteAnimationTarget;
32import android.view.SurfaceControl;
33import android.view.SurfaceControl.Transaction;
34
35import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
36
37import java.lang.ref.WeakReference;
38import java.util.ArrayList;
39
40/**
41 * Helper class to run app animations in a remote process.
42 */
43class RemoteAnimationController {
44    private static final String TAG = TAG_WITH_CLASS_NAME ? "RemoteAnimationController" : TAG_WM;
45    private static final long TIMEOUT_MS = 2000;
46
47    private final WindowManagerService mService;
48    private final RemoteAnimationAdapter mRemoteAnimationAdapter;
49    private final ArrayList<RemoteAnimationAdapterWrapper> mPendingAnimations = new ArrayList<>();
50    private final Rect mTmpRect = new Rect();
51    private final Handler mHandler;
52    private FinishedCallback mFinishedCallback;
53
54    private final Runnable mTimeoutRunnable = () -> {
55        onAnimationFinished();
56        invokeAnimationCancelled();
57    };
58
59    RemoteAnimationController(WindowManagerService service,
60            RemoteAnimationAdapter remoteAnimationAdapter, Handler handler) {
61        mService = service;
62        mRemoteAnimationAdapter = remoteAnimationAdapter;
63        mHandler = handler;
64    }
65
66    /**
67     * Creates an animation for each individual {@link AppWindowToken}.
68     *
69     * @param appWindowToken The app to animate.
70     * @param position The position app bounds, in screen coordinates.
71     * @param stackBounds The stack bounds of the app.
72     * @return The adapter to be run on the app.
73     */
74    AnimationAdapter createAnimationAdapter(AppWindowToken appWindowToken, Point position,
75            Rect stackBounds) {
76        final RemoteAnimationAdapterWrapper adapter = new RemoteAnimationAdapterWrapper(
77                appWindowToken, position, stackBounds);
78        mPendingAnimations.add(adapter);
79        return adapter;
80    }
81
82    /**
83     * Called when the transition is ready to be started, and all leashes have been set up.
84     */
85    void goodToGo() {
86        if (mPendingAnimations.isEmpty()) {
87            onAnimationFinished();
88            return;
89        }
90
91        // Scale the timeout with the animator scale the controlling app is using.
92        mHandler.postDelayed(mTimeoutRunnable,
93                (long) (TIMEOUT_MS * mService.getCurrentAnimatorScale()));
94        mFinishedCallback = new FinishedCallback(this);
95
96        final RemoteAnimationTarget[] animations = createAnimations();
97        mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
98            try {
99                mRemoteAnimationAdapter.getRunner().onAnimationStart(animations,
100                        mFinishedCallback);
101            } catch (RemoteException e) {
102                Slog.e(TAG, "Failed to start remote animation", e);
103                onAnimationFinished();
104            }
105        });
106        sendRunningRemoteAnimation(true);
107    }
108
109    private RemoteAnimationTarget[] createAnimations() {
110        final ArrayList<RemoteAnimationTarget> targets = new ArrayList<>();
111        for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
112            final RemoteAnimationTarget target =
113                    mPendingAnimations.get(i).createRemoteAppAnimation();
114            if (target != null) {
115                targets.add(target);
116            }
117        }
118        return targets.toArray(new RemoteAnimationTarget[targets.size()]);
119    }
120
121    private void onAnimationFinished() {
122        mHandler.removeCallbacks(mTimeoutRunnable);
123        synchronized (mService.mWindowMap) {
124            releaseFinishedCallback();
125            mService.openSurfaceTransaction();
126            try {
127                for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
128                    final RemoteAnimationAdapterWrapper adapter = mPendingAnimations.get(i);
129                    adapter.mCapturedFinishCallback.onAnimationFinished(adapter);
130                }
131            } finally {
132                mService.closeSurfaceTransaction("RemoteAnimationController#finished");
133            }
134        }
135        sendRunningRemoteAnimation(false);
136    }
137
138    private void invokeAnimationCancelled() {
139        try {
140            mRemoteAnimationAdapter.getRunner().onAnimationCancelled();
141        } catch (RemoteException e) {
142            Slog.e(TAG, "Failed to notify cancel", e);
143        }
144    }
145
146    private void releaseFinishedCallback() {
147        if (mFinishedCallback != null) {
148            mFinishedCallback.release();
149            mFinishedCallback = null;
150        }
151    }
152
153    private void sendRunningRemoteAnimation(boolean running) {
154        final int pid = mRemoteAnimationAdapter.getCallingPid();
155        if (pid == 0) {
156            throw new RuntimeException("Calling pid of remote animation was null");
157        }
158        mService.sendSetRunningRemoteAnimation(pid, running);
159    }
160
161    private static final class FinishedCallback extends IRemoteAnimationFinishedCallback.Stub {
162
163        RemoteAnimationController mOuter;
164
165        FinishedCallback(RemoteAnimationController outer) {
166            mOuter = outer;
167        }
168
169        @Override
170        public void onAnimationFinished() throws RemoteException {
171            if (mOuter != null) {
172                mOuter.onAnimationFinished();
173
174                // In case the client holds on to the finish callback, make sure we don't leak
175                // RemoteAnimationController which in turn would leak the runner on the client.
176                mOuter = null;
177            }
178        }
179
180        /**
181         * Marks this callback as not be used anymore by releasing the reference to the outer class
182         * to prevent memory leak.
183         */
184        void release() {
185            mOuter = null;
186        }
187    };
188
189    private class RemoteAnimationAdapterWrapper implements AnimationAdapter {
190
191        private final AppWindowToken mAppWindowToken;
192        private SurfaceControl mCapturedLeash;
193        private OnAnimationFinishedCallback mCapturedFinishCallback;
194        private final Point mPosition = new Point();
195        private final Rect mStackBounds = new Rect();
196
197        RemoteAnimationAdapterWrapper(AppWindowToken appWindowToken, Point position,
198                Rect stackBounds) {
199            mAppWindowToken = appWindowToken;
200            mPosition.set(position.x, position.y);
201            mStackBounds.set(stackBounds);
202        }
203
204        RemoteAnimationTarget createRemoteAppAnimation() {
205            final Task task = mAppWindowToken.getTask();
206            final WindowState mainWindow = mAppWindowToken.findMainWindow();
207            if (task == null) {
208                return null;
209            }
210            if (mainWindow == null) {
211                return null;
212            }
213            return new RemoteAnimationTarget(task.mTaskId, getMode(),
214                    mCapturedLeash, !mAppWindowToken.fillsParent(),
215                    mainWindow.mWinAnimator.mLastClipRect, mainWindow.mContentInsets,
216                    mAppWindowToken.getPrefixOrderIndex(), mPosition, mStackBounds,
217                    task.getWindowConfiguration());
218        }
219
220        private int getMode() {
221            if (mService.mOpeningApps.contains(mAppWindowToken)) {
222                return RemoteAnimationTarget.MODE_OPENING;
223            } else {
224                return RemoteAnimationTarget.MODE_CLOSING;
225            }
226        }
227
228        @Override
229        public boolean getDetachWallpaper() {
230            return false;
231        }
232
233        @Override
234        public boolean getShowWallpaper() {
235            return false;
236        }
237
238        @Override
239        public int getBackgroundColor() {
240            return 0;
241        }
242
243        @Override
244        public void startAnimation(SurfaceControl animationLeash, Transaction t,
245                OnAnimationFinishedCallback finishCallback) {
246
247            // Restore z-layering, position and stack crop until client has a chance to modify it.
248            t.setLayer(animationLeash, mAppWindowToken.getPrefixOrderIndex());
249            t.setPosition(animationLeash, mPosition.x, mPosition.y);
250            mTmpRect.set(mStackBounds);
251            mTmpRect.offsetTo(0, 0);
252            t.setWindowCrop(animationLeash, mTmpRect);
253            mCapturedLeash = animationLeash;
254            mCapturedFinishCallback = finishCallback;
255        }
256
257        @Override
258        public void onAnimationCancelled(SurfaceControl animationLeash) {
259            mPendingAnimations.remove(this);
260            if (mPendingAnimations.isEmpty()) {
261                mHandler.removeCallbacks(mTimeoutRunnable);
262                releaseFinishedCallback();
263                invokeAnimationCancelled();
264                sendRunningRemoteAnimation(false);
265            }
266        }
267
268        @Override
269        public long getDurationHint() {
270            return mRemoteAnimationAdapter.getDuration();
271        }
272
273        @Override
274        public long getStatusBarTransitionsStartTime() {
275            return SystemClock.uptimeMillis()
276                    + mRemoteAnimationAdapter.getStatusBarTransitionDelay();
277        }
278    }
279}
280