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.annotation.NonNull; 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 import android.view.WindowManager; 41 42 import com.android.internal.annotations.VisibleForTesting; 43 import com.android.internal.protolog.ProtoLogImpl; 44 import com.android.internal.protolog.common.ProtoLog; 45 import com.android.internal.util.FastPrintWriter; 46 import com.android.server.wm.SurfaceAnimator.AnimationType; 47 import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback; 48 49 import java.io.PrintWriter; 50 import java.io.StringWriter; 51 import java.util.ArrayList; 52 53 /** 54 * Helper class to run app animations in a remote process. 55 */ 56 class RemoteAnimationController implements DeathRecipient { 57 private static final String TAG = TAG_WITH_CLASS_NAME 58 ? "RemoteAnimationController" : TAG_WM; 59 private static final long TIMEOUT_MS = 10000; 60 61 private final WindowManagerService mService; 62 private final DisplayContent mDisplayContent; 63 private final RemoteAnimationAdapter mRemoteAnimationAdapter; 64 private final ArrayList<RemoteAnimationRecord> mPendingAnimations = new ArrayList<>(); 65 private final ArrayList<WallpaperAnimationAdapter> mPendingWallpaperAnimations = 66 new ArrayList<>(); 67 @VisibleForTesting 68 final ArrayList<NonAppWindowAnimationAdapter> mPendingNonAppAnimations = new ArrayList<>(); 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 relative to its parent. 89 * @param localBounds The bounds of the app relative to its parent. 90 * @param endBounds The end bounds after the transition, in screen coordinates. 91 * @param startBounds The start 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 endBounds, Rect startBounds)94 RemoteAnimationRecord createRemoteAnimationRecord(WindowContainer windowContainer, 95 Point position, Rect localBounds, Rect endBounds, Rect startBounds) { 96 ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "createAnimationAdapter(): container=%s", 97 windowContainer); 98 final RemoteAnimationRecord adapters = new RemoteAnimationRecord(windowContainer, position, 99 localBounds, endBounds, 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.mThumbnailAdapter.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(mDisplayContent, 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.mThumbnailAdapter.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 private @RemoteAnimationTarget.Mode int mMode = RemoteAnimationTarget.MODE_CHANGING; 400 401 RemoteAnimationRecord(WindowContainer windowContainer, Point endPos, Rect localBounds, 402 Rect endBounds, Rect startBounds) { 403 mWindowContainer = windowContainer; 404 if (startBounds != null) { 405 mStartBounds = new Rect(startBounds); 406 mAdapter = new RemoteAnimationAdapterWrapper(this, endPos, localBounds, endBounds, 407 mStartBounds); 408 if (mRemoteAnimationAdapter.getChangeNeedsSnapshot()) { 409 final Rect thumbnailLocalBounds = new Rect(startBounds); 410 thumbnailLocalBounds.offsetTo(0, 0); 411 // Snapshot is located at (0,0) of the animation leash. It doesn't have size 412 // change, so the startBounds is its end bounds, and no start bounds for it. 413 mThumbnailAdapter = new RemoteAnimationAdapterWrapper(this, new Point(0, 0), 414 thumbnailLocalBounds, startBounds, new Rect()); 415 } 416 } else { 417 mAdapter = new RemoteAnimationAdapterWrapper(this, endPos, localBounds, endBounds, 418 new Rect()); 419 mStartBounds = null; 420 } 421 } 422 423 RemoteAnimationTarget createRemoteAnimationTarget() { 424 if (mAdapter == null 425 || mAdapter.mCapturedFinishCallback == null 426 || mAdapter.mCapturedLeash == null) { 427 return null; 428 } 429 mTarget = mWindowContainer.createRemoteAnimationTarget(this); 430 return mTarget; 431 } 432 433 void setMode(@RemoteAnimationTarget.Mode int mode) { 434 mMode = mode; 435 } 436 437 int getMode() { 438 return mMode; 439 } 440 441 /** Whether its parent is also an animation target in the same transition. */ 442 boolean hasAnimatingParent() { 443 // mOpeningApps and mClosingApps are only activities, so only need to check 444 // mChangingContainers. 445 for (int i = mDisplayContent.mChangingContainers.size() - 1; i >= 0; i--) { 446 if (mWindowContainer.isDescendantOf( 447 mDisplayContent.mChangingContainers.valueAt(i))) { 448 return true; 449 } 450 } 451 return false; 452 } 453 } 454 455 class RemoteAnimationAdapterWrapper implements AnimationAdapter { 456 private final RemoteAnimationRecord mRecord; 457 SurfaceControl mCapturedLeash; 458 private OnAnimationFinishedCallback mCapturedFinishCallback; 459 private @AnimationType int mAnimationType; 460 final Point mPosition = new Point(); 461 final Rect mLocalBounds; 462 final Rect mEndBounds = new Rect(); 463 final Rect mStartBounds = new Rect(); 464 465 RemoteAnimationAdapterWrapper(RemoteAnimationRecord record, Point position, 466 Rect localBounds, Rect endBounds, Rect startBounds) { 467 mRecord = record; 468 mPosition.set(position.x, position.y); 469 mLocalBounds = localBounds; 470 mEndBounds.set(endBounds); 471 mStartBounds.set(startBounds); 472 } 473 474 @Override 475 public boolean getShowWallpaper() { 476 return false; 477 } 478 479 @Override 480 public void startAnimation(SurfaceControl animationLeash, Transaction t, 481 @AnimationType int type, @NonNull OnAnimationFinishedCallback finishCallback) { 482 ProtoLog.d(WM_DEBUG_REMOTE_ANIMATIONS, "startAnimation"); 483 484 if (mStartBounds.isEmpty()) { 485 // Restore position and stack crop until client has a chance to modify it. 486 t.setPosition(animationLeash, mPosition.x, mPosition.y); 487 t.setWindowCrop(animationLeash, mEndBounds.width(), mEndBounds.height()); 488 } else { 489 // Offset the change animation leash to the relative start position in parent. 490 // (mPosition) is the relative end position in parent container. 491 // (mStartBounds - mEndBounds) is the position difference between start and end. 492 // (mPosition + mStartBounds - mEndBounds) will be the relative start position. 493 t.setPosition(animationLeash, mPosition.x + mStartBounds.left - mEndBounds.left, 494 mPosition.y + mStartBounds.top - mEndBounds.top); 495 t.setWindowCrop(animationLeash, mStartBounds.width(), mStartBounds.height()); 496 } 497 mCapturedLeash = animationLeash; 498 mCapturedFinishCallback = finishCallback; 499 mAnimationType = type; 500 } 501 502 @Override 503 public void onAnimationCancelled(SurfaceControl animationLeash) { 504 if (mRecord.mAdapter == this) { 505 mRecord.mAdapter = null; 506 } else { 507 mRecord.mThumbnailAdapter = null; 508 } 509 if (mRecord.mAdapter == null && mRecord.mThumbnailAdapter == null) { 510 mPendingAnimations.remove(mRecord); 511 } 512 if (mPendingAnimations.isEmpty()) { 513 cancelAnimation("allAppAnimationsCanceled"); 514 } 515 } 516 517 @Override 518 public long getDurationHint() { 519 return mRemoteAnimationAdapter.getDuration(); 520 } 521 522 @Override 523 public long getStatusBarTransitionsStartTime() { 524 return SystemClock.uptimeMillis() 525 + mRemoteAnimationAdapter.getStatusBarTransitionDelay(); 526 } 527 528 @Override 529 public void dump(PrintWriter pw, String prefix) { 530 pw.print(prefix); pw.print("container="); pw.println(mRecord.mWindowContainer); 531 if (mRecord.mTarget != null) { 532 pw.print(prefix); pw.println("Target:"); 533 mRecord.mTarget.dump(pw, prefix + " "); 534 } else { 535 pw.print(prefix); pw.println("Target: null"); 536 } 537 } 538 539 @Override 540 public void dumpDebug(ProtoOutputStream proto) { 541 final long token = proto.start(REMOTE); 542 if (mRecord.mTarget != null) { 543 mRecord.mTarget.dumpDebug(proto, TARGET); 544 } 545 proto.end(token); 546 } 547 } 548 } 549