blob: c5900677e41e98feacdad33df22ea961f7938b6d [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
Jorim Jaggif75d1612018-02-27 15:05:21 +010019import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
Jorim Jaggi33a701a2017-12-01 14:58:18 +010020import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
21import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
Yi Jin6c6e9ca2018-03-20 16:53:35 -070022import static com.android.server.wm.AnimationAdapterProto.REMOTE;
23import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET;
Jorim Jaggi33a701a2017-12-01 14:58:18 +010024
25import android.graphics.Point;
26import android.graphics.Rect;
27import android.os.Handler;
28import android.os.RemoteException;
29import android.os.SystemClock;
30import android.util.Slog;
Jorim Jaggif75d1612018-02-27 15:05:21 +010031import android.util.proto.ProtoOutputStream;
Jorim Jaggi33a701a2017-12-01 14:58:18 +010032import android.view.IRemoteAnimationFinishedCallback;
Jorim Jaggi33a701a2017-12-01 14:58:18 +010033import android.view.RemoteAnimationAdapter;
34import android.view.RemoteAnimationTarget;
35import android.view.SurfaceControl;
36import android.view.SurfaceControl.Transaction;
37
Jorim Jaggif75d1612018-02-27 15:05:21 +010038import com.android.internal.util.FastPrintWriter;
Jorim Jaggi33a701a2017-12-01 14:58:18 +010039import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
40
Jorim Jaggif75d1612018-02-27 15:05:21 +010041import java.io.PrintWriter;
42import java.io.StringWriter;
Jorim Jaggi33a701a2017-12-01 14:58:18 +010043import java.util.ArrayList;
44
45/**
46 * Helper class to run app animations in a remote process.
47 */
48class RemoteAnimationController {
49 private static final String TAG = TAG_WITH_CLASS_NAME ? "RemoteAnimationController" : TAG_WM;
50 private static final long TIMEOUT_MS = 2000;
51
52 private final WindowManagerService mService;
53 private final RemoteAnimationAdapter mRemoteAnimationAdapter;
54 private final ArrayList<RemoteAnimationAdapterWrapper> mPendingAnimations = new ArrayList<>();
55 private final Rect mTmpRect = new Rect();
56 private final Handler mHandler;
Jorim Jaggi91f9f3482018-02-14 10:58:26 -080057 private FinishedCallback mFinishedCallback;
Jorim Jaggi33a701a2017-12-01 14:58:18 +010058
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() {
Jorim Jaggi93f9fe32018-01-25 15:06:13 +010091 if (mPendingAnimations.isEmpty()) {
92 onAnimationFinished();
93 return;
94 }
Jorim Jaggia19d7812018-02-01 15:03:59 +010095
96 // Scale the timeout with the animator scale the controlling app is using.
97 mHandler.postDelayed(mTimeoutRunnable,
98 (long) (TIMEOUT_MS * mService.getCurrentAnimatorScale()));
Jorim Jaggi91f9f3482018-02-14 10:58:26 -080099 mFinishedCallback = new FinishedCallback(this);
Chavi Weingarten16d0d072018-02-12 23:50:28 +0000100
101 final RemoteAnimationTarget[] animations = createAnimations();
Jorim Jaggic4d29f22018-03-22 16:30:56 +0100102 if (animations.length == 0) {
103 onAnimationFinished();
104 return;
105 }
Chavi Weingarten16d0d072018-02-12 23:50:28 +0000106 mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
107 try {
108 mRemoteAnimationAdapter.getRunner().onAnimationStart(animations,
109 mFinishedCallback);
110 } catch (RemoteException e) {
111 Slog.e(TAG, "Failed to start remote animation", e);
112 onAnimationFinished();
113 }
114 });
Jorim Jaggibc2aabe2018-03-08 17:27:43 +0100115 sendRunningRemoteAnimation(true);
Jorim Jaggif75d1612018-02-27 15:05:21 +0100116 if (DEBUG_APP_TRANSITIONS) {
117 writeStartDebugStatement();
118 }
119 }
120
121 private void writeStartDebugStatement() {
122 Slog.i(TAG, "Starting remote animation");
123 final StringWriter sw = new StringWriter();
124 final FastPrintWriter pw = new FastPrintWriter(sw);
125 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
126 mPendingAnimations.get(i).dump(pw, "");
127 }
128 pw.close();
129 Slog.i(TAG, sw.toString());
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100130 }
131
132 private RemoteAnimationTarget[] createAnimations() {
Jorim Jaggi17770212018-01-22 20:01:21 +0100133 final ArrayList<RemoteAnimationTarget> targets = new ArrayList<>();
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100134 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
Jorim Jaggi17770212018-01-22 20:01:21 +0100135 final RemoteAnimationTarget target =
136 mPendingAnimations.get(i).createRemoteAppAnimation();
137 if (target != null) {
138 targets.add(target);
Jorim Jaggic4d29f22018-03-22 16:30:56 +0100139 } else {
140 mPendingAnimations.remove(i);
Jorim Jaggi17770212018-01-22 20:01:21 +0100141 }
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100142 }
Jorim Jaggi17770212018-01-22 20:01:21 +0100143 return targets.toArray(new RemoteAnimationTarget[targets.size()]);
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100144 }
145
146 private void onAnimationFinished() {
147 mHandler.removeCallbacks(mTimeoutRunnable);
148 synchronized (mService.mWindowMap) {
Jorim Jaggi91f9f3482018-02-14 10:58:26 -0800149 releaseFinishedCallback();
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100150 mService.openSurfaceTransaction();
151 try {
152 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
153 final RemoteAnimationAdapterWrapper adapter = mPendingAnimations.get(i);
154 adapter.mCapturedFinishCallback.onAnimationFinished(adapter);
155 }
156 } finally {
157 mService.closeSurfaceTransaction("RemoteAnimationController#finished");
158 }
159 }
Jorim Jaggibc2aabe2018-03-08 17:27:43 +0100160 sendRunningRemoteAnimation(false);
Jorim Jaggif75d1612018-02-27 15:05:21 +0100161 if (DEBUG_APP_TRANSITIONS) Slog.i(TAG, "Finishing remote animation");
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100162 }
163
164 private void invokeAnimationCancelled() {
165 try {
166 mRemoteAnimationAdapter.getRunner().onAnimationCancelled();
167 } catch (RemoteException e) {
168 Slog.e(TAG, "Failed to notify cancel", e);
169 }
170 }
171
Jorim Jaggi91f9f3482018-02-14 10:58:26 -0800172 private void releaseFinishedCallback() {
173 if (mFinishedCallback != null) {
174 mFinishedCallback.release();
175 mFinishedCallback = null;
176 }
177 }
178
Jorim Jaggibc2aabe2018-03-08 17:27:43 +0100179 private void sendRunningRemoteAnimation(boolean running) {
180 final int pid = mRemoteAnimationAdapter.getCallingPid();
181 if (pid == 0) {
182 throw new RuntimeException("Calling pid of remote animation was null");
183 }
184 mService.sendSetRunningRemoteAnimation(pid, running);
185 }
186
Jorim Jaggi91f9f3482018-02-14 10:58:26 -0800187 private static final class FinishedCallback extends IRemoteAnimationFinishedCallback.Stub {
188
189 RemoteAnimationController mOuter;
190
191 FinishedCallback(RemoteAnimationController outer) {
192 mOuter = outer;
193 }
194
195 @Override
196 public void onAnimationFinished() throws RemoteException {
197 if (mOuter != null) {
198 mOuter.onAnimationFinished();
199
200 // In case the client holds on to the finish callback, make sure we don't leak
201 // RemoteAnimationController which in turn would leak the runner on the client.
202 mOuter = null;
203 }
204 }
205
206 /**
207 * Marks this callback as not be used anymore by releasing the reference to the outer class
208 * to prevent memory leak.
209 */
210 void release() {
211 mOuter = null;
212 }
213 };
214
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100215 private class RemoteAnimationAdapterWrapper implements AnimationAdapter {
216
217 private final AppWindowToken mAppWindowToken;
218 private SurfaceControl mCapturedLeash;
219 private OnAnimationFinishedCallback mCapturedFinishCallback;
220 private final Point mPosition = new Point();
221 private final Rect mStackBounds = new Rect();
Jorim Jaggif75d1612018-02-27 15:05:21 +0100222 private RemoteAnimationTarget mTarget;
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100223
224 RemoteAnimationAdapterWrapper(AppWindowToken appWindowToken, Point position,
225 Rect stackBounds) {
226 mAppWindowToken = appWindowToken;
227 mPosition.set(position.x, position.y);
228 mStackBounds.set(stackBounds);
229 }
230
231 RemoteAnimationTarget createRemoteAppAnimation() {
Jorim Jaggi17770212018-01-22 20:01:21 +0100232 final Task task = mAppWindowToken.getTask();
233 final WindowState mainWindow = mAppWindowToken.findMainWindow();
Jorim Jaggic4d29f22018-03-22 16:30:56 +0100234 if (task == null || mainWindow == null || mCapturedFinishCallback == null
235 || mCapturedLeash == null) {
Jorim Jaggi17770212018-01-22 20:01:21 +0100236 return null;
237 }
Jorim Jaggif75d1612018-02-27 15:05:21 +0100238 mTarget = new RemoteAnimationTarget(task.mTaskId, getMode(),
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100239 mCapturedLeash, !mAppWindowToken.fillsParent(),
Winson Chung584d6522018-02-07 23:57:38 +0000240 mainWindow.mWinAnimator.mLastClipRect, mainWindow.mContentInsets,
Winson Chunge2d72172018-01-25 17:46:20 +0000241 mAppWindowToken.getPrefixOrderIndex(), mPosition, mStackBounds,
Vadim Tryshev593e9562018-03-08 17:15:45 -0800242 task.getWindowConfiguration(), false /*isNotInRecents*/);
Jorim Jaggif75d1612018-02-27 15:05:21 +0100243 return mTarget;
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100244 }
245
246 private int getMode() {
247 if (mService.mOpeningApps.contains(mAppWindowToken)) {
248 return RemoteAnimationTarget.MODE_OPENING;
249 } else {
250 return RemoteAnimationTarget.MODE_CLOSING;
251 }
252 }
253
254 @Override
255 public boolean getDetachWallpaper() {
256 return false;
257 }
258
259 @Override
Jorim Jaggi82c17862018-02-21 17:50:18 +0100260 public boolean getShowWallpaper() {
261 return false;
262 }
263
264 @Override
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100265 public int getBackgroundColor() {
266 return 0;
267 }
268
269 @Override
270 public void startAnimation(SurfaceControl animationLeash, Transaction t,
271 OnAnimationFinishedCallback finishCallback) {
272
273 // Restore z-layering, position and stack crop until client has a chance to modify it.
274 t.setLayer(animationLeash, mAppWindowToken.getPrefixOrderIndex());
275 t.setPosition(animationLeash, mPosition.x, mPosition.y);
276 mTmpRect.set(mStackBounds);
277 mTmpRect.offsetTo(0, 0);
278 t.setWindowCrop(animationLeash, mTmpRect);
279 mCapturedLeash = animationLeash;
280 mCapturedFinishCallback = finishCallback;
281 }
282
283 @Override
284 public void onAnimationCancelled(SurfaceControl animationLeash) {
285 mPendingAnimations.remove(this);
286 if (mPendingAnimations.isEmpty()) {
287 mHandler.removeCallbacks(mTimeoutRunnable);
Jorim Jaggi91f9f3482018-02-14 10:58:26 -0800288 releaseFinishedCallback();
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100289 invokeAnimationCancelled();
Jorim Jaggibc2aabe2018-03-08 17:27:43 +0100290 sendRunningRemoteAnimation(false);
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100291 }
292 }
293
294 @Override
295 public long getDurationHint() {
296 return mRemoteAnimationAdapter.getDuration();
297 }
298
299 @Override
300 public long getStatusBarTransitionsStartTime() {
301 return SystemClock.uptimeMillis()
302 + mRemoteAnimationAdapter.getStatusBarTransitionDelay();
303 }
Jorim Jaggif75d1612018-02-27 15:05:21 +0100304
305 @Override
306 public void dump(PrintWriter pw, String prefix) {
307 pw.print(prefix); pw.print("token="); pw.println(mAppWindowToken);
308 if (mTarget != null) {
309 pw.print(prefix); pw.println("Target:");
310 mTarget.dump(pw, prefix + " ");
311 } else {
312 pw.print(prefix); pw.println("Target: null");
313 }
314 }
315
316 @Override
317 public void writeToProto(ProtoOutputStream proto) {
318 final long token = proto.start(REMOTE);
319 if (mTarget != null) {
320 mTarget.writeToProto(proto, TARGET);
321 }
322 proto.end(token);
323 }
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100324 }
325}