blob: 16f4cd0d50ed8ea61ce4b71e23d717298a72d8ea [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 Jaggiab20e162018-03-27 13:06:09 +020019import static com.android.server.wm.AnimationAdapterProto.REMOTE;
20import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET;
Jorim Jaggif75d1612018-02-27 15:05:21 +010021import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
Jorim Jaggi33a701a2017-12-01 14:58:18 +010022import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
23import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
24
25import android.graphics.Point;
26import android.graphics.Rect;
Jorim Jaggiab20e162018-03-27 13:06:09 +020027import android.os.Binder;
Jorim Jaggi33a701a2017-12-01 14:58:18 +010028import android.os.Handler;
Adrian Roos842e7882018-03-26 17:34:06 +020029import android.os.IBinder.DeathRecipient;
Jorim Jaggi33a701a2017-12-01 14:58:18 +010030import android.os.RemoteException;
31import android.os.SystemClock;
32import android.util.Slog;
Jorim Jaggif75d1612018-02-27 15:05:21 +010033import android.util.proto.ProtoOutputStream;
Jorim Jaggi33a701a2017-12-01 14:58:18 +010034import android.view.IRemoteAnimationFinishedCallback;
Jorim Jaggi33a701a2017-12-01 14:58:18 +010035import android.view.RemoteAnimationAdapter;
36import android.view.RemoteAnimationTarget;
37import android.view.SurfaceControl;
38import android.view.SurfaceControl.Transaction;
39
Jorim Jaggif75d1612018-02-27 15:05:21 +010040import com.android.internal.util.FastPrintWriter;
Jorim Jaggi33a701a2017-12-01 14:58:18 +010041import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
Jorim Jaggi817ebdd2018-03-26 15:46:01 +020042import com.android.server.wm.utils.InsetUtils;
Jorim Jaggi33a701a2017-12-01 14:58:18 +010043
Jorim Jaggif75d1612018-02-27 15:05:21 +010044import java.io.PrintWriter;
45import java.io.StringWriter;
Jorim Jaggi33a701a2017-12-01 14:58:18 +010046import java.util.ArrayList;
47
48/**
49 * Helper class to run app animations in a remote process.
50 */
Adrian Roos842e7882018-03-26 17:34:06 +020051class RemoteAnimationController implements DeathRecipient {
Jorim Jaggi33a701a2017-12-01 14:58:18 +010052 private static final String TAG = TAG_WITH_CLASS_NAME ? "RemoteAnimationController" : TAG_WM;
53 private static final long TIMEOUT_MS = 2000;
54
55 private final WindowManagerService mService;
56 private final RemoteAnimationAdapter mRemoteAnimationAdapter;
57 private final ArrayList<RemoteAnimationAdapterWrapper> mPendingAnimations = new ArrayList<>();
58 private final Rect mTmpRect = new Rect();
59 private final Handler mHandler;
Adrian Roos842e7882018-03-26 17:34:06 +020060 private final Runnable mTimeoutRunnable = this::cancelAnimation;
Jorim Jaggi33a701a2017-12-01 14:58:18 +010061
Adrian Roos842e7882018-03-26 17:34:06 +020062 private FinishedCallback mFinishedCallback;
63 private boolean mCanceled;
Adrian Roos653c6c12018-04-09 14:12:46 -070064 private boolean mLinkedToDeathOfRunner;
Jorim Jaggi33a701a2017-12-01 14:58:18 +010065
66 RemoteAnimationController(WindowManagerService service,
67 RemoteAnimationAdapter remoteAnimationAdapter, Handler handler) {
68 mService = service;
69 mRemoteAnimationAdapter = remoteAnimationAdapter;
70 mHandler = handler;
71 }
72
73 /**
74 * Creates an animation for each individual {@link AppWindowToken}.
75 *
76 * @param appWindowToken The app to animate.
77 * @param position The position app bounds, in screen coordinates.
78 * @param stackBounds The stack bounds of the app.
79 * @return The adapter to be run on the app.
80 */
81 AnimationAdapter createAnimationAdapter(AppWindowToken appWindowToken, Point position,
82 Rect stackBounds) {
83 final RemoteAnimationAdapterWrapper adapter = new RemoteAnimationAdapterWrapper(
84 appWindowToken, position, stackBounds);
85 mPendingAnimations.add(adapter);
86 return adapter;
87 }
88
89 /**
90 * Called when the transition is ready to be started, and all leashes have been set up.
91 */
92 void goodToGo() {
Adrian Roos842e7882018-03-26 17:34:06 +020093 if (mPendingAnimations.isEmpty() || mCanceled) {
Jorim Jaggi93f9fe32018-01-25 15:06:13 +010094 onAnimationFinished();
95 return;
96 }
Jorim Jaggia19d7812018-02-01 15:03:59 +010097
98 // Scale the timeout with the animator scale the controlling app is using.
99 mHandler.postDelayed(mTimeoutRunnable,
100 (long) (TIMEOUT_MS * mService.getCurrentAnimatorScale()));
Jorim Jaggi91f9f3482018-02-14 10:58:26 -0800101 mFinishedCallback = new FinishedCallback(this);
Chavi Weingarten16d0d072018-02-12 23:50:28 +0000102
103 final RemoteAnimationTarget[] animations = createAnimations();
Jorim Jaggic4d29f22018-03-22 16:30:56 +0100104 if (animations.length == 0) {
105 onAnimationFinished();
106 return;
107 }
Chavi Weingarten16d0d072018-02-12 23:50:28 +0000108 mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
109 try {
Adrian Roos653c6c12018-04-09 14:12:46 -0700110 linkToDeathOfRunner();
Adrian Roos842e7882018-03-26 17:34:06 +0200111 mRemoteAnimationAdapter.getRunner().onAnimationStart(animations, mFinishedCallback);
Chavi Weingarten16d0d072018-02-12 23:50:28 +0000112 } catch (RemoteException e) {
113 Slog.e(TAG, "Failed to start remote animation", e);
114 onAnimationFinished();
115 }
116 });
Jorim Jaggibc2aabe2018-03-08 17:27:43 +0100117 sendRunningRemoteAnimation(true);
Jorim Jaggif75d1612018-02-27 15:05:21 +0100118 if (DEBUG_APP_TRANSITIONS) {
119 writeStartDebugStatement();
120 }
121 }
122
Adrian Roos842e7882018-03-26 17:34:06 +0200123 private void cancelAnimation() {
124 synchronized (mService.getWindowManagerLock()) {
125 if (mCanceled) {
126 return;
127 }
128 mCanceled = true;
129 }
130 onAnimationFinished();
131 invokeAnimationCancelled();
132 }
133
Jorim Jaggif75d1612018-02-27 15:05:21 +0100134 private void writeStartDebugStatement() {
135 Slog.i(TAG, "Starting remote animation");
136 final StringWriter sw = new StringWriter();
137 final FastPrintWriter pw = new FastPrintWriter(sw);
138 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
139 mPendingAnimations.get(i).dump(pw, "");
140 }
141 pw.close();
142 Slog.i(TAG, sw.toString());
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100143 }
144
145 private RemoteAnimationTarget[] createAnimations() {
Jorim Jaggi17770212018-01-22 20:01:21 +0100146 final ArrayList<RemoteAnimationTarget> targets = new ArrayList<>();
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100147 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
Jorim Jaggib8a9cbe2018-03-27 18:02:18 +0200148 final RemoteAnimationAdapterWrapper wrapper = mPendingAnimations.get(i);
Jorim Jaggi17770212018-01-22 20:01:21 +0100149 final RemoteAnimationTarget target =
150 mPendingAnimations.get(i).createRemoteAppAnimation();
151 if (target != null) {
152 targets.add(target);
Jorim Jaggic4d29f22018-03-22 16:30:56 +0100153 } else {
Jorim Jaggib8a9cbe2018-03-27 18:02:18 +0200154
155 // We can't really start an animation but we still need to make sure to finish the
156 // pending animation that was started by SurfaceAnimator
157 if (wrapper.mCapturedFinishCallback != null) {
158 wrapper.mCapturedFinishCallback.onAnimationFinished(wrapper);
159 }
Jorim Jaggic4d29f22018-03-22 16:30:56 +0100160 mPendingAnimations.remove(i);
Jorim Jaggi17770212018-01-22 20:01:21 +0100161 }
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100162 }
Jorim Jaggi17770212018-01-22 20:01:21 +0100163 return targets.toArray(new RemoteAnimationTarget[targets.size()]);
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100164 }
165
166 private void onAnimationFinished() {
167 mHandler.removeCallbacks(mTimeoutRunnable);
168 synchronized (mService.mWindowMap) {
Adrian Roos653c6c12018-04-09 14:12:46 -0700169 unlinkToDeathOfRunner();
Jorim Jaggi91f9f3482018-02-14 10:58:26 -0800170 releaseFinishedCallback();
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100171 mService.openSurfaceTransaction();
172 try {
173 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
174 final RemoteAnimationAdapterWrapper adapter = mPendingAnimations.get(i);
175 adapter.mCapturedFinishCallback.onAnimationFinished(adapter);
176 }
177 } finally {
178 mService.closeSurfaceTransaction("RemoteAnimationController#finished");
179 }
180 }
Jorim Jaggibc2aabe2018-03-08 17:27:43 +0100181 sendRunningRemoteAnimation(false);
Jorim Jaggif75d1612018-02-27 15:05:21 +0100182 if (DEBUG_APP_TRANSITIONS) Slog.i(TAG, "Finishing remote animation");
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100183 }
184
185 private void invokeAnimationCancelled() {
186 try {
187 mRemoteAnimationAdapter.getRunner().onAnimationCancelled();
188 } catch (RemoteException e) {
189 Slog.e(TAG, "Failed to notify cancel", e);
190 }
191 }
192
Jorim Jaggi91f9f3482018-02-14 10:58:26 -0800193 private void releaseFinishedCallback() {
194 if (mFinishedCallback != null) {
195 mFinishedCallback.release();
196 mFinishedCallback = null;
197 }
198 }
199
Jorim Jaggibc2aabe2018-03-08 17:27:43 +0100200 private void sendRunningRemoteAnimation(boolean running) {
201 final int pid = mRemoteAnimationAdapter.getCallingPid();
202 if (pid == 0) {
203 throw new RuntimeException("Calling pid of remote animation was null");
204 }
205 mService.sendSetRunningRemoteAnimation(pid, running);
206 }
207
Adrian Roos653c6c12018-04-09 14:12:46 -0700208 private void linkToDeathOfRunner() throws RemoteException {
209 if (!mLinkedToDeathOfRunner) {
210 mRemoteAnimationAdapter.getRunner().asBinder().linkToDeath(this, 0);
211 mLinkedToDeathOfRunner = true;
212 }
213 }
214
215 private void unlinkToDeathOfRunner() {
216 if (mLinkedToDeathOfRunner) {
217 mRemoteAnimationAdapter.getRunner().asBinder().unlinkToDeath(this, 0);
218 mLinkedToDeathOfRunner = false;
219 }
220 }
221
Adrian Roos842e7882018-03-26 17:34:06 +0200222 @Override
223 public void binderDied() {
224 cancelAnimation();
225 }
226
Jorim Jaggi91f9f3482018-02-14 10:58:26 -0800227 private static final class FinishedCallback extends IRemoteAnimationFinishedCallback.Stub {
228
229 RemoteAnimationController mOuter;
230
231 FinishedCallback(RemoteAnimationController outer) {
232 mOuter = outer;
233 }
234
235 @Override
236 public void onAnimationFinished() throws RemoteException {
Jorim Jaggiab20e162018-03-27 13:06:09 +0200237 final long token = Binder.clearCallingIdentity();
238 try {
239 if (mOuter != null) {
240 mOuter.onAnimationFinished();
Jorim Jaggi91f9f3482018-02-14 10:58:26 -0800241
Jorim Jaggiab20e162018-03-27 13:06:09 +0200242 // In case the client holds on to the finish callback, make sure we don't leak
243 // RemoteAnimationController which in turn would leak the runner on the client.
244 mOuter = null;
245 }
246 } finally {
247 Binder.restoreCallingIdentity(token);
Jorim Jaggi91f9f3482018-02-14 10:58:26 -0800248 }
249 }
250
251 /**
252 * Marks this callback as not be used anymore by releasing the reference to the outer class
253 * to prevent memory leak.
254 */
255 void release() {
256 mOuter = null;
257 }
258 };
259
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100260 private class RemoteAnimationAdapterWrapper implements AnimationAdapter {
261
262 private final AppWindowToken mAppWindowToken;
263 private SurfaceControl mCapturedLeash;
264 private OnAnimationFinishedCallback mCapturedFinishCallback;
265 private final Point mPosition = new Point();
266 private final Rect mStackBounds = new Rect();
Jorim Jaggif75d1612018-02-27 15:05:21 +0100267 private RemoteAnimationTarget mTarget;
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100268
269 RemoteAnimationAdapterWrapper(AppWindowToken appWindowToken, Point position,
270 Rect stackBounds) {
271 mAppWindowToken = appWindowToken;
272 mPosition.set(position.x, position.y);
273 mStackBounds.set(stackBounds);
274 }
275
276 RemoteAnimationTarget createRemoteAppAnimation() {
Jorim Jaggi17770212018-01-22 20:01:21 +0100277 final Task task = mAppWindowToken.getTask();
278 final WindowState mainWindow = mAppWindowToken.findMainWindow();
Jorim Jaggic4d29f22018-03-22 16:30:56 +0100279 if (task == null || mainWindow == null || mCapturedFinishCallback == null
280 || mCapturedLeash == null) {
Jorim Jaggi17770212018-01-22 20:01:21 +0100281 return null;
282 }
Jorim Jaggi817ebdd2018-03-26 15:46:01 +0200283 final Rect insets = new Rect(mainWindow.mContentInsets);
284 InsetUtils.addInsets(insets, mAppWindowToken.getLetterboxInsets());
Jorim Jaggif75d1612018-02-27 15:05:21 +0100285 mTarget = new RemoteAnimationTarget(task.mTaskId, getMode(),
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100286 mCapturedLeash, !mAppWindowToken.fillsParent(),
Jorim Jaggi817ebdd2018-03-26 15:46:01 +0200287 mainWindow.mWinAnimator.mLastClipRect, insets,
Winson Chunge2d72172018-01-25 17:46:20 +0000288 mAppWindowToken.getPrefixOrderIndex(), mPosition, mStackBounds,
Vadim Tryshev593e9562018-03-08 17:15:45 -0800289 task.getWindowConfiguration(), false /*isNotInRecents*/);
Jorim Jaggif75d1612018-02-27 15:05:21 +0100290 return mTarget;
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100291 }
292
293 private int getMode() {
294 if (mService.mOpeningApps.contains(mAppWindowToken)) {
295 return RemoteAnimationTarget.MODE_OPENING;
296 } else {
297 return RemoteAnimationTarget.MODE_CLOSING;
298 }
299 }
300
301 @Override
302 public boolean getDetachWallpaper() {
303 return false;
304 }
305
306 @Override
Jorim Jaggi82c17862018-02-21 17:50:18 +0100307 public boolean getShowWallpaper() {
308 return false;
309 }
310
311 @Override
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100312 public int getBackgroundColor() {
313 return 0;
314 }
315
316 @Override
317 public void startAnimation(SurfaceControl animationLeash, Transaction t,
318 OnAnimationFinishedCallback finishCallback) {
319
320 // Restore z-layering, position and stack crop until client has a chance to modify it.
321 t.setLayer(animationLeash, mAppWindowToken.getPrefixOrderIndex());
322 t.setPosition(animationLeash, mPosition.x, mPosition.y);
323 mTmpRect.set(mStackBounds);
324 mTmpRect.offsetTo(0, 0);
325 t.setWindowCrop(animationLeash, mTmpRect);
326 mCapturedLeash = animationLeash;
327 mCapturedFinishCallback = finishCallback;
328 }
329
330 @Override
331 public void onAnimationCancelled(SurfaceControl animationLeash) {
332 mPendingAnimations.remove(this);
333 if (mPendingAnimations.isEmpty()) {
334 mHandler.removeCallbacks(mTimeoutRunnable);
Jorim Jaggi91f9f3482018-02-14 10:58:26 -0800335 releaseFinishedCallback();
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100336 invokeAnimationCancelled();
Jorim Jaggibc2aabe2018-03-08 17:27:43 +0100337 sendRunningRemoteAnimation(false);
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100338 }
339 }
340
341 @Override
342 public long getDurationHint() {
343 return mRemoteAnimationAdapter.getDuration();
344 }
345
346 @Override
347 public long getStatusBarTransitionsStartTime() {
348 return SystemClock.uptimeMillis()
349 + mRemoteAnimationAdapter.getStatusBarTransitionDelay();
350 }
Jorim Jaggif75d1612018-02-27 15:05:21 +0100351
352 @Override
353 public void dump(PrintWriter pw, String prefix) {
354 pw.print(prefix); pw.print("token="); pw.println(mAppWindowToken);
355 if (mTarget != null) {
356 pw.print(prefix); pw.println("Target:");
357 mTarget.dump(pw, prefix + " ");
358 } else {
359 pw.print(prefix); pw.println("Target: null");
360 }
361 }
362
363 @Override
364 public void writeToProto(ProtoOutputStream proto) {
365 final long token = proto.start(REMOTE);
366 if (mTarget != null) {
367 mTarget.writeToProto(proto, TARGET);
368 }
369 proto.end(token);
370 }
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100371 }
372}