blob: 67ef4710310355200adbdf89008f33a6af402858 [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;
Adrian Roos653c6c12018-04-09 14:12:46 -070067 private boolean mLinkedToDeathOfRunner;
Jorim Jaggi33a701a2017-12-01 14:58:18 +010068
69 RemoteAnimationController(WindowManagerService service,
70 RemoteAnimationAdapter remoteAnimationAdapter, Handler handler) {
71 mService = service;
72 mRemoteAnimationAdapter = remoteAnimationAdapter;
73 mHandler = handler;
74 }
75
76 /**
77 * Creates an animation for each individual {@link AppWindowToken}.
78 *
79 * @param appWindowToken The app to animate.
80 * @param position The position app bounds, in screen coordinates.
81 * @param stackBounds The stack bounds of the app.
82 * @return The adapter to be run on the app.
83 */
84 AnimationAdapter createAnimationAdapter(AppWindowToken appWindowToken, Point position,
85 Rect stackBounds) {
Winson Chung703719b2018-04-18 17:53:15 -070086 if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "createAnimationAdapter(): token="
87 + appWindowToken);
Jorim Jaggi33a701a2017-12-01 14:58:18 +010088 final RemoteAnimationAdapterWrapper adapter = new RemoteAnimationAdapterWrapper(
89 appWindowToken, position, stackBounds);
90 mPendingAnimations.add(adapter);
91 return adapter;
92 }
93
94 /**
95 * Called when the transition is ready to be started, and all leashes have been set up.
96 */
97 void goodToGo() {
Winson Chungc6c3f852018-04-09 15:41:03 -070098 if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "goodToGo()");
Adrian Roos842e7882018-03-26 17:34:06 +020099 if (mPendingAnimations.isEmpty() || mCanceled) {
Winson Chung703719b2018-04-18 17:53:15 -0700100 if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "goodToGo(): Animation finished already,"
101 + " canceled=" + mCanceled
102 + " mPendingAnimations=" + mPendingAnimations.size());
Jorim Jaggi93f9fe32018-01-25 15:06:13 +0100103 onAnimationFinished();
104 return;
105 }
Jorim Jaggia19d7812018-02-01 15:03:59 +0100106
107 // Scale the timeout with the animator scale the controlling app is using.
108 mHandler.postDelayed(mTimeoutRunnable,
109 (long) (TIMEOUT_MS * mService.getCurrentAnimatorScale()));
Jorim Jaggi91f9f3482018-02-14 10:58:26 -0800110 mFinishedCallback = new FinishedCallback(this);
Chavi Weingarten16d0d072018-02-12 23:50:28 +0000111
112 final RemoteAnimationTarget[] animations = createAnimations();
Jorim Jaggic4d29f22018-03-22 16:30:56 +0100113 if (animations.length == 0) {
Winson Chungc6c3f852018-04-09 15:41:03 -0700114 if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "goodToGo(): No apps to animate");
Jorim Jaggic4d29f22018-03-22 16:30:56 +0100115 onAnimationFinished();
116 return;
117 }
Chavi Weingarten16d0d072018-02-12 23:50:28 +0000118 mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
119 try {
Adrian Roos653c6c12018-04-09 14:12:46 -0700120 linkToDeathOfRunner();
Adrian Roos842e7882018-03-26 17:34:06 +0200121 mRemoteAnimationAdapter.getRunner().onAnimationStart(animations, mFinishedCallback);
Chavi Weingarten16d0d072018-02-12 23:50:28 +0000122 } catch (RemoteException e) {
123 Slog.e(TAG, "Failed to start remote animation", e);
124 onAnimationFinished();
125 }
Winson Chungc6c3f852018-04-09 15:41:03 -0700126 if (DEBUG_REMOTE_ANIMATIONS) {
127 Slog.d(TAG, "startAnimation(): Notify animation start:");
Winson Chungc6c3f852018-04-09 15:41:03 -0700128 writeStartDebugStatement();
129 }
Chavi Weingarten16d0d072018-02-12 23:50:28 +0000130 });
Jorim Jaggibc2aabe2018-03-08 17:27:43 +0100131 sendRunningRemoteAnimation(true);
Jorim Jaggif75d1612018-02-27 15:05:21 +0100132 }
133
Winson Chungc6c3f852018-04-09 15:41:03 -0700134 private void cancelAnimation(String reason) {
135 if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "cancelAnimation(): reason=" + reason);
Adrian Roos842e7882018-03-26 17:34:06 +0200136 synchronized (mService.getWindowManagerLock()) {
137 if (mCanceled) {
138 return;
139 }
140 mCanceled = true;
141 }
142 onAnimationFinished();
143 invokeAnimationCancelled();
144 }
145
Jorim Jaggif75d1612018-02-27 15:05:21 +0100146 private void writeStartDebugStatement() {
147 Slog.i(TAG, "Starting remote animation");
148 final StringWriter sw = new StringWriter();
149 final FastPrintWriter pw = new FastPrintWriter(sw);
150 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
151 mPendingAnimations.get(i).dump(pw, "");
152 }
153 pw.close();
154 Slog.i(TAG, sw.toString());
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100155 }
156
157 private RemoteAnimationTarget[] createAnimations() {
Winson Chungc6c3f852018-04-09 15:41:03 -0700158 if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "createAnimations()");
Jorim Jaggi17770212018-01-22 20:01:21 +0100159 final ArrayList<RemoteAnimationTarget> targets = new ArrayList<>();
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100160 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
Jorim Jaggib8a9cbe2018-03-27 18:02:18 +0200161 final RemoteAnimationAdapterWrapper wrapper = mPendingAnimations.get(i);
Winson Chungc6c3f852018-04-09 15:41:03 -0700162 final RemoteAnimationTarget target = wrapper.createRemoteAppAnimation();
Jorim Jaggi17770212018-01-22 20:01:21 +0100163 if (target != null) {
Winson Chungc6c3f852018-04-09 15:41:03 -0700164 if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "\tAdd token=" + wrapper.mAppWindowToken);
Jorim Jaggi17770212018-01-22 20:01:21 +0100165 targets.add(target);
Jorim Jaggic4d29f22018-03-22 16:30:56 +0100166 } else {
Winson Chung703719b2018-04-18 17:53:15 -0700167 if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "\tRemove token="
168 + 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);
185 synchronized (mService.mWindowMap) {
Adrian Roos653c6c12018-04-09 14:12:46 -0700186 unlinkToDeathOfRunner();
Jorim Jaggi91f9f3482018-02-14 10:58:26 -0800187 releaseFinishedCallback();
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100188 mService.openSurfaceTransaction();
189 try {
Winson Chung703719b2018-04-18 17:53:15 -0700190 if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG,
191 "onAnimationFinished(): Notify animation finished:");
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100192 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
193 final RemoteAnimationAdapterWrapper adapter = mPendingAnimations.get(i);
194 adapter.mCapturedFinishCallback.onAnimationFinished(adapter);
Winson Chungc6c3f852018-04-09 15:41:03 -0700195 if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "\t" + adapter.mAppWindowToken);
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100196 }
Winson Chungc6c3f852018-04-09 15:41:03 -0700197 } catch (Exception e) {
198 Slog.e(TAG, "Failed to finish remote animation", e);
199 throw e;
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100200 } finally {
201 mService.closeSurfaceTransaction("RemoteAnimationController#finished");
202 }
203 }
Jorim Jaggibc2aabe2018-03-08 17:27:43 +0100204 sendRunningRemoteAnimation(false);
Winson Chungc6c3f852018-04-09 15:41:03 -0700205 if (DEBUG_REMOTE_ANIMATIONS) Slog.i(TAG, "Finishing remote animation");
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100206 }
207
208 private void invokeAnimationCancelled() {
209 try {
210 mRemoteAnimationAdapter.getRunner().onAnimationCancelled();
211 } catch (RemoteException e) {
212 Slog.e(TAG, "Failed to notify cancel", e);
213 }
214 }
215
Jorim Jaggi91f9f3482018-02-14 10:58:26 -0800216 private void releaseFinishedCallback() {
217 if (mFinishedCallback != null) {
218 mFinishedCallback.release();
219 mFinishedCallback = null;
220 }
221 }
222
Jorim Jaggibc2aabe2018-03-08 17:27:43 +0100223 private void sendRunningRemoteAnimation(boolean running) {
224 final int pid = mRemoteAnimationAdapter.getCallingPid();
225 if (pid == 0) {
226 throw new RuntimeException("Calling pid of remote animation was null");
227 }
228 mService.sendSetRunningRemoteAnimation(pid, running);
229 }
230
Adrian Roos653c6c12018-04-09 14:12:46 -0700231 private void linkToDeathOfRunner() throws RemoteException {
232 if (!mLinkedToDeathOfRunner) {
233 mRemoteAnimationAdapter.getRunner().asBinder().linkToDeath(this, 0);
234 mLinkedToDeathOfRunner = true;
235 }
236 }
237
238 private void unlinkToDeathOfRunner() {
239 if (mLinkedToDeathOfRunner) {
240 mRemoteAnimationAdapter.getRunner().asBinder().unlinkToDeath(this, 0);
241 mLinkedToDeathOfRunner = false;
242 }
243 }
244
Adrian Roos842e7882018-03-26 17:34:06 +0200245 @Override
246 public void binderDied() {
Winson Chungc6c3f852018-04-09 15:41:03 -0700247 cancelAnimation("binderDied");
Adrian Roos842e7882018-03-26 17:34:06 +0200248 }
249
Jorim Jaggi91f9f3482018-02-14 10:58:26 -0800250 private static final class FinishedCallback extends IRemoteAnimationFinishedCallback.Stub {
251
252 RemoteAnimationController mOuter;
253
254 FinishedCallback(RemoteAnimationController outer) {
255 mOuter = outer;
256 }
257
258 @Override
259 public void onAnimationFinished() throws RemoteException {
Winson Chungc6c3f852018-04-09 15:41:03 -0700260 if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "app-onAnimationFinished(): mOuter=" + mOuter);
Jorim Jaggiab20e162018-03-27 13:06:09 +0200261 final long token = Binder.clearCallingIdentity();
262 try {
263 if (mOuter != null) {
264 mOuter.onAnimationFinished();
Jorim Jaggi91f9f3482018-02-14 10:58:26 -0800265
Jorim Jaggiab20e162018-03-27 13:06:09 +0200266 // In case the client holds on to the finish callback, make sure we don't leak
267 // RemoteAnimationController which in turn would leak the runner on the client.
268 mOuter = null;
269 }
270 } finally {
271 Binder.restoreCallingIdentity(token);
Jorim Jaggi91f9f3482018-02-14 10:58:26 -0800272 }
273 }
274
275 /**
276 * Marks this callback as not be used anymore by releasing the reference to the outer class
277 * to prevent memory leak.
278 */
279 void release() {
Winson Chungc6c3f852018-04-09 15:41:03 -0700280 if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "app-release(): mOuter=" + mOuter);
Jorim Jaggi91f9f3482018-02-14 10:58:26 -0800281 mOuter = null;
282 }
283 };
284
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100285 private class RemoteAnimationAdapterWrapper implements AnimationAdapter {
286
287 private final AppWindowToken mAppWindowToken;
288 private SurfaceControl mCapturedLeash;
289 private OnAnimationFinishedCallback mCapturedFinishCallback;
290 private final Point mPosition = new Point();
291 private final Rect mStackBounds = new Rect();
Jorim Jaggif75d1612018-02-27 15:05:21 +0100292 private RemoteAnimationTarget mTarget;
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100293
294 RemoteAnimationAdapterWrapper(AppWindowToken appWindowToken, Point position,
295 Rect stackBounds) {
296 mAppWindowToken = appWindowToken;
297 mPosition.set(position.x, position.y);
298 mStackBounds.set(stackBounds);
299 }
300
301 RemoteAnimationTarget createRemoteAppAnimation() {
Jorim Jaggi17770212018-01-22 20:01:21 +0100302 final Task task = mAppWindowToken.getTask();
303 final WindowState mainWindow = mAppWindowToken.findMainWindow();
Jorim Jaggic4d29f22018-03-22 16:30:56 +0100304 if (task == null || mainWindow == null || mCapturedFinishCallback == null
305 || mCapturedLeash == null) {
Jorim Jaggi17770212018-01-22 20:01:21 +0100306 return null;
307 }
Jorim Jaggi817ebdd2018-03-26 15:46:01 +0200308 final Rect insets = new Rect(mainWindow.mContentInsets);
309 InsetUtils.addInsets(insets, mAppWindowToken.getLetterboxInsets());
Jorim Jaggif75d1612018-02-27 15:05:21 +0100310 mTarget = new RemoteAnimationTarget(task.mTaskId, getMode(),
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100311 mCapturedLeash, !mAppWindowToken.fillsParent(),
Jorim Jaggi817ebdd2018-03-26 15:46:01 +0200312 mainWindow.mWinAnimator.mLastClipRect, insets,
Winson Chunge2d72172018-01-25 17:46:20 +0000313 mAppWindowToken.getPrefixOrderIndex(), mPosition, mStackBounds,
Vadim Tryshev593e9562018-03-08 17:15:45 -0800314 task.getWindowConfiguration(), false /*isNotInRecents*/);
Jorim Jaggif75d1612018-02-27 15:05:21 +0100315 return mTarget;
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100316 }
317
318 private int getMode() {
319 if (mService.mOpeningApps.contains(mAppWindowToken)) {
320 return RemoteAnimationTarget.MODE_OPENING;
321 } else {
322 return RemoteAnimationTarget.MODE_CLOSING;
323 }
324 }
325
326 @Override
327 public boolean getDetachWallpaper() {
328 return false;
329 }
330
331 @Override
Jorim Jaggi82c17862018-02-21 17:50:18 +0100332 public boolean getShowWallpaper() {
333 return false;
334 }
335
336 @Override
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100337 public int getBackgroundColor() {
338 return 0;
339 }
340
341 @Override
342 public void startAnimation(SurfaceControl animationLeash, Transaction t,
343 OnAnimationFinishedCallback finishCallback) {
Winson Chungc6c3f852018-04-09 15:41:03 -0700344 if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "startAnimation");
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100345
346 // Restore z-layering, position and stack crop until client has a chance to modify it.
347 t.setLayer(animationLeash, mAppWindowToken.getPrefixOrderIndex());
348 t.setPosition(animationLeash, mPosition.x, mPosition.y);
349 mTmpRect.set(mStackBounds);
350 mTmpRect.offsetTo(0, 0);
351 t.setWindowCrop(animationLeash, mTmpRect);
352 mCapturedLeash = animationLeash;
353 mCapturedFinishCallback = finishCallback;
354 }
355
356 @Override
357 public void onAnimationCancelled(SurfaceControl animationLeash) {
358 mPendingAnimations.remove(this);
359 if (mPendingAnimations.isEmpty()) {
360 mHandler.removeCallbacks(mTimeoutRunnable);
Jorim Jaggi91f9f3482018-02-14 10:58:26 -0800361 releaseFinishedCallback();
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100362 invokeAnimationCancelled();
Jorim Jaggibc2aabe2018-03-08 17:27:43 +0100363 sendRunningRemoteAnimation(false);
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100364 }
365 }
366
367 @Override
368 public long getDurationHint() {
369 return mRemoteAnimationAdapter.getDuration();
370 }
371
372 @Override
373 public long getStatusBarTransitionsStartTime() {
374 return SystemClock.uptimeMillis()
375 + mRemoteAnimationAdapter.getStatusBarTransitionDelay();
376 }
Jorim Jaggif75d1612018-02-27 15:05:21 +0100377
378 @Override
379 public void dump(PrintWriter pw, String prefix) {
380 pw.print(prefix); pw.print("token="); pw.println(mAppWindowToken);
381 if (mTarget != null) {
382 pw.print(prefix); pw.println("Target:");
383 mTarget.dump(pw, prefix + " ");
384 } else {
385 pw.print(prefix); pw.println("Target: null");
386 }
387 }
388
389 @Override
390 public void writeToProto(ProtoOutputStream proto) {
391 final long token = proto.start(REMOTE);
392 if (mTarget != null) {
393 mTarget.writeToProto(proto, TARGET);
394 }
395 proto.end(token);
396 }
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100397 }
398}