RemoteAnimationController.java revision 16d0d07df83eb00edd719715e6e42d91b600602f
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.util.ArrayList;
38
39/**
40 * Helper class to run app animations in a remote process.
41 */
42class RemoteAnimationController {
43    private static final String TAG = TAG_WITH_CLASS_NAME ? "RemoteAnimationController" : TAG_WM;
44    private static final long TIMEOUT_MS = 2000;
45
46    private final WindowManagerService mService;
47    private final RemoteAnimationAdapter mRemoteAnimationAdapter;
48    private final ArrayList<RemoteAnimationAdapterWrapper> mPendingAnimations = new ArrayList<>();
49    private final Rect mTmpRect = new Rect();
50    private final Handler mHandler;
51
52    private final IRemoteAnimationFinishedCallback mFinishedCallback = new Stub() {
53        @Override
54        public void onAnimationFinished() throws RemoteException {
55            RemoteAnimationController.this.onAnimationFinished();
56        }
57    };
58
59    private final Runnable mTimeoutRunnable = () -> {
60        onAnimationFinished();
61        invokeAnimationCancelled();
62    };
63
64    RemoteAnimationController(WindowManagerService service,
65            RemoteAnimationAdapter remoteAnimationAdapter, Handler handler) {
66        mService = service;
67        mRemoteAnimationAdapter = remoteAnimationAdapter;
68        mHandler = handler;
69    }
70
71    /**
72     * Creates an animation for each individual {@link AppWindowToken}.
73     *
74     * @param appWindowToken The app to animate.
75     * @param position The position app bounds, in screen coordinates.
76     * @param stackBounds The stack bounds of the app.
77     * @return The adapter to be run on the app.
78     */
79    AnimationAdapter createAnimationAdapter(AppWindowToken appWindowToken, Point position,
80            Rect stackBounds) {
81        final RemoteAnimationAdapterWrapper adapter = new RemoteAnimationAdapterWrapper(
82                appWindowToken, position, stackBounds);
83        mPendingAnimations.add(adapter);
84        return adapter;
85    }
86
87    /**
88     * Called when the transition is ready to be started, and all leashes have been set up.
89     */
90    void goodToGo() {
91        if (mPendingAnimations.isEmpty()) {
92            onAnimationFinished();
93            return;
94        }
95
96        // Scale the timeout with the animator scale the controlling app is using.
97        mHandler.postDelayed(mTimeoutRunnable,
98                (long) (TIMEOUT_MS * mService.getCurrentAnimatorScale()));
99
100        final RemoteAnimationTarget[] animations = createAnimations();
101        mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
102            try {
103                mRemoteAnimationAdapter.getRunner().onAnimationStart(animations,
104                        mFinishedCallback);
105            } catch (RemoteException e) {
106                Slog.e(TAG, "Failed to start remote animation", e);
107                onAnimationFinished();
108            }
109        });
110    }
111
112    private RemoteAnimationTarget[] createAnimations() {
113        final ArrayList<RemoteAnimationTarget> targets = new ArrayList<>();
114        for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
115            final RemoteAnimationTarget target =
116                    mPendingAnimations.get(i).createRemoteAppAnimation();
117            if (target != null) {
118                targets.add(target);
119            }
120        }
121        return targets.toArray(new RemoteAnimationTarget[targets.size()]);
122    }
123
124    private void onAnimationFinished() {
125        mHandler.removeCallbacks(mTimeoutRunnable);
126        synchronized (mService.mWindowMap) {
127            mService.openSurfaceTransaction();
128            try {
129                for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
130                    final RemoteAnimationAdapterWrapper adapter = mPendingAnimations.get(i);
131                    adapter.mCapturedFinishCallback.onAnimationFinished(adapter);
132                }
133            } finally {
134                mService.closeSurfaceTransaction("RemoteAnimationController#finished");
135            }
136        }
137    }
138
139    private void invokeAnimationCancelled() {
140        try {
141            mRemoteAnimationAdapter.getRunner().onAnimationCancelled();
142        } catch (RemoteException e) {
143            Slog.e(TAG, "Failed to notify cancel", e);
144        }
145    }
146
147    private class RemoteAnimationAdapterWrapper implements AnimationAdapter {
148
149        private final AppWindowToken mAppWindowToken;
150        private SurfaceControl mCapturedLeash;
151        private OnAnimationFinishedCallback mCapturedFinishCallback;
152        private final Point mPosition = new Point();
153        private final Rect mStackBounds = new Rect();
154
155        RemoteAnimationAdapterWrapper(AppWindowToken appWindowToken, Point position,
156                Rect stackBounds) {
157            mAppWindowToken = appWindowToken;
158            mPosition.set(position.x, position.y);
159            mStackBounds.set(stackBounds);
160        }
161
162        RemoteAnimationTarget createRemoteAppAnimation() {
163            final Task task = mAppWindowToken.getTask();
164            final WindowState mainWindow = mAppWindowToken.findMainWindow();
165            if (task == null) {
166                return null;
167            }
168            if (mainWindow == null) {
169                return null;
170            }
171            return new RemoteAnimationTarget(task.mTaskId, getMode(),
172                    mCapturedLeash, !mAppWindowToken.fillsParent(),
173                    mainWindow.mWinAnimator.mLastClipRect, mainWindow.mContentInsets,
174                    mAppWindowToken.getPrefixOrderIndex(), mPosition, mStackBounds,
175                    task.getWindowConfiguration());
176        }
177
178        private int getMode() {
179            if (mService.mOpeningApps.contains(mAppWindowToken)) {
180                return RemoteAnimationTarget.MODE_OPENING;
181            } else {
182                return RemoteAnimationTarget.MODE_CLOSING;
183            }
184        }
185
186        @Override
187        public boolean getDetachWallpaper() {
188            return false;
189        }
190
191        @Override
192        public int getBackgroundColor() {
193            return 0;
194        }
195
196        @Override
197        public void startAnimation(SurfaceControl animationLeash, Transaction t,
198                OnAnimationFinishedCallback finishCallback) {
199
200            // Restore z-layering, position and stack crop until client has a chance to modify it.
201            t.setLayer(animationLeash, mAppWindowToken.getPrefixOrderIndex());
202            t.setPosition(animationLeash, mPosition.x, mPosition.y);
203            mTmpRect.set(mStackBounds);
204            mTmpRect.offsetTo(0, 0);
205            t.setWindowCrop(animationLeash, mTmpRect);
206            mCapturedLeash = animationLeash;
207            mCapturedFinishCallback = finishCallback;
208        }
209
210        @Override
211        public void onAnimationCancelled(SurfaceControl animationLeash) {
212            mPendingAnimations.remove(this);
213            if (mPendingAnimations.isEmpty()) {
214                mHandler.removeCallbacks(mTimeoutRunnable);
215                invokeAnimationCancelled();
216            }
217        }
218
219        @Override
220        public long getDurationHint() {
221            return mRemoteAnimationAdapter.getDuration();
222        }
223
224        @Override
225        public long getStatusBarTransitionsStartTime() {
226            return SystemClock.uptimeMillis()
227                    + mRemoteAnimationAdapter.getStatusBarTransitionDelay();
228        }
229    }
230}
231