blob: 7c3aa84159c0cc699917aa1b5e4de5393f688c44 [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;
Winson Chungc6c3f852018-04-09 15:41:03 -070022import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_REMOTE_ANIMATIONS;
Jorim Jaggi33a701a2017-12-01 14:58:18 +010023import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
24import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
25
26import android.graphics.Point;
27import android.graphics.Rect;
Jorim Jaggiab20e162018-03-27 13:06:09 +020028import android.os.Binder;
Jorim Jaggi33a701a2017-12-01 14:58:18 +010029import android.os.Handler;
Adrian Roos842e7882018-03-26 17:34:06 +020030import android.os.IBinder.DeathRecipient;
Jorim Jaggi33a701a2017-12-01 14:58:18 +010031import android.os.RemoteException;
32import android.os.SystemClock;
33import android.util.Slog;
Jorim Jaggif75d1612018-02-27 15:05:21 +010034import android.util.proto.ProtoOutputStream;
Jorim Jaggi33a701a2017-12-01 14:58:18 +010035import android.view.IRemoteAnimationFinishedCallback;
Jorim Jaggi33a701a2017-12-01 14:58:18 +010036import android.view.RemoteAnimationAdapter;
37import android.view.RemoteAnimationTarget;
38import android.view.SurfaceControl;
39import android.view.SurfaceControl.Transaction;
40
Jorim Jaggif75d1612018-02-27 15:05:21 +010041import com.android.internal.util.FastPrintWriter;
Jorim Jaggi33a701a2017-12-01 14:58:18 +010042import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback;
Jorim Jaggi817ebdd2018-03-26 15:46:01 +020043import com.android.server.wm.utils.InsetUtils;
Jorim Jaggi33a701a2017-12-01 14:58:18 +010044
Jorim Jaggif75d1612018-02-27 15:05:21 +010045import java.io.PrintWriter;
46import java.io.StringWriter;
Jorim Jaggi33a701a2017-12-01 14:58:18 +010047import java.util.ArrayList;
48
49/**
50 * Helper class to run app animations in a remote process.
51 */
Adrian Roos842e7882018-03-26 17:34:06 +020052class RemoteAnimationController implements DeathRecipient {
Winson Chungc6c3f852018-04-09 15:41:03 -070053 private static final String TAG = TAG_WITH_CLASS_NAME
54 || (DEBUG_REMOTE_ANIMATIONS && !DEBUG_APP_TRANSITIONS)
55 ? "RemoteAnimationController" : TAG_WM;
Jorim Jaggi33a701a2017-12-01 14:58:18 +010056 private static final long TIMEOUT_MS = 2000;
57
58 private final WindowManagerService mService;
59 private final RemoteAnimationAdapter mRemoteAnimationAdapter;
60 private final ArrayList<RemoteAnimationAdapterWrapper> mPendingAnimations = new ArrayList<>();
61 private final Rect mTmpRect = new Rect();
62 private final Handler mHandler;
Winson Chungc6c3f852018-04-09 15:41:03 -070063 private final Runnable mTimeoutRunnable = () -> cancelAnimation("timeoutRunnable");
Jorim Jaggi33a701a2017-12-01 14:58:18 +010064
Adrian Roos842e7882018-03-26 17:34:06 +020065 private FinishedCallback mFinishedCallback;
66 private boolean mCanceled;
Jorim Jaggi33a701a2017-12-01 14:58:18 +010067
68 RemoteAnimationController(WindowManagerService service,
69 RemoteAnimationAdapter remoteAnimationAdapter, Handler handler) {
70 mService = service;
71 mRemoteAnimationAdapter = remoteAnimationAdapter;
72 mHandler = handler;
73 }
74
75 /**
76 * Creates an animation for each individual {@link AppWindowToken}.
77 *
78 * @param appWindowToken The app to animate.
79 * @param position The position app bounds, in screen coordinates.
80 * @param stackBounds The stack bounds of the app.
81 * @return The adapter to be run on the app.
82 */
83 AnimationAdapter createAnimationAdapter(AppWindowToken appWindowToken, Point position,
84 Rect stackBounds) {
Winson Chungc6c3f852018-04-09 15:41:03 -070085 if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "createAnimationAdapter(): token=" + appWindowToken);
Jorim Jaggi33a701a2017-12-01 14:58:18 +010086 final RemoteAnimationAdapterWrapper adapter = new RemoteAnimationAdapterWrapper(
87 appWindowToken, position, stackBounds);
88 mPendingAnimations.add(adapter);
89 return adapter;
90 }
91
92 /**
93 * Called when the transition is ready to be started, and all leashes have been set up.
94 */
95 void goodToGo() {
Winson Chungc6c3f852018-04-09 15:41:03 -070096 if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "goodToGo()");
Adrian Roos842e7882018-03-26 17:34:06 +020097 if (mPendingAnimations.isEmpty() || mCanceled) {
Winson Chungc6c3f852018-04-09 15:41:03 -070098 if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "goodToGo(): Animation finished before good to go, canceled="
99 + mCanceled + " mPendingAnimations=" + mPendingAnimations.size());
Jorim Jaggi93f9fe32018-01-25 15:06:13 +0100100 onAnimationFinished();
101 return;
102 }
Jorim Jaggia19d7812018-02-01 15:03:59 +0100103
104 // Scale the timeout with the animator scale the controlling app is using.
105 mHandler.postDelayed(mTimeoutRunnable,
106 (long) (TIMEOUT_MS * mService.getCurrentAnimatorScale()));
Jorim Jaggi91f9f3482018-02-14 10:58:26 -0800107 mFinishedCallback = new FinishedCallback(this);
Chavi Weingarten16d0d072018-02-12 23:50:28 +0000108
109 final RemoteAnimationTarget[] animations = createAnimations();
Jorim Jaggic4d29f22018-03-22 16:30:56 +0100110 if (animations.length == 0) {
Winson Chungc6c3f852018-04-09 15:41:03 -0700111 if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "goodToGo(): No apps to animate");
Jorim Jaggic4d29f22018-03-22 16:30:56 +0100112 onAnimationFinished();
113 return;
114 }
Chavi Weingarten16d0d072018-02-12 23:50:28 +0000115 mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
116 try {
Adrian Roos842e7882018-03-26 17:34:06 +0200117 mRemoteAnimationAdapter.getRunner().asBinder().linkToDeath(this, 0);
118 mRemoteAnimationAdapter.getRunner().onAnimationStart(animations, mFinishedCallback);
Chavi Weingarten16d0d072018-02-12 23:50:28 +0000119 } catch (RemoteException e) {
120 Slog.e(TAG, "Failed to start remote animation", e);
121 onAnimationFinished();
122 }
Winson Chungc6c3f852018-04-09 15:41:03 -0700123 if (DEBUG_REMOTE_ANIMATIONS) {
124 Slog.d(TAG, "startAnimation(): Notify animation start:");
125 for (int i = 0; i < mPendingAnimations.size(); i++) {
126 Slog.d(TAG, "\t" + mPendingAnimations.get(i).mAppWindowToken);
127 }
128 } else if (DEBUG_APP_TRANSITIONS) {
129 writeStartDebugStatement();
130 }
Chavi Weingarten16d0d072018-02-12 23:50:28 +0000131 });
Jorim Jaggibc2aabe2018-03-08 17:27:43 +0100132 sendRunningRemoteAnimation(true);
Jorim Jaggif75d1612018-02-27 15:05:21 +0100133 }
134
Winson Chungc6c3f852018-04-09 15:41:03 -0700135 private void cancelAnimation(String reason) {
136 if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "cancelAnimation(): reason=" + reason);
Adrian Roos842e7882018-03-26 17:34:06 +0200137 synchronized (mService.getWindowManagerLock()) {
138 if (mCanceled) {
139 return;
140 }
141 mCanceled = true;
142 }
143 onAnimationFinished();
144 invokeAnimationCancelled();
145 }
146
Jorim Jaggif75d1612018-02-27 15:05:21 +0100147 private void writeStartDebugStatement() {
148 Slog.i(TAG, "Starting remote animation");
149 final StringWriter sw = new StringWriter();
150 final FastPrintWriter pw = new FastPrintWriter(sw);
151 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
152 mPendingAnimations.get(i).dump(pw, "");
153 }
154 pw.close();
155 Slog.i(TAG, sw.toString());
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100156 }
157
158 private RemoteAnimationTarget[] createAnimations() {
Winson Chungc6c3f852018-04-09 15:41:03 -0700159 if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "createAnimations()");
Jorim Jaggi17770212018-01-22 20:01:21 +0100160 final ArrayList<RemoteAnimationTarget> targets = new ArrayList<>();
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100161 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
Jorim Jaggib8a9cbe2018-03-27 18:02:18 +0200162 final RemoteAnimationAdapterWrapper wrapper = mPendingAnimations.get(i);
Winson Chungc6c3f852018-04-09 15:41:03 -0700163 final RemoteAnimationTarget target = wrapper.createRemoteAppAnimation();
Jorim Jaggi17770212018-01-22 20:01:21 +0100164 if (target != null) {
Winson Chungc6c3f852018-04-09 15:41:03 -0700165 if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "\tAdd token=" + wrapper.mAppWindowToken);
Jorim Jaggi17770212018-01-22 20:01:21 +0100166 targets.add(target);
Jorim Jaggic4d29f22018-03-22 16:30:56 +0100167 } else {
Winson Chungc6c3f852018-04-09 15:41:03 -0700168 if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "\tRemove token=" + wrapper.mAppWindowToken);
Jorim Jaggib8a9cbe2018-03-27 18:02:18 +0200169
170 // We can't really start an animation but we still need to make sure to finish the
171 // pending animation that was started by SurfaceAnimator
172 if (wrapper.mCapturedFinishCallback != null) {
173 wrapper.mCapturedFinishCallback.onAnimationFinished(wrapper);
174 }
Jorim Jaggic4d29f22018-03-22 16:30:56 +0100175 mPendingAnimations.remove(i);
Jorim Jaggi17770212018-01-22 20:01:21 +0100176 }
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100177 }
Jorim Jaggi17770212018-01-22 20:01:21 +0100178 return targets.toArray(new RemoteAnimationTarget[targets.size()]);
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100179 }
180
181 private void onAnimationFinished() {
Winson Chungc6c3f852018-04-09 15:41:03 -0700182 if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "onAnimationFinished(): mPendingAnimations="
183 + mPendingAnimations.size());
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100184 mHandler.removeCallbacks(mTimeoutRunnable);
Adrian Roos842e7882018-03-26 17:34:06 +0200185 mRemoteAnimationAdapter.getRunner().asBinder().unlinkToDeath(this, 0);
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100186 synchronized (mService.mWindowMap) {
Jorim Jaggi91f9f3482018-02-14 10:58:26 -0800187 releaseFinishedCallback();
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100188 mService.openSurfaceTransaction();
189 try {
Winson Chungc6c3f852018-04-09 15:41:03 -0700190 if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "onAnimationFinished(): Notify animation finished:");
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100191 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
192 final RemoteAnimationAdapterWrapper adapter = mPendingAnimations.get(i);
193 adapter.mCapturedFinishCallback.onAnimationFinished(adapter);
Winson Chungc6c3f852018-04-09 15:41:03 -0700194 if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "\t" + adapter.mAppWindowToken);
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100195 }
Winson Chungc6c3f852018-04-09 15:41:03 -0700196 } catch (Exception e) {
197 Slog.e(TAG, "Failed to finish remote animation", e);
198 throw e;
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100199 } finally {
200 mService.closeSurfaceTransaction("RemoteAnimationController#finished");
201 }
202 }
Jorim Jaggibc2aabe2018-03-08 17:27:43 +0100203 sendRunningRemoteAnimation(false);
Winson Chungc6c3f852018-04-09 15:41:03 -0700204 if (DEBUG_REMOTE_ANIMATIONS) Slog.i(TAG, "Finishing remote animation");
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100205 }
206
207 private void invokeAnimationCancelled() {
208 try {
209 mRemoteAnimationAdapter.getRunner().onAnimationCancelled();
210 } catch (RemoteException e) {
211 Slog.e(TAG, "Failed to notify cancel", e);
212 }
213 }
214
Jorim Jaggi91f9f3482018-02-14 10:58:26 -0800215 private void releaseFinishedCallback() {
216 if (mFinishedCallback != null) {
217 mFinishedCallback.release();
218 mFinishedCallback = null;
219 }
220 }
221
Jorim Jaggibc2aabe2018-03-08 17:27:43 +0100222 private void sendRunningRemoteAnimation(boolean running) {
223 final int pid = mRemoteAnimationAdapter.getCallingPid();
224 if (pid == 0) {
225 throw new RuntimeException("Calling pid of remote animation was null");
226 }
227 mService.sendSetRunningRemoteAnimation(pid, running);
228 }
229
Adrian Roos842e7882018-03-26 17:34:06 +0200230 @Override
231 public void binderDied() {
Winson Chungc6c3f852018-04-09 15:41:03 -0700232 cancelAnimation("binderDied");
Adrian Roos842e7882018-03-26 17:34:06 +0200233 }
234
Jorim Jaggi91f9f3482018-02-14 10:58:26 -0800235 private static final class FinishedCallback extends IRemoteAnimationFinishedCallback.Stub {
236
237 RemoteAnimationController mOuter;
238
239 FinishedCallback(RemoteAnimationController outer) {
240 mOuter = outer;
241 }
242
243 @Override
244 public void onAnimationFinished() throws RemoteException {
Winson Chungc6c3f852018-04-09 15:41:03 -0700245 if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "app-onAnimationFinished(): mOuter=" + mOuter);
Jorim Jaggiab20e162018-03-27 13:06:09 +0200246 final long token = Binder.clearCallingIdentity();
247 try {
248 if (mOuter != null) {
249 mOuter.onAnimationFinished();
Jorim Jaggi91f9f3482018-02-14 10:58:26 -0800250
Jorim Jaggiab20e162018-03-27 13:06:09 +0200251 // In case the client holds on to the finish callback, make sure we don't leak
252 // RemoteAnimationController which in turn would leak the runner on the client.
253 mOuter = null;
254 }
255 } finally {
256 Binder.restoreCallingIdentity(token);
Jorim Jaggi91f9f3482018-02-14 10:58:26 -0800257 }
258 }
259
260 /**
261 * Marks this callback as not be used anymore by releasing the reference to the outer class
262 * to prevent memory leak.
263 */
264 void release() {
Winson Chungc6c3f852018-04-09 15:41:03 -0700265 if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "app-release(): mOuter=" + mOuter);
Jorim Jaggi91f9f3482018-02-14 10:58:26 -0800266 mOuter = null;
267 }
268 };
269
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100270 private class RemoteAnimationAdapterWrapper implements AnimationAdapter {
271
272 private final AppWindowToken mAppWindowToken;
273 private SurfaceControl mCapturedLeash;
274 private OnAnimationFinishedCallback mCapturedFinishCallback;
275 private final Point mPosition = new Point();
276 private final Rect mStackBounds = new Rect();
Jorim Jaggif75d1612018-02-27 15:05:21 +0100277 private RemoteAnimationTarget mTarget;
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100278
279 RemoteAnimationAdapterWrapper(AppWindowToken appWindowToken, Point position,
280 Rect stackBounds) {
281 mAppWindowToken = appWindowToken;
282 mPosition.set(position.x, position.y);
283 mStackBounds.set(stackBounds);
284 }
285
286 RemoteAnimationTarget createRemoteAppAnimation() {
Jorim Jaggi17770212018-01-22 20:01:21 +0100287 final Task task = mAppWindowToken.getTask();
288 final WindowState mainWindow = mAppWindowToken.findMainWindow();
Jorim Jaggic4d29f22018-03-22 16:30:56 +0100289 if (task == null || mainWindow == null || mCapturedFinishCallback == null
290 || mCapturedLeash == null) {
Jorim Jaggi17770212018-01-22 20:01:21 +0100291 return null;
292 }
Jorim Jaggi817ebdd2018-03-26 15:46:01 +0200293 final Rect insets = new Rect(mainWindow.mContentInsets);
294 InsetUtils.addInsets(insets, mAppWindowToken.getLetterboxInsets());
Jorim Jaggif75d1612018-02-27 15:05:21 +0100295 mTarget = new RemoteAnimationTarget(task.mTaskId, getMode(),
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100296 mCapturedLeash, !mAppWindowToken.fillsParent(),
Jorim Jaggi817ebdd2018-03-26 15:46:01 +0200297 mainWindow.mWinAnimator.mLastClipRect, insets,
Winson Chunge2d72172018-01-25 17:46:20 +0000298 mAppWindowToken.getPrefixOrderIndex(), mPosition, mStackBounds,
Vadim Tryshev593e9562018-03-08 17:15:45 -0800299 task.getWindowConfiguration(), false /*isNotInRecents*/);
Jorim Jaggif75d1612018-02-27 15:05:21 +0100300 return mTarget;
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100301 }
302
303 private int getMode() {
304 if (mService.mOpeningApps.contains(mAppWindowToken)) {
305 return RemoteAnimationTarget.MODE_OPENING;
306 } else {
307 return RemoteAnimationTarget.MODE_CLOSING;
308 }
309 }
310
311 @Override
312 public boolean getDetachWallpaper() {
313 return false;
314 }
315
316 @Override
Jorim Jaggi82c17862018-02-21 17:50:18 +0100317 public boolean getShowWallpaper() {
318 return false;
319 }
320
321 @Override
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100322 public int getBackgroundColor() {
323 return 0;
324 }
325
326 @Override
327 public void startAnimation(SurfaceControl animationLeash, Transaction t,
328 OnAnimationFinishedCallback finishCallback) {
Winson Chungc6c3f852018-04-09 15:41:03 -0700329 if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "startAnimation");
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100330
331 // Restore z-layering, position and stack crop until client has a chance to modify it.
332 t.setLayer(animationLeash, mAppWindowToken.getPrefixOrderIndex());
333 t.setPosition(animationLeash, mPosition.x, mPosition.y);
334 mTmpRect.set(mStackBounds);
335 mTmpRect.offsetTo(0, 0);
336 t.setWindowCrop(animationLeash, mTmpRect);
337 mCapturedLeash = animationLeash;
338 mCapturedFinishCallback = finishCallback;
339 }
340
341 @Override
342 public void onAnimationCancelled(SurfaceControl animationLeash) {
343 mPendingAnimations.remove(this);
344 if (mPendingAnimations.isEmpty()) {
345 mHandler.removeCallbacks(mTimeoutRunnable);
Jorim Jaggi91f9f3482018-02-14 10:58:26 -0800346 releaseFinishedCallback();
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100347 invokeAnimationCancelled();
Jorim Jaggibc2aabe2018-03-08 17:27:43 +0100348 sendRunningRemoteAnimation(false);
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100349 }
350 }
351
352 @Override
353 public long getDurationHint() {
354 return mRemoteAnimationAdapter.getDuration();
355 }
356
357 @Override
358 public long getStatusBarTransitionsStartTime() {
359 return SystemClock.uptimeMillis()
360 + mRemoteAnimationAdapter.getStatusBarTransitionDelay();
361 }
Jorim Jaggif75d1612018-02-27 15:05:21 +0100362
363 @Override
364 public void dump(PrintWriter pw, String prefix) {
365 pw.print(prefix); pw.print("token="); pw.println(mAppWindowToken);
366 if (mTarget != null) {
367 pw.print(prefix); pw.println("Target:");
368 mTarget.dump(pw, prefix + " ");
369 } else {
370 pw.print(prefix); pw.println("Target: null");
371 }
372 }
373
374 @Override
375 public void writeToProto(ProtoOutputStream proto) {
376 final long token = proto.start(REMOTE);
377 if (mTarget != null) {
378 mTarget.writeToProto(proto, TARGET);
379 }
380 proto.end(token);
381 }
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100382 }
383}