blob: 541a6f6213b648cf6dcf2dafc76a380fc7b3a4f7 [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;
Jorim Jaggi817ebdd2018-03-26 15:46:01 +020040import com.android.server.wm.utils.InsetUtils;
Jorim Jaggi33a701a2017-12-01 14:58:18 +010041
Jorim Jaggif75d1612018-02-27 15:05:21 +010042import java.io.PrintWriter;
43import java.io.StringWriter;
Jorim Jaggi33a701a2017-12-01 14:58:18 +010044import java.util.ArrayList;
45
46/**
47 * Helper class to run app animations in a remote process.
48 */
49class RemoteAnimationController {
50 private static final String TAG = TAG_WITH_CLASS_NAME ? "RemoteAnimationController" : TAG_WM;
51 private static final long TIMEOUT_MS = 2000;
52
53 private final WindowManagerService mService;
54 private final RemoteAnimationAdapter mRemoteAnimationAdapter;
55 private final ArrayList<RemoteAnimationAdapterWrapper> mPendingAnimations = new ArrayList<>();
56 private final Rect mTmpRect = new Rect();
57 private final Handler mHandler;
Jorim Jaggi91f9f3482018-02-14 10:58:26 -080058 private FinishedCallback mFinishedCallback;
Jorim Jaggi33a701a2017-12-01 14:58:18 +010059
60 private final Runnable mTimeoutRunnable = () -> {
61 onAnimationFinished();
62 invokeAnimationCancelled();
63 };
64
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() {
Jorim Jaggi93f9fe32018-01-25 15:06:13 +010092 if (mPendingAnimations.isEmpty()) {
93 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 {
109 mRemoteAnimationAdapter.getRunner().onAnimationStart(animations,
110 mFinishedCallback);
111 } 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
122 private void writeStartDebugStatement() {
123 Slog.i(TAG, "Starting remote animation");
124 final StringWriter sw = new StringWriter();
125 final FastPrintWriter pw = new FastPrintWriter(sw);
126 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
127 mPendingAnimations.get(i).dump(pw, "");
128 }
129 pw.close();
130 Slog.i(TAG, sw.toString());
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100131 }
132
133 private RemoteAnimationTarget[] createAnimations() {
Jorim Jaggi17770212018-01-22 20:01:21 +0100134 final ArrayList<RemoteAnimationTarget> targets = new ArrayList<>();
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100135 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
Jorim Jaggib8a9cbe2018-03-27 18:02:18 +0200136 final RemoteAnimationAdapterWrapper wrapper = mPendingAnimations.get(i);
Jorim Jaggi17770212018-01-22 20:01:21 +0100137 final RemoteAnimationTarget target =
138 mPendingAnimations.get(i).createRemoteAppAnimation();
139 if (target != null) {
140 targets.add(target);
Jorim Jaggic4d29f22018-03-22 16:30:56 +0100141 } else {
Jorim Jaggib8a9cbe2018-03-27 18:02:18 +0200142
143 // We can't really start an animation but we still need to make sure to finish the
144 // pending animation that was started by SurfaceAnimator
145 if (wrapper.mCapturedFinishCallback != null) {
146 wrapper.mCapturedFinishCallback.onAnimationFinished(wrapper);
147 }
Jorim Jaggic4d29f22018-03-22 16:30:56 +0100148 mPendingAnimations.remove(i);
Jorim Jaggi17770212018-01-22 20:01:21 +0100149 }
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100150 }
Jorim Jaggi17770212018-01-22 20:01:21 +0100151 return targets.toArray(new RemoteAnimationTarget[targets.size()]);
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100152 }
153
154 private void onAnimationFinished() {
155 mHandler.removeCallbacks(mTimeoutRunnable);
156 synchronized (mService.mWindowMap) {
Jorim Jaggi91f9f3482018-02-14 10:58:26 -0800157 releaseFinishedCallback();
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100158 mService.openSurfaceTransaction();
159 try {
160 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) {
161 final RemoteAnimationAdapterWrapper adapter = mPendingAnimations.get(i);
162 adapter.mCapturedFinishCallback.onAnimationFinished(adapter);
163 }
164 } finally {
165 mService.closeSurfaceTransaction("RemoteAnimationController#finished");
166 }
167 }
Jorim Jaggibc2aabe2018-03-08 17:27:43 +0100168 sendRunningRemoteAnimation(false);
Jorim Jaggif75d1612018-02-27 15:05:21 +0100169 if (DEBUG_APP_TRANSITIONS) Slog.i(TAG, "Finishing remote animation");
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100170 }
171
172 private void invokeAnimationCancelled() {
173 try {
174 mRemoteAnimationAdapter.getRunner().onAnimationCancelled();
175 } catch (RemoteException e) {
176 Slog.e(TAG, "Failed to notify cancel", e);
177 }
178 }
179
Jorim Jaggi91f9f3482018-02-14 10:58:26 -0800180 private void releaseFinishedCallback() {
181 if (mFinishedCallback != null) {
182 mFinishedCallback.release();
183 mFinishedCallback = null;
184 }
185 }
186
Jorim Jaggibc2aabe2018-03-08 17:27:43 +0100187 private void sendRunningRemoteAnimation(boolean running) {
188 final int pid = mRemoteAnimationAdapter.getCallingPid();
189 if (pid == 0) {
190 throw new RuntimeException("Calling pid of remote animation was null");
191 }
192 mService.sendSetRunningRemoteAnimation(pid, running);
193 }
194
Jorim Jaggi91f9f3482018-02-14 10:58:26 -0800195 private static final class FinishedCallback extends IRemoteAnimationFinishedCallback.Stub {
196
197 RemoteAnimationController mOuter;
198
199 FinishedCallback(RemoteAnimationController outer) {
200 mOuter = outer;
201 }
202
203 @Override
204 public void onAnimationFinished() throws RemoteException {
205 if (mOuter != null) {
206 mOuter.onAnimationFinished();
207
208 // In case the client holds on to the finish callback, make sure we don't leak
209 // RemoteAnimationController which in turn would leak the runner on the client.
210 mOuter = null;
211 }
212 }
213
214 /**
215 * Marks this callback as not be used anymore by releasing the reference to the outer class
216 * to prevent memory leak.
217 */
218 void release() {
219 mOuter = null;
220 }
221 };
222
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100223 private class RemoteAnimationAdapterWrapper implements AnimationAdapter {
224
225 private final AppWindowToken mAppWindowToken;
226 private SurfaceControl mCapturedLeash;
227 private OnAnimationFinishedCallback mCapturedFinishCallback;
228 private final Point mPosition = new Point();
229 private final Rect mStackBounds = new Rect();
Jorim Jaggif75d1612018-02-27 15:05:21 +0100230 private RemoteAnimationTarget mTarget;
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100231
232 RemoteAnimationAdapterWrapper(AppWindowToken appWindowToken, Point position,
233 Rect stackBounds) {
234 mAppWindowToken = appWindowToken;
235 mPosition.set(position.x, position.y);
236 mStackBounds.set(stackBounds);
237 }
238
239 RemoteAnimationTarget createRemoteAppAnimation() {
Jorim Jaggi17770212018-01-22 20:01:21 +0100240 final Task task = mAppWindowToken.getTask();
241 final WindowState mainWindow = mAppWindowToken.findMainWindow();
Jorim Jaggic4d29f22018-03-22 16:30:56 +0100242 if (task == null || mainWindow == null || mCapturedFinishCallback == null
243 || mCapturedLeash == null) {
Jorim Jaggi17770212018-01-22 20:01:21 +0100244 return null;
245 }
Jorim Jaggi817ebdd2018-03-26 15:46:01 +0200246 final Rect insets = new Rect(mainWindow.mContentInsets);
247 InsetUtils.addInsets(insets, mAppWindowToken.getLetterboxInsets());
Jorim Jaggif75d1612018-02-27 15:05:21 +0100248 mTarget = new RemoteAnimationTarget(task.mTaskId, getMode(),
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100249 mCapturedLeash, !mAppWindowToken.fillsParent(),
Jorim Jaggi817ebdd2018-03-26 15:46:01 +0200250 mainWindow.mWinAnimator.mLastClipRect, insets,
Winson Chunge2d72172018-01-25 17:46:20 +0000251 mAppWindowToken.getPrefixOrderIndex(), mPosition, mStackBounds,
Vadim Tryshev593e9562018-03-08 17:15:45 -0800252 task.getWindowConfiguration(), false /*isNotInRecents*/);
Jorim Jaggif75d1612018-02-27 15:05:21 +0100253 return mTarget;
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100254 }
255
256 private int getMode() {
257 if (mService.mOpeningApps.contains(mAppWindowToken)) {
258 return RemoteAnimationTarget.MODE_OPENING;
259 } else {
260 return RemoteAnimationTarget.MODE_CLOSING;
261 }
262 }
263
264 @Override
265 public boolean getDetachWallpaper() {
266 return false;
267 }
268
269 @Override
Jorim Jaggi82c17862018-02-21 17:50:18 +0100270 public boolean getShowWallpaper() {
271 return false;
272 }
273
274 @Override
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100275 public int getBackgroundColor() {
276 return 0;
277 }
278
279 @Override
280 public void startAnimation(SurfaceControl animationLeash, Transaction t,
281 OnAnimationFinishedCallback finishCallback) {
282
283 // Restore z-layering, position and stack crop until client has a chance to modify it.
284 t.setLayer(animationLeash, mAppWindowToken.getPrefixOrderIndex());
285 t.setPosition(animationLeash, mPosition.x, mPosition.y);
286 mTmpRect.set(mStackBounds);
287 mTmpRect.offsetTo(0, 0);
288 t.setWindowCrop(animationLeash, mTmpRect);
289 mCapturedLeash = animationLeash;
290 mCapturedFinishCallback = finishCallback;
291 }
292
293 @Override
294 public void onAnimationCancelled(SurfaceControl animationLeash) {
295 mPendingAnimations.remove(this);
296 if (mPendingAnimations.isEmpty()) {
297 mHandler.removeCallbacks(mTimeoutRunnable);
Jorim Jaggi91f9f3482018-02-14 10:58:26 -0800298 releaseFinishedCallback();
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100299 invokeAnimationCancelled();
Jorim Jaggibc2aabe2018-03-08 17:27:43 +0100300 sendRunningRemoteAnimation(false);
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100301 }
302 }
303
304 @Override
305 public long getDurationHint() {
306 return mRemoteAnimationAdapter.getDuration();
307 }
308
309 @Override
310 public long getStatusBarTransitionsStartTime() {
311 return SystemClock.uptimeMillis()
312 + mRemoteAnimationAdapter.getStatusBarTransitionDelay();
313 }
Jorim Jaggif75d1612018-02-27 15:05:21 +0100314
315 @Override
316 public void dump(PrintWriter pw, String prefix) {
317 pw.print(prefix); pw.print("token="); pw.println(mAppWindowToken);
318 if (mTarget != null) {
319 pw.print(prefix); pw.println("Target:");
320 mTarget.dump(pw, prefix + " ");
321 } else {
322 pw.print(prefix); pw.println("Target: null");
323 }
324 }
325
326 @Override
327 public void writeToProto(ProtoOutputStream proto) {
328 final long token = proto.start(REMOTE);
329 if (mTarget != null) {
330 mTarget.writeToProto(proto, TARGET);
331 }
332 proto.end(token);
333 }
Jorim Jaggi33a701a2017-12-01 14:58:18 +0100334 }
335}