blob: 3be7b23590e58ba9816d9b0b4eba6a62ff65b0b1 [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;
Jorim Jaggi33a701a2017-12-01 14:58:18 +010064
65 RemoteAnimationController(WindowManagerService service,
66 RemoteAnimationAdapter remoteAnimationAdapter, Handler handler) {
67 mService = service;
68 mRemoteAnimationAdapter = remoteAnimationAdapter;
69 mHandler = handler;
70 }
71
72 /**
73 * Creates an animation for each individual {@link AppWindowToken}.
74 *
75 * @param appWindowToken The app to animate.
76 * @param position The position app bounds, in screen coordinates.
77 * @param stackBounds The stack bounds of the app.
78 * @return The adapter to be run on the app.
79 */
80 AnimationAdapter createAnimationAdapter(AppWindowToken appWindowToken, Point position,
81 Rect stackBounds) {
82 final RemoteAnimationAdapterWrapper adapter = new RemoteAnimationAdapterWrapper(
83 appWindowToken, position, stackBounds);
84 mPendingAnimations.add(adapter);
85 return adapter;
86 }
87
88 /**
89 * Called when the transition is ready to be started, and all leashes have been set up.
90 */
91 void goodToGo() {
Adrian Roos842e7882018-03-26 17:34:06 +020092 if (mPendingAnimations.isEmpty() || mCanceled) {
Jorim Jaggi93f9fe32018-01-25 15:06:13 +010093 onAnimationFinished();
94 return;
95 }
Jorim Jaggia19d7812018-02-01 15:03:59 +010096
97 // Scale the timeout with the animator scale the controlling app is using.
98 mHandler.postDelayed(mTimeoutRunnable,
99 (long) (TIMEOUT_MS * mService.getCurrentAnimatorScale()));
Jorim Jaggi91f9f3482018-02-14 10:58:26 -0800100 mFinishedCallback = new FinishedCallback(this);
Chavi Weingarten16d0d072018-02-12 23:50:28 +0000101
102 final RemoteAnimationTarget[] animations = createAnimations();
Jorim Jaggic4d29f22018-03-22 16:30:56 +0100103 if (animations.length == 0) {
104 onAnimationFinished();
105 return;
106 }
Chavi Weingarten16d0d072018-02-12 23:50:28 +0000107 mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
108 try {
Adrian Roos842e7882018-03-26 17:34:06 +0200109 mRemoteAnimationAdapter.getRunner().asBinder().linkToDeath(this, 0);
110 mRemoteAnimationAdapter.getRunner().onAnimationStart(animations, mFinishedCallback);
Chavi Weingarten16d0d072018-02-12 23:50:28 +0000111 } catch (RemoteException e) {
112 Slog.e(TAG, "Failed to start remote animation", e);
113 onAnimationFinished();
114 }
115 });
Jorim Jaggibc2aabe2018-03-08 17:27:43 +0100116 sendRunningRemoteAnimation(true);
Jorim Jaggif75d1612018-02-27 15:05:21 +0100117 if (DEBUG_APP_TRANSITIONS) {
118 writeStartDebugStatement();
119 }
120 }
121
Adrian Roos842e7882018-03-26 17:34:06 +0200122 private void cancelAnimation() {
123 synchronized (mService.getWindowManagerLock()) {
124 if (mCanceled) {
125 return;
126 }
127 mCanceled = true;
128 }
129 onAnimationFinished();
130 invokeAnimationCancelled();
131 }
132
Jorim Jaggif75d1612018-02-27 15:05:21 +0100133 private void writeStartDebugStatement() {
134 Slog.i(TAG, "Starting remote animation");
135 final StringWriter sw = new StringWriter();
136 final FastPrintWriter pw = new FastPrintWriter(sw);
137 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
138 mPendingAnimations.get(i).dump(pw, "");
139 }
140 pw.close();
141 Slog.i(TAG, sw.toString());
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100142 }
143
144 private RemoteAnimationTarget[] createAnimations() {
Jorim Jaggi17770212018-01-22 20:01:21 +0100145 final ArrayList<RemoteAnimationTarget> targets = new ArrayList<>();
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100146 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
Jorim Jaggib8a9cbe2018-03-27 18:02:18 +0200147 final RemoteAnimationAdapterWrapper wrapper = mPendingAnimations.get(i);
Jorim Jaggi17770212018-01-22 20:01:21 +0100148 final RemoteAnimationTarget target =
149 mPendingAnimations.get(i).createRemoteAppAnimation();
150 if (target != null) {
151 targets.add(target);
Jorim Jaggic4d29f22018-03-22 16:30:56 +0100152 } else {
Jorim Jaggib8a9cbe2018-03-27 18:02:18 +0200153
154 // We can't really start an animation but we still need to make sure to finish the
155 // pending animation that was started by SurfaceAnimator
156 if (wrapper.mCapturedFinishCallback != null) {
157 wrapper.mCapturedFinishCallback.onAnimationFinished(wrapper);
158 }
Jorim Jaggic4d29f22018-03-22 16:30:56 +0100159 mPendingAnimations.remove(i);
Jorim Jaggi17770212018-01-22 20:01:21 +0100160 }
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100161 }
Jorim Jaggi17770212018-01-22 20:01:21 +0100162 return targets.toArray(new RemoteAnimationTarget[targets.size()]);
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100163 }
164
165 private void onAnimationFinished() {
166 mHandler.removeCallbacks(mTimeoutRunnable);
Adrian Roos842e7882018-03-26 17:34:06 +0200167 mRemoteAnimationAdapter.getRunner().asBinder().unlinkToDeath(this, 0);
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100168 synchronized (mService.mWindowMap) {
Jorim Jaggi91f9f3482018-02-14 10:58:26 -0800169 releaseFinishedCallback();
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100170 mService.openSurfaceTransaction();
171 try {
172 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
173 final RemoteAnimationAdapterWrapper adapter = mPendingAnimations.get(i);
174 adapter.mCapturedFinishCallback.onAnimationFinished(adapter);
175 }
176 } finally {
177 mService.closeSurfaceTransaction("RemoteAnimationController#finished");
178 }
179 }
Jorim Jaggibc2aabe2018-03-08 17:27:43 +0100180 sendRunningRemoteAnimation(false);
Jorim Jaggif75d1612018-02-27 15:05:21 +0100181 if (DEBUG_APP_TRANSITIONS) Slog.i(TAG, "Finishing remote animation");
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100182 }
183
184 private void invokeAnimationCancelled() {
185 try {
186 mRemoteAnimationAdapter.getRunner().onAnimationCancelled();
187 } catch (RemoteException e) {
188 Slog.e(TAG, "Failed to notify cancel", e);
189 }
190 }
191
Jorim Jaggi91f9f3482018-02-14 10:58:26 -0800192 private void releaseFinishedCallback() {
193 if (mFinishedCallback != null) {
194 mFinishedCallback.release();
195 mFinishedCallback = null;
196 }
197 }
198
Jorim Jaggibc2aabe2018-03-08 17:27:43 +0100199 private void sendRunningRemoteAnimation(boolean running) {
200 final int pid = mRemoteAnimationAdapter.getCallingPid();
201 if (pid == 0) {
202 throw new RuntimeException("Calling pid of remote animation was null");
203 }
204 mService.sendSetRunningRemoteAnimation(pid, running);
205 }
206
Adrian Roos842e7882018-03-26 17:34:06 +0200207 @Override
208 public void binderDied() {
209 cancelAnimation();
210 }
211
Jorim Jaggi91f9f3482018-02-14 10:58:26 -0800212 private static final class FinishedCallback extends IRemoteAnimationFinishedCallback.Stub {
213
214 RemoteAnimationController mOuter;
215
216 FinishedCallback(RemoteAnimationController outer) {
217 mOuter = outer;
218 }
219
220 @Override
221 public void onAnimationFinished() throws RemoteException {
Jorim Jaggiab20e162018-03-27 13:06:09 +0200222 final long token = Binder.clearCallingIdentity();
223 try {
224 if (mOuter != null) {
225 mOuter.onAnimationFinished();
Jorim Jaggi91f9f3482018-02-14 10:58:26 -0800226
Jorim Jaggiab20e162018-03-27 13:06:09 +0200227 // In case the client holds on to the finish callback, make sure we don't leak
228 // RemoteAnimationController which in turn would leak the runner on the client.
229 mOuter = null;
230 }
231 } finally {
232 Binder.restoreCallingIdentity(token);
Jorim Jaggi91f9f3482018-02-14 10:58:26 -0800233 }
234 }
235
236 /**
237 * Marks this callback as not be used anymore by releasing the reference to the outer class
238 * to prevent memory leak.
239 */
240 void release() {
241 mOuter = null;
242 }
243 };
244
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100245 private class RemoteAnimationAdapterWrapper implements AnimationAdapter {
246
247 private final AppWindowToken mAppWindowToken;
248 private SurfaceControl mCapturedLeash;
249 private OnAnimationFinishedCallback mCapturedFinishCallback;
250 private final Point mPosition = new Point();
251 private final Rect mStackBounds = new Rect();
Jorim Jaggif75d1612018-02-27 15:05:21 +0100252 private RemoteAnimationTarget mTarget;
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100253
254 RemoteAnimationAdapterWrapper(AppWindowToken appWindowToken, Point position,
255 Rect stackBounds) {
256 mAppWindowToken = appWindowToken;
257 mPosition.set(position.x, position.y);
258 mStackBounds.set(stackBounds);
259 }
260
261 RemoteAnimationTarget createRemoteAppAnimation() {
Jorim Jaggi17770212018-01-22 20:01:21 +0100262 final Task task = mAppWindowToken.getTask();
263 final WindowState mainWindow = mAppWindowToken.findMainWindow();
Jorim Jaggic4d29f22018-03-22 16:30:56 +0100264 if (task == null || mainWindow == null || mCapturedFinishCallback == null
265 || mCapturedLeash == null) {
Jorim Jaggi17770212018-01-22 20:01:21 +0100266 return null;
267 }
Jorim Jaggi817ebdd2018-03-26 15:46:01 +0200268 final Rect insets = new Rect(mainWindow.mContentInsets);
269 InsetUtils.addInsets(insets, mAppWindowToken.getLetterboxInsets());
Jorim Jaggif75d1612018-02-27 15:05:21 +0100270 mTarget = new RemoteAnimationTarget(task.mTaskId, getMode(),
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100271 mCapturedLeash, !mAppWindowToken.fillsParent(),
Jorim Jaggi817ebdd2018-03-26 15:46:01 +0200272 mainWindow.mWinAnimator.mLastClipRect, insets,
Winson Chunge2d72172018-01-25 17:46:20 +0000273 mAppWindowToken.getPrefixOrderIndex(), mPosition, mStackBounds,
Vadim Tryshev593e9562018-03-08 17:15:45 -0800274 task.getWindowConfiguration(), false /*isNotInRecents*/);
Jorim Jaggif75d1612018-02-27 15:05:21 +0100275 return mTarget;
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100276 }
277
278 private int getMode() {
279 if (mService.mOpeningApps.contains(mAppWindowToken)) {
280 return RemoteAnimationTarget.MODE_OPENING;
281 } else {
282 return RemoteAnimationTarget.MODE_CLOSING;
283 }
284 }
285
286 @Override
287 public boolean getDetachWallpaper() {
288 return false;
289 }
290
291 @Override
Jorim Jaggi82c17862018-02-21 17:50:18 +0100292 public boolean getShowWallpaper() {
293 return false;
294 }
295
296 @Override
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100297 public int getBackgroundColor() {
298 return 0;
299 }
300
301 @Override
302 public void startAnimation(SurfaceControl animationLeash, Transaction t,
303 OnAnimationFinishedCallback finishCallback) {
304
305 // Restore z-layering, position and stack crop until client has a chance to modify it.
306 t.setLayer(animationLeash, mAppWindowToken.getPrefixOrderIndex());
307 t.setPosition(animationLeash, mPosition.x, mPosition.y);
308 mTmpRect.set(mStackBounds);
309 mTmpRect.offsetTo(0, 0);
310 t.setWindowCrop(animationLeash, mTmpRect);
311 mCapturedLeash = animationLeash;
312 mCapturedFinishCallback = finishCallback;
313 }
314
315 @Override
316 public void onAnimationCancelled(SurfaceControl animationLeash) {
317 mPendingAnimations.remove(this);
318 if (mPendingAnimations.isEmpty()) {
319 mHandler.removeCallbacks(mTimeoutRunnable);
Jorim Jaggi91f9f3482018-02-14 10:58:26 -0800320 releaseFinishedCallback();
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100321 invokeAnimationCancelled();
Jorim Jaggibc2aabe2018-03-08 17:27:43 +0100322 sendRunningRemoteAnimation(false);
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100323 }
324 }
325
326 @Override
327 public long getDurationHint() {
328 return mRemoteAnimationAdapter.getDuration();
329 }
330
331 @Override
332 public long getStatusBarTransitionsStartTime() {
333 return SystemClock.uptimeMillis()
334 + mRemoteAnimationAdapter.getStatusBarTransitionDelay();
335 }
Jorim Jaggif75d1612018-02-27 15:05:21 +0100336
337 @Override
338 public void dump(PrintWriter pw, String prefix) {
339 pw.print(prefix); pw.print("token="); pw.println(mAppWindowToken);
340 if (mTarget != null) {
341 pw.print(prefix); pw.println("Target:");
342 mTarget.dump(pw, prefix + " ");
343 } else {
344 pw.print(prefix); pw.println("Target: null");
345 }
346 }
347
348 @Override
349 public void writeToProto(ProtoOutputStream proto) {
350 final long token = proto.start(REMOTE);
351 if (mTarget != null) {
352 mTarget.writeToProto(proto, TARGET);
353 }
354 proto.end(token);
355 }
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100356 }
357}