blob: e4bb0436e1e7ffd50767ea218087828791c385e8 [file] [log] [blame]
Jorim Jaggi33a701a2017-12-01 14:58:18 +01001/*
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
Jorim Jaggi91f9f3482018-02-14 10:58:26 -080037import java.lang.ref.WeakReference;
Jorim Jaggi33a701a2017-12-01 14:58:18 +010038import 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;
Jorim Jaggi91f9f3482018-02-14 10:58:26 -080052 private FinishedCallback mFinishedCallback;
Jorim Jaggi33a701a2017-12-01 14:58:18 +010053
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() {
Jorim Jaggi93f9fe32018-01-25 15:06:13 +010086 if (mPendingAnimations.isEmpty()) {
87 onAnimationFinished();
88 return;
89 }
Jorim Jaggia19d7812018-02-01 15:03:59 +010090
91 // Scale the timeout with the animator scale the controlling app is using.
92 mHandler.postDelayed(mTimeoutRunnable,
93 (long) (TIMEOUT_MS * mService.getCurrentAnimatorScale()));
Jorim Jaggi91f9f3482018-02-14 10:58:26 -080094 mFinishedCallback = new FinishedCallback(this);
Chavi Weingarten16d0d072018-02-12 23:50:28 +000095
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 });
Jorim Jaggibc2aabe2018-03-08 17:27:43 +0100106 sendRunningRemoteAnimation(true);
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100107 }
108
109 private RemoteAnimationTarget[] createAnimations() {
Jorim Jaggi17770212018-01-22 20:01:21 +0100110 final ArrayList<RemoteAnimationTarget> targets = new ArrayList<>();
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100111 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
Jorim Jaggi17770212018-01-22 20:01:21 +0100112 final RemoteAnimationTarget target =
113 mPendingAnimations.get(i).createRemoteAppAnimation();
114 if (target != null) {
115 targets.add(target);
116 }
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100117 }
Jorim Jaggi17770212018-01-22 20:01:21 +0100118 return targets.toArray(new RemoteAnimationTarget[targets.size()]);
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100119 }
120
121 private void onAnimationFinished() {
122 mHandler.removeCallbacks(mTimeoutRunnable);
123 synchronized (mService.mWindowMap) {
Jorim Jaggi91f9f3482018-02-14 10:58:26 -0800124 releaseFinishedCallback();
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100125 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 }
Jorim Jaggibc2aabe2018-03-08 17:27:43 +0100135 sendRunningRemoteAnimation(false);
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100136 }
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
Jorim Jaggi91f9f3482018-02-14 10:58:26 -0800146 private void releaseFinishedCallback() {
147 if (mFinishedCallback != null) {
148 mFinishedCallback.release();
149 mFinishedCallback = null;
150 }
151 }
152
Jorim Jaggibc2aabe2018-03-08 17:27:43 +0100153 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
Jorim Jaggi91f9f3482018-02-14 10:58:26 -0800161 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
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100189 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() {
Jorim Jaggi17770212018-01-22 20:01:21 +0100205 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(),
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100214 mCapturedLeash, !mAppWindowToken.fillsParent(),
Winson Chung584d6522018-02-07 23:57:38 +0000215 mainWindow.mWinAnimator.mLastClipRect, mainWindow.mContentInsets,
Winson Chunge2d72172018-01-25 17:46:20 +0000216 mAppWindowToken.getPrefixOrderIndex(), mPosition, mStackBounds,
217 task.getWindowConfiguration());
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100218 }
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
Jorim Jaggi82c17862018-02-21 17:50:18 +0100234 public boolean getShowWallpaper() {
235 return false;
236 }
237
238 @Override
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100239 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);
Jorim Jaggi91f9f3482018-02-14 10:58:26 -0800262 releaseFinishedCallback();
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100263 invokeAnimationCancelled();
Jorim Jaggibc2aabe2018-03-08 17:27:43 +0100264 sendRunningRemoteAnimation(false);
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100265 }
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}