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