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.internal.protolog.ProtoLogGroup.WM_DEBUG_REMOTE_ANIMATIONS; 20 import static com.android.server.wm.AnimationAdapterProto.REMOTE; 21 import static com.android.server.wm.RemoteAnimationAdapterWrapperProto.TARGET; 22 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; 23 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 24 25 import android.graphics.Point; 26 import android.graphics.Rect; 27 import android.os.Binder; 28 import android.os.Handler; 29 import android.os.IBinder.DeathRecipient; 30 import android.os.RemoteException; 31 import android.os.SystemClock; 32 import android.util.Slog; 33 import android.util.proto.ProtoOutputStream; 34 import android.view.IRemoteAnimationFinishedCallback; 35 import android.view.RemoteAnimationAdapter; 36 import android.view.RemoteAnimationTarget; 37 import android.view.SurfaceControl; 38 import android.view.SurfaceControl.Transaction; 39 import android.view.WindowManager; 40 41 import com.android.internal.annotations.VisibleForTesting; 42 import com.android.internal.protolog.ProtoLogImpl; 43 import com.android.internal.protolog.common.ProtoLog; 44 import com.android.internal.util.FastPrintWriter; 45 import com.android.server.wm.SurfaceAnimator.AnimationType; 46 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback; 47 48 import java.io.PrintWriter; 49 import java.io.StringWriter; 50 import java.util.ArrayList; 51 52 /** 53 * Helper class to run app animations in a remote process. 54 */ 55 class RemoteAnimationController implements DeathRecipient { 56 private static final String TAG = TAG_WITH_CLASS_NAME 57 ? "RemoteAnimationController" : TAG_WM; 58 private static final long TIMEOUT_MS = 10000; 59 60 private final WindowManagerService mService; 61 private final DisplayContent mDisplayContent; 62 private final RemoteAnimationAdapter mRemoteAnimationAdapter; 63 private final ArrayList<RemoteAnimationRecord> mPendingAnimations = new ArrayList<>(); 64 private final ArrayList<WallpaperAnimationAdapter> mPendingWallpaperAnimations = 65 new ArrayList<>(); 66 @VisibleForTesting 67 final ArrayList<NonAppWindowAnimationAdapter> mPendingNonAppAnimations = new ArrayList<>(); 68 private final Rect mTmpRect = new Rect(); 69 private final Handler mHandler; 70 private final Runnable mTimeoutRunnable = () -> cancelAnimation("timeoutRunnable"); 71 72 private FinishedCallback mFinishedCallback; 73 private boolean mCanceled; 74 private boolean mLinkedToDeathOfRunner; 75 RemoteAnimationController(WindowManagerService service, DisplayContent displayContent, RemoteAnimationAdapter remoteAnimationAdapter, Handler handler)76 RemoteAnimationController(WindowManagerService service, DisplayContent displayContent, 77 RemoteAnimationAdapter remoteAnimationAdapter, Handler handler) { 78 mService = service; 79 mDisplayContent = displayContent; 80 mRemoteAnimationAdapter = remoteAnimationAdapter; 81 mHandler = handler; 82 } 83 84 /** 85 * Creates an animation record for each individual {@link WindowContainer}. 86 * 87 * @param windowContainer The windows to animate. 88 * @param position The position app bounds, in screen coordinates. 89 * @param localBounds The bounds of the app relative to its parent. 90 * @param stackBounds The stack bounds of the app relative to position. 91 * @param startBounds The stack bounds before the transition, in screen coordinates 92 * @return The record representing animation(s) to run on the app. 93 */ createRemoteAnimationRecord(WindowContainer windowContainer, Point position, Rect localBounds, Rect stackBounds, Rect startBounds)94 RemoteAnimationRecord createRemoteAnimationRecord(WindowContainer windowContainer, 95 Point position, Rect localBounds, Rect stackBounds, Rect startBounds) { 96 ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "createAnimationAdapter(): container=%s", 97 windowContainer); 98 final RemoteAnimationRecord adapters = new RemoteAnimationRecord(windowContainer, position, 99 localBounds, stackBounds, startBounds); 100 mPendingAnimations.add(adapters); 101 return adapters; 102 } 103 104 /** 105 * Called when the transition is ready to be started, and all leashes have been set up. 106 */ goodToGo(@indowManager.TransitionOldType int transit)107 void goodToGo(@WindowManager.TransitionOldType int transit) { 108 ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "goodToGo()"); 109 if (mCanceled) { 110 ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, 111 "goodToGo(): Animation canceled already"); 112 onAnimationFinished(); 113 invokeAnimationCancelled(); 114 return; 115 } 116 117 // Scale the timeout with the animator scale the controlling app is using. 118 mHandler.postDelayed(mTimeoutRunnable, 119 (long) (TIMEOUT_MS * mService.getCurrentAnimatorScale())); 120 mFinishedCallback = new FinishedCallback(this); 121 122 // Create the app targets 123 final RemoteAnimationTarget[] appTargets = createAppAnimations(); 124 if (appTargets.length == 0 && !AppTransition.isKeyguardOccludeTransitOld(transit)) { 125 // Keyguard occlude transition can be executed before the occluding activity becomes 126 // visible. Even in this case, KeyguardService expects to receive binder call, so we 127 // don't cancel remote animation. 128 ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, 129 "goodToGo(): No apps to animate, mPendingAnimations=%d", 130 mPendingAnimations.size()); 131 onAnimationFinished(); 132 invokeAnimationCancelled(); 133 return; 134 } 135 136 // Create the remote wallpaper animation targets (if any) 137 final RemoteAnimationTarget[] wallpaperTargets = createWallpaperAnimations(); 138 139 // Create the remote non app animation targets (if any) 140 final RemoteAnimationTarget[] nonAppTargets = createNonAppWindowAnimations(transit); 141 142 mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> { 143 try { 144 linkToDeathOfRunner(); 145 ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "goodToGo(): onAnimationStart," 146 + " transit=%s, apps=%d, wallpapers=%d, nonApps=%d", 147 AppTransition.appTransitionOldToString(transit), appTargets.length, 148 wallpaperTargets.length, nonAppTargets.length); 149 mRemoteAnimationAdapter.getRunner().onAnimationStart(transit, appTargets, 150 wallpaperTargets, nonAppTargets, mFinishedCallback); 151 } catch (RemoteException e) { 152 Slog.e(TAG, "Failed to start remote animation", e); 153 onAnimationFinished(); 154 } 155 if (ProtoLogImpl.isEnabled(WM_DEBUG_REMOTE_ANIMATIONS)) { 156 ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "startAnimation(): Notify animation start:"); 157 writeStartDebugStatement(); 158 } 159 }); 160 setRunningRemoteAnimation(true); 161 } 162 cancelAnimation(String reason)163 void cancelAnimation(String reason) { 164 ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "cancelAnimation(): reason=%s", reason); 165 synchronized (mService.getWindowManagerLock()) { 166 if (mCanceled) { 167 return; 168 } 169 mCanceled = true; 170 } 171 onAnimationFinished(); 172 invokeAnimationCancelled(); 173 } 174 writeStartDebugStatement()175 private void writeStartDebugStatement() { 176 ProtoLog.i(WM_DEBUG_REMOTE_ANIMATIONS, "Starting remote animation"); 177 final StringWriter sw = new StringWriter(); 178 final FastPrintWriter pw = new FastPrintWriter(sw); 179 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { 180 mPendingAnimations.get(i).mAdapter.dump(pw, ""); 181 } 182 pw.close(); 183 ProtoLog.i(WM_DEBUG_REMOTE_ANIMATIONS, "%s", sw.toString()); 184 } 185 createAppAnimations()186 private RemoteAnimationTarget[] createAppAnimations() { 187 ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "createAppAnimations()"); 188 final ArrayList<RemoteAnimationTarget> targets = new ArrayList<>(); 189 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { 190 final RemoteAnimationRecord wrappers = mPendingAnimations.get(i); 191 final RemoteAnimationTarget target = wrappers.createRemoteAnimationTarget(); 192 if (target != null) { 193 ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "\tAdd container=%s", 194 wrappers.mWindowContainer); 195 targets.add(target); 196 } else { 197 ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "\tRemove container=%s", 198 wrappers.mWindowContainer); 199 200 // We can't really start an animation but we still need to make sure to finish the 201 // pending animation that was started by SurfaceAnimator 202 if (wrappers.mAdapter != null 203 && wrappers.mAdapter.mCapturedFinishCallback != null) { 204 wrappers.mAdapter.mCapturedFinishCallback 205 .onAnimationFinished(wrappers.mAdapter.mAnimationType, 206 wrappers.mAdapter); 207 } 208 if (wrappers.mThumbnailAdapter != null 209 && wrappers.mThumbnailAdapter.mCapturedFinishCallback != null) { 210 wrappers.mThumbnailAdapter.mCapturedFinishCallback 211 .onAnimationFinished(wrappers.mAdapter.mAnimationType, 212 wrappers.mThumbnailAdapter); 213 } 214 mPendingAnimations.remove(i); 215 } 216 } 217 return targets.toArray(new RemoteAnimationTarget[targets.size()]); 218 } 219 createWallpaperAnimations()220 private RemoteAnimationTarget[] createWallpaperAnimations() { 221 ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "createWallpaperAnimations()"); 222 return WallpaperAnimationAdapter.startWallpaperAnimations(mService, 223 mRemoteAnimationAdapter.getDuration(), 224 mRemoteAnimationAdapter.getStatusBarTransitionDelay(), 225 adapter -> { 226 synchronized (mService.mGlobalLock) { 227 // If the wallpaper animation is canceled, continue with the app animation 228 mPendingWallpaperAnimations.remove(adapter); 229 } 230 }, mPendingWallpaperAnimations); 231 } 232 233 private RemoteAnimationTarget[] createNonAppWindowAnimations( 234 @WindowManager.TransitionOldType int transit) { 235 ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "createNonAppWindowAnimations()"); 236 return NonAppWindowAnimationAdapter.startNonAppWindowAnimations(mService, 237 mDisplayContent, 238 transit, 239 mRemoteAnimationAdapter.getDuration(), 240 mRemoteAnimationAdapter.getStatusBarTransitionDelay(), 241 mPendingNonAppAnimations); 242 } 243 244 private void onAnimationFinished() { 245 ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "onAnimationFinished(): mPendingAnimations=%d", 246 mPendingAnimations.size()); 247 mHandler.removeCallbacks(mTimeoutRunnable); 248 synchronized (mService.mGlobalLock) { 249 unlinkToDeathOfRunner(); 250 releaseFinishedCallback(); 251 mService.openSurfaceTransaction(); 252 try { 253 ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, 254 "onAnimationFinished(): Notify animation finished:"); 255 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { 256 final RemoteAnimationRecord adapters = mPendingAnimations.get(i); 257 if (adapters.mAdapter != null) { 258 adapters.mAdapter.mCapturedFinishCallback 259 .onAnimationFinished(adapters.mAdapter.mAnimationType, 260 adapters.mAdapter); 261 } 262 if (adapters.mThumbnailAdapter != null) { 263 adapters.mThumbnailAdapter.mCapturedFinishCallback 264 .onAnimationFinished(adapters.mAdapter.mAnimationType, 265 adapters.mThumbnailAdapter); 266 } 267 mPendingAnimations.remove(i); 268 ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "\tcontainer=%s", 269 adapters.mWindowContainer); 270 } 271 272 for (int i = mPendingWallpaperAnimations.size() - 1; i >= 0; i--) { 273 final WallpaperAnimationAdapter adapter = mPendingWallpaperAnimations.get(i); 274 adapter.getLeashFinishedCallback().onAnimationFinished( 275 adapter.getLastAnimationType(), adapter); 276 mPendingWallpaperAnimations.remove(i); 277 ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "\twallpaper=%s", adapter.getToken()); 278 } 279 280 for (int i = mPendingNonAppAnimations.size() - 1; i >= 0; i--) { 281 final NonAppWindowAnimationAdapter adapter = mPendingNonAppAnimations.get(i); 282 adapter.getLeashFinishedCallback().onAnimationFinished( 283 adapter.getLastAnimationType(), adapter); 284 mPendingNonAppAnimations.remove(i); 285 ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "\tnonApp=%s", 286 adapter.getWindowContainer()); 287 } 288 } catch (Exception e) { 289 Slog.e(TAG, "Failed to finish remote animation", e); 290 throw e; 291 } finally { 292 mService.closeSurfaceTransaction("RemoteAnimationController#finished"); 293 } 294 } 295 setRunningRemoteAnimation(false); 296 ProtoLog.i(WM_DEBUG_REMOTE_ANIMATIONS, "Finishing remote animation"); 297 } 298 299 private void invokeAnimationCancelled() { 300 try { 301 mRemoteAnimationAdapter.getRunner().onAnimationCancelled(); 302 } catch (RemoteException e) { 303 Slog.e(TAG, "Failed to notify cancel", e); 304 } 305 } 306 307 private void releaseFinishedCallback() { 308 if (mFinishedCallback != null) { 309 mFinishedCallback.release(); 310 mFinishedCallback = null; 311 } 312 } 313 314 private void setRunningRemoteAnimation(boolean running) { 315 final int pid = mRemoteAnimationAdapter.getCallingPid(); 316 final int uid = mRemoteAnimationAdapter.getCallingUid(); 317 318 if (pid == 0) { 319 throw new RuntimeException("Calling pid of remote animation was null"); 320 } 321 final WindowProcessController wpc = mService.mAtmService.getProcessController(pid, uid); 322 if (wpc == null) { 323 Slog.w(TAG, "Unable to find process with pid=" + pid + " uid=" + uid); 324 return; 325 } 326 wpc.setRunningRemoteAnimation(running); 327 } 328 329 private void linkToDeathOfRunner() throws RemoteException { 330 if (!mLinkedToDeathOfRunner) { 331 mRemoteAnimationAdapter.getRunner().asBinder().linkToDeath(this, 0); 332 mLinkedToDeathOfRunner = true; 333 } 334 } 335 336 private void unlinkToDeathOfRunner() { 337 if (mLinkedToDeathOfRunner) { 338 mRemoteAnimationAdapter.getRunner().asBinder().unlinkToDeath(this, 0); 339 mLinkedToDeathOfRunner = false; 340 } 341 } 342 343 @Override 344 public void binderDied() { 345 cancelAnimation("binderDied"); 346 } 347 348 private static final class FinishedCallback extends IRemoteAnimationFinishedCallback.Stub { 349 350 RemoteAnimationController mOuter; 351 352 FinishedCallback(RemoteAnimationController outer) { 353 mOuter = outer; 354 } 355 356 @Override 357 public void onAnimationFinished() throws RemoteException { 358 ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "app-onAnimationFinished(): mOuter=%s", mOuter); 359 final long token = Binder.clearCallingIdentity(); 360 try { 361 if (mOuter != null) { 362 mOuter.onAnimationFinished(); 363 364 // In case the client holds on to the finish callback, make sure we don't leak 365 // RemoteAnimationController which in turn would leak the runner on the client. 366 mOuter = null; 367 } 368 } finally { 369 Binder.restoreCallingIdentity(token); 370 } 371 } 372 373 /** 374 * Marks this callback as not be used anymore by releasing the reference to the outer class 375 * to prevent memory leak. 376 */ 377 void release() { 378 ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "app-release(): mOuter=%s", mOuter); 379 mOuter = null; 380 } 381 }; 382 383 /** 384 * Contains information about a remote-animation for one WindowContainer. This keeps track of, 385 * potentially, multiple animating surfaces (AdapterWrappers) associated with one 386 * Window/Transition. For example, a change transition has an adapter controller for the 387 * main window and an adapter controlling the start-state snapshot. 388 * <p> 389 * This can be thought of as a bridge between the information that the remote animator sees (via 390 * {@link RemoteAnimationTarget}) and what the server sees (the 391 * {@link RemoteAnimationAdapterWrapper}(s) interfacing with the moving surfaces). 392 */ 393 public class RemoteAnimationRecord { 394 RemoteAnimationAdapterWrapper mAdapter; 395 RemoteAnimationAdapterWrapper mThumbnailAdapter = null; 396 RemoteAnimationTarget mTarget; 397 final WindowContainer mWindowContainer; 398 final Rect mStartBounds; 399 400 RemoteAnimationRecord(WindowContainer windowContainer, Point endPos, Rect localBounds, 401 Rect endBounds, Rect startBounds) { 402 mWindowContainer = windowContainer; 403 if (startBounds != null) { 404 mStartBounds = new Rect(startBounds); 405 mAdapter = new RemoteAnimationAdapterWrapper(this, endPos, localBounds, endBounds, 406 mStartBounds); 407 mTmpRect.set(startBounds); 408 mTmpRect.offsetTo(0, 0); 409 if (mRemoteAnimationAdapter.getChangeNeedsSnapshot()) { 410 mThumbnailAdapter = 411 new RemoteAnimationAdapterWrapper(this, new Point(0, 0), localBounds, 412 mTmpRect, new Rect()); 413 } 414 } else { 415 mAdapter = new RemoteAnimationAdapterWrapper(this, endPos, localBounds, endBounds, 416 new Rect(endPos.x, endPos.y, endBounds.right, endBounds.bottom)); 417 mStartBounds = null; 418 } 419 } 420 421 RemoteAnimationTarget createRemoteAnimationTarget() { 422 if (mAdapter == null 423 || mAdapter.mCapturedFinishCallback == null 424 || mAdapter.mCapturedLeash == null) { 425 return null; 426 } 427 mTarget = mWindowContainer.createRemoteAnimationTarget(this); 428 return mTarget; 429 } 430 431 int getMode() { 432 final DisplayContent dc = mWindowContainer.getDisplayContent(); 433 final ActivityRecord topActivity = mWindowContainer.getTopMostActivity(); 434 // Note that opening/closing transitions are per-activity while changing transitions 435 // are per-task. 436 if (dc.mOpeningApps.contains(topActivity)) { 437 return RemoteAnimationTarget.MODE_OPENING; 438 } else if (dc.mChangingContainers.contains(mWindowContainer)) { 439 return RemoteAnimationTarget.MODE_CHANGING; 440 } else { 441 return RemoteAnimationTarget.MODE_CLOSING; 442 } 443 } 444 } 445 446 class RemoteAnimationAdapterWrapper implements AnimationAdapter { 447 private final RemoteAnimationRecord mRecord; 448 SurfaceControl mCapturedLeash; 449 private OnAnimationFinishedCallback mCapturedFinishCallback; 450 private @AnimationType int mAnimationType; 451 final Point mPosition = new Point(); 452 final Rect mLocalBounds; 453 final Rect mRootTaskBounds = new Rect(); 454 final Rect mStartBounds = new Rect(); 455 456 RemoteAnimationAdapterWrapper(RemoteAnimationRecord record, Point position, 457 Rect localBounds, Rect rootTaskBounds, Rect startBounds) { 458 mRecord = record; 459 mPosition.set(position.x, position.y); 460 mLocalBounds = localBounds; 461 mRootTaskBounds.set(rootTaskBounds); 462 mStartBounds.set(startBounds); 463 } 464 465 @Override 466 public boolean getShowWallpaper() { 467 return false; 468 } 469 470 @Override 471 public void startAnimation(SurfaceControl animationLeash, Transaction t, 472 @AnimationType int type, OnAnimationFinishedCallback finishCallback) { 473 ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "startAnimation"); 474 475 // Restore position and stack crop until client has a chance to modify it. 476 if (mStartBounds.isEmpty()) { 477 t.setPosition(animationLeash, 0, 0); 478 t.setWindowCrop(animationLeash, -1, -1); 479 } else { 480 t.setPosition(animationLeash, mStartBounds.left, mStartBounds.top); 481 t.setWindowCrop(animationLeash, mStartBounds.width(), mStartBounds.height()); 482 } 483 mCapturedLeash = animationLeash; 484 mCapturedFinishCallback = finishCallback; 485 mAnimationType = type; 486 } 487 488 @Override 489 public void onAnimationCancelled(SurfaceControl animationLeash) { 490 if (mRecord.mAdapter == this) { 491 mRecord.mAdapter = null; 492 } else { 493 mRecord.mThumbnailAdapter = null; 494 } 495 if (mRecord.mAdapter == null && mRecord.mThumbnailAdapter == null) { 496 mPendingAnimations.remove(mRecord); 497 } 498 if (mPendingAnimations.isEmpty()) { 499 cancelAnimation("allAppAnimationsCanceled"); 500 } 501 } 502 503 @Override 504 public long getDurationHint() { 505 return mRemoteAnimationAdapter.getDuration(); 506 } 507 508 @Override 509 public long getStatusBarTransitionsStartTime() { 510 return SystemClock.uptimeMillis() 511 + mRemoteAnimationAdapter.getStatusBarTransitionDelay(); 512 } 513 514 @Override 515 public void dump(PrintWriter pw, String prefix) { 516 pw.print(prefix); pw.print("container="); pw.println(mRecord.mWindowContainer); 517 if (mRecord.mTarget != null) { 518 pw.print(prefix); pw.println("Target:"); 519 mRecord.mTarget.dump(pw, prefix + " "); 520 } else { 521 pw.print(prefix); pw.println("Target: null"); 522 } 523 } 524 525 @Override 526 public void dumpDebug(ProtoOutputStream proto) { 527 final long token = proto.start(REMOTE); 528 if (mRecord.mTarget != null) { 529 mRecord.mTarget.dumpDebug(proto, TARGET); 530 } 531 proto.end(token); 532 } 533 } 534 } 535