1 /* 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 17 package com.android.server.wm; 18 19 import static com.android.server.wm.AnimationAdapterProto.REMOTE; 20 import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET; 21 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS; 22 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_REMOTE_ANIMATIONS; 23 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; 24 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 25 26 import android.graphics.Point; 27 import android.graphics.Rect; 28 import android.os.Binder; 29 import android.os.Handler; 30 import android.os.IBinder.DeathRecipient; 31 import android.os.RemoteException; 32 import android.os.SystemClock; 33 import android.util.Slog; 34 import android.util.proto.ProtoOutputStream; 35 import android.view.IRemoteAnimationFinishedCallback; 36 import android.view.RemoteAnimationAdapter; 37 import android.view.RemoteAnimationTarget; 38 import android.view.SurfaceControl; 39 import android.view.SurfaceControl.Transaction; 40 41 import com.android.internal.util.FastPrintWriter; 42 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback; 43 import com.android.server.wm.utils.InsetUtils; 44 45 import java.io.PrintWriter; 46 import java.io.StringWriter; 47 import java.util.ArrayList; 48 49 /** 50 * Helper class to run app animations in a remote process. 51 */ 52 class RemoteAnimationController implements DeathRecipient { 53 private static final String TAG = TAG_WITH_CLASS_NAME 54 || (DEBUG_REMOTE_ANIMATIONS && !DEBUG_APP_TRANSITIONS) 55 ? "RemoteAnimationController" : TAG_WM; 56 private static final long TIMEOUT_MS = 2000; 57 58 private final WindowManagerService mService; 59 private final RemoteAnimationAdapter mRemoteAnimationAdapter; 60 private final ArrayList<RemoteAnimationRecord> mPendingAnimations = new ArrayList<>(); 61 private final Rect mTmpRect = new Rect(); 62 private final Handler mHandler; 63 private final Runnable mTimeoutRunnable = () -> cancelAnimation("timeoutRunnable"); 64 65 private FinishedCallback mFinishedCallback; 66 private boolean mCanceled; 67 private boolean mLinkedToDeathOfRunner; 68 RemoteAnimationController(WindowManagerService service, RemoteAnimationAdapter remoteAnimationAdapter, Handler handler)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 record 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 relative to position. 82 * @param startBounds The stack bounds before the transition, in screen coordinates 83 * @return The record representing animation(s) to run on the app. 84 */ createRemoteAnimationRecord(AppWindowToken appWindowToken, Point position, Rect stackBounds, Rect startBounds)85 RemoteAnimationRecord createRemoteAnimationRecord(AppWindowToken appWindowToken, 86 Point position, Rect stackBounds, Rect startBounds) { 87 if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "createAnimationAdapter(): token=" 88 + appWindowToken); 89 final RemoteAnimationRecord adapters = 90 new RemoteAnimationRecord(appWindowToken, position, stackBounds, startBounds); 91 mPendingAnimations.add(adapters); 92 return adapters; 93 } 94 95 /** 96 * Called when the transition is ready to be started, and all leashes have been set up. 97 */ goodToGo()98 void goodToGo() { 99 if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "goodToGo()"); 100 if (mPendingAnimations.isEmpty() || mCanceled) { 101 if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "goodToGo(): Animation finished already," 102 + " canceled=" + mCanceled 103 + " mPendingAnimations=" + mPendingAnimations.size()); 104 onAnimationFinished(); 105 return; 106 } 107 108 // Scale the timeout with the animator scale the controlling app is using. 109 mHandler.postDelayed(mTimeoutRunnable, 110 (long) (TIMEOUT_MS * mService.getCurrentAnimatorScale())); 111 mFinishedCallback = new FinishedCallback(this); 112 113 final RemoteAnimationTarget[] animations = createAnimations(); 114 if (animations.length == 0) { 115 if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "goodToGo(): No apps to animate"); 116 onAnimationFinished(); 117 return; 118 } 119 mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> { 120 try { 121 linkToDeathOfRunner(); 122 mRemoteAnimationAdapter.getRunner().onAnimationStart(animations, mFinishedCallback); 123 } catch (RemoteException e) { 124 Slog.e(TAG, "Failed to start remote animation", e); 125 onAnimationFinished(); 126 } 127 if (DEBUG_REMOTE_ANIMATIONS) { 128 Slog.d(TAG, "startAnimation(): Notify animation start:"); 129 writeStartDebugStatement(); 130 } 131 }); 132 setRunningRemoteAnimation(true); 133 } 134 cancelAnimation(String reason)135 void cancelAnimation(String reason) { 136 if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "cancelAnimation(): reason=" + reason); 137 synchronized (mService.getWindowManagerLock()) { 138 if (mCanceled) { 139 return; 140 } 141 mCanceled = true; 142 } 143 onAnimationFinished(); 144 invokeAnimationCancelled(); 145 } 146 writeStartDebugStatement()147 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).mAdapter.dump(pw, ""); 153 } 154 pw.close(); 155 Slog.i(TAG, sw.toString()); 156 } 157 createAnimations()158 private RemoteAnimationTarget[] createAnimations() { 159 if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "createAnimations()"); 160 final ArrayList<RemoteAnimationTarget> targets = new ArrayList<>(); 161 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { 162 final RemoteAnimationRecord wrappers = mPendingAnimations.get(i); 163 final RemoteAnimationTarget target = wrappers.createRemoteAnimationTarget(); 164 if (target != null) { 165 if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "\tAdd token=" + wrappers.mAppWindowToken); 166 targets.add(target); 167 } else { 168 if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "\tRemove token=" 169 + wrappers.mAppWindowToken); 170 171 // We can't really start an animation but we still need to make sure to finish the 172 // pending animation that was started by SurfaceAnimator 173 if (wrappers.mAdapter != null 174 && wrappers.mAdapter.mCapturedFinishCallback != null) { 175 wrappers.mAdapter.mCapturedFinishCallback 176 .onAnimationFinished(wrappers.mAdapter); 177 } 178 if (wrappers.mThumbnailAdapter != null 179 && wrappers.mThumbnailAdapter.mCapturedFinishCallback != null) { 180 wrappers.mThumbnailAdapter.mCapturedFinishCallback 181 .onAnimationFinished(wrappers.mThumbnailAdapter); 182 } 183 mPendingAnimations.remove(i); 184 } 185 } 186 return targets.toArray(new RemoteAnimationTarget[targets.size()]); 187 } 188 onAnimationFinished()189 private void onAnimationFinished() { 190 if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "onAnimationFinished(): mPendingAnimations=" 191 + mPendingAnimations.size()); 192 mHandler.removeCallbacks(mTimeoutRunnable); 193 synchronized (mService.mGlobalLock) { 194 unlinkToDeathOfRunner(); 195 releaseFinishedCallback(); 196 mService.openSurfaceTransaction(); 197 try { 198 if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, 199 "onAnimationFinished(): Notify animation finished:"); 200 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { 201 final RemoteAnimationRecord adapters = mPendingAnimations.get(i); 202 if (adapters.mAdapter != null) { 203 adapters.mAdapter.mCapturedFinishCallback 204 .onAnimationFinished(adapters.mAdapter); 205 } 206 if (adapters.mThumbnailAdapter != null) { 207 adapters.mThumbnailAdapter.mCapturedFinishCallback 208 .onAnimationFinished(adapters.mThumbnailAdapter); 209 } 210 if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "\t" + adapters.mAppWindowToken); 211 } 212 } catch (Exception e) { 213 Slog.e(TAG, "Failed to finish remote animation", e); 214 throw e; 215 } finally { 216 mService.closeSurfaceTransaction("RemoteAnimationController#finished"); 217 } 218 } 219 setRunningRemoteAnimation(false); 220 if (DEBUG_REMOTE_ANIMATIONS) Slog.i(TAG, "Finishing remote animation"); 221 } 222 invokeAnimationCancelled()223 private void invokeAnimationCancelled() { 224 try { 225 mRemoteAnimationAdapter.getRunner().onAnimationCancelled(); 226 } catch (RemoteException e) { 227 Slog.e(TAG, "Failed to notify cancel", e); 228 } 229 } 230 releaseFinishedCallback()231 private void releaseFinishedCallback() { 232 if (mFinishedCallback != null) { 233 mFinishedCallback.release(); 234 mFinishedCallback = null; 235 } 236 } 237 setRunningRemoteAnimation(boolean running)238 private void setRunningRemoteAnimation(boolean running) { 239 final int pid = mRemoteAnimationAdapter.getCallingPid(); 240 final int uid = mRemoteAnimationAdapter.getCallingUid(); 241 if (pid == 0) { 242 throw new RuntimeException("Calling pid of remote animation was null"); 243 } 244 final WindowProcessController wpc = mService.mAtmService.getProcessController(pid, uid); 245 if (wpc == null) { 246 Slog.w(TAG, "Unable to find process with pid=" + pid + " uid=" + uid); 247 return; 248 } 249 wpc.setRunningRemoteAnimation(running); 250 } 251 linkToDeathOfRunner()252 private void linkToDeathOfRunner() throws RemoteException { 253 if (!mLinkedToDeathOfRunner) { 254 mRemoteAnimationAdapter.getRunner().asBinder().linkToDeath(this, 0); 255 mLinkedToDeathOfRunner = true; 256 } 257 } 258 unlinkToDeathOfRunner()259 private void unlinkToDeathOfRunner() { 260 if (mLinkedToDeathOfRunner) { 261 mRemoteAnimationAdapter.getRunner().asBinder().unlinkToDeath(this, 0); 262 mLinkedToDeathOfRunner = false; 263 } 264 } 265 266 @Override binderDied()267 public void binderDied() { 268 cancelAnimation("binderDied"); 269 } 270 271 private static final class FinishedCallback extends IRemoteAnimationFinishedCallback.Stub { 272 273 RemoteAnimationController mOuter; 274 FinishedCallback(RemoteAnimationController outer)275 FinishedCallback(RemoteAnimationController outer) { 276 mOuter = outer; 277 } 278 279 @Override onAnimationFinished()280 public void onAnimationFinished() throws RemoteException { 281 if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "app-onAnimationFinished(): mOuter=" + mOuter); 282 final long token = Binder.clearCallingIdentity(); 283 try { 284 if (mOuter != null) { 285 mOuter.onAnimationFinished(); 286 287 // In case the client holds on to the finish callback, make sure we don't leak 288 // RemoteAnimationController which in turn would leak the runner on the client. 289 mOuter = null; 290 } 291 } finally { 292 Binder.restoreCallingIdentity(token); 293 } 294 } 295 296 /** 297 * Marks this callback as not be used anymore by releasing the reference to the outer class 298 * to prevent memory leak. 299 */ release()300 void release() { 301 if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "app-release(): mOuter=" + mOuter); 302 mOuter = null; 303 } 304 }; 305 306 /** 307 * Contains information about a remote-animation for one AppWindowToken. This keeps track of, 308 * potentially, multiple animating surfaces (AdapterWrappers) associated with one 309 * Window/Transition. For example, a change transition has an adapter controller for the 310 * main window and an adapter controlling the start-state snapshot. 311 * <p> 312 * This can be thought of as a bridge between the information that the remote animator sees (via 313 * {@link RemoteAnimationTarget}) and what the server sees (the 314 * {@link RemoteAnimationAdapterWrapper}(s) interfacing with the moving surfaces). 315 */ 316 public class RemoteAnimationRecord { 317 RemoteAnimationAdapterWrapper mAdapter; 318 RemoteAnimationAdapterWrapper mThumbnailAdapter = null; 319 RemoteAnimationTarget mTarget; 320 final AppWindowToken mAppWindowToken; 321 final Rect mStartBounds; 322 RemoteAnimationRecord(AppWindowToken appWindowToken, Point endPos, Rect endBounds, Rect startBounds)323 RemoteAnimationRecord(AppWindowToken appWindowToken, Point endPos, Rect endBounds, 324 Rect startBounds) { 325 mAppWindowToken = appWindowToken; 326 mAdapter = new RemoteAnimationAdapterWrapper(this, endPos, endBounds); 327 if (startBounds != null) { 328 mStartBounds = new Rect(startBounds); 329 mTmpRect.set(startBounds); 330 mTmpRect.offsetTo(0, 0); 331 if (mRemoteAnimationAdapter.getChangeNeedsSnapshot()) { 332 mThumbnailAdapter = 333 new RemoteAnimationAdapterWrapper(this, new Point(0, 0), mTmpRect); 334 } 335 } else { 336 mStartBounds = null; 337 } 338 } 339 createRemoteAnimationTarget()340 RemoteAnimationTarget createRemoteAnimationTarget() { 341 final Task task = mAppWindowToken.getTask(); 342 final WindowState mainWindow = mAppWindowToken.findMainWindow(); 343 if (task == null || mainWindow == null || mAdapter == null 344 || mAdapter.mCapturedFinishCallback == null 345 || mAdapter.mCapturedLeash == null) { 346 return null; 347 } 348 final Rect insets = new Rect(); 349 mainWindow.getContentInsets(insets); 350 InsetUtils.addInsets(insets, mAppWindowToken.getLetterboxInsets()); 351 mTarget = new RemoteAnimationTarget(task.mTaskId, getMode(), 352 mAdapter.mCapturedLeash, !mAppWindowToken.fillsParent(), 353 mainWindow.mWinAnimator.mLastClipRect, insets, 354 mAppWindowToken.getPrefixOrderIndex(), mAdapter.mPosition, 355 mAdapter.mStackBounds, task.getWindowConfiguration(), false /*isNotInRecents*/, 356 mThumbnailAdapter != null ? mThumbnailAdapter.mCapturedLeash : null, 357 mStartBounds); 358 return mTarget; 359 } 360 getMode()361 private int getMode() { 362 final DisplayContent dc = mAppWindowToken.getDisplayContent(); 363 if (dc.mOpeningApps.contains(mAppWindowToken)) { 364 return RemoteAnimationTarget.MODE_OPENING; 365 } else if (dc.mChangingApps.contains(mAppWindowToken)) { 366 return RemoteAnimationTarget.MODE_CHANGING; 367 } else { 368 return RemoteAnimationTarget.MODE_CLOSING; 369 } 370 } 371 } 372 373 private class RemoteAnimationAdapterWrapper implements AnimationAdapter { 374 private final RemoteAnimationRecord mRecord; 375 SurfaceControl mCapturedLeash; 376 private OnAnimationFinishedCallback mCapturedFinishCallback; 377 private final Point mPosition = new Point(); 378 private final Rect mStackBounds = new Rect(); 379 RemoteAnimationAdapterWrapper(RemoteAnimationRecord record, Point position, Rect stackBounds)380 RemoteAnimationAdapterWrapper(RemoteAnimationRecord record, Point position, 381 Rect stackBounds) { 382 mRecord = record; 383 mPosition.set(position.x, position.y); 384 mStackBounds.set(stackBounds); 385 } 386 387 @Override getShowWallpaper()388 public boolean getShowWallpaper() { 389 return false; 390 } 391 392 @Override getBackgroundColor()393 public int getBackgroundColor() { 394 return 0; 395 } 396 397 @Override startAnimation(SurfaceControl animationLeash, Transaction t, OnAnimationFinishedCallback finishCallback)398 public void startAnimation(SurfaceControl animationLeash, Transaction t, 399 OnAnimationFinishedCallback finishCallback) { 400 if (DEBUG_REMOTE_ANIMATIONS) Slog.d(TAG, "startAnimation"); 401 402 // Restore z-layering, position and stack crop until client has a chance to modify it. 403 t.setLayer(animationLeash, mRecord.mAppWindowToken.getPrefixOrderIndex()); 404 t.setPosition(animationLeash, mPosition.x, mPosition.y); 405 mTmpRect.set(mStackBounds); 406 mTmpRect.offsetTo(0, 0); 407 t.setWindowCrop(animationLeash, mTmpRect); 408 mCapturedLeash = animationLeash; 409 mCapturedFinishCallback = finishCallback; 410 } 411 412 @Override onAnimationCancelled(SurfaceControl animationLeash)413 public void onAnimationCancelled(SurfaceControl animationLeash) { 414 if (mRecord.mAdapter == this) { 415 mRecord.mAdapter = null; 416 } else { 417 mRecord.mThumbnailAdapter = null; 418 } 419 if (mRecord.mAdapter == null && mRecord.mThumbnailAdapter == null) { 420 mPendingAnimations.remove(mRecord); 421 } 422 if (mPendingAnimations.isEmpty()) { 423 mHandler.removeCallbacks(mTimeoutRunnable); 424 releaseFinishedCallback(); 425 invokeAnimationCancelled(); 426 setRunningRemoteAnimation(false); 427 } 428 } 429 430 @Override getDurationHint()431 public long getDurationHint() { 432 return mRemoteAnimationAdapter.getDuration(); 433 } 434 435 @Override getStatusBarTransitionsStartTime()436 public long getStatusBarTransitionsStartTime() { 437 return SystemClock.uptimeMillis() 438 + mRemoteAnimationAdapter.getStatusBarTransitionDelay(); 439 } 440 441 @Override dump(PrintWriter pw, String prefix)442 public void dump(PrintWriter pw, String prefix) { 443 pw.print(prefix); pw.print("token="); pw.println(mRecord.mAppWindowToken); 444 if (mRecord.mTarget != null) { 445 pw.print(prefix); pw.println("Target:"); 446 mRecord.mTarget.dump(pw, prefix + " "); 447 } else { 448 pw.print(prefix); pw.println("Target: null"); 449 } 450 } 451 452 @Override writeToProto(ProtoOutputStream proto)453 public void writeToProto(ProtoOutputStream proto) { 454 final long token = proto.start(REMOTE); 455 if (mRecord.mTarget != null) { 456 mRecord.mTarget.writeToProto(proto, TARGET); 457 } 458 proto.end(token); 459 } 460 } 461 } 462