1 /* 2 * Copyright (C) 2017 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 android.os.Trace.TRACE_TAG_WINDOW_MANAGER; 20 import static android.util.TimeUtils.NANOS_PER_MS; 21 import static android.view.Choreographer.CALLBACK_TRAVERSAL; 22 import static android.view.Choreographer.getSfInstance; 23 24 import android.animation.AnimationHandler; 25 import android.animation.AnimationHandler.AnimationFrameCallbackProvider; 26 import android.animation.Animator; 27 import android.animation.AnimatorListenerAdapter; 28 import android.animation.ValueAnimator; 29 import android.annotation.Nullable; 30 import android.graphics.BitmapShader; 31 import android.graphics.Canvas; 32 import android.graphics.Insets; 33 import android.graphics.Paint; 34 import android.graphics.PixelFormat; 35 import android.graphics.Rect; 36 import android.hardware.power.Boost; 37 import android.os.Handler; 38 import android.os.PowerManagerInternal; 39 import android.os.Trace; 40 import android.util.ArrayMap; 41 import android.util.Log; 42 import android.view.Choreographer; 43 import android.view.Surface; 44 import android.view.SurfaceControl; 45 import android.view.SurfaceControl.Transaction; 46 import android.view.animation.Animation; 47 import android.view.animation.Transformation; 48 49 import com.android.internal.annotations.GuardedBy; 50 import com.android.internal.annotations.VisibleForTesting; 51 import com.android.internal.graphics.SfVsyncFrameCallbackProvider; 52 import com.android.server.AnimationThread; 53 import com.android.server.wm.LocalAnimationAdapter.AnimationSpec; 54 55 import java.util.ArrayList; 56 import java.util.concurrent.ExecutorService; 57 import java.util.concurrent.Executors; 58 import java.util.function.Supplier; 59 60 /** 61 * Class to run animations without holding the window manager lock. 62 */ 63 class SurfaceAnimationRunner { 64 65 private final Object mLock = new Object(); 66 67 /** 68 * Lock for cancelling animations. Must be acquired on it's own, or after acquiring 69 * {@link #mLock} 70 */ 71 private final Object mCancelLock = new Object(); 72 73 /** 74 * Lock for synchronizing {@link #mEdgeExtensions} to prevent race conditions when managing 75 * created edge extension surfaces. 76 */ 77 private final Object mEdgeExtensionLock = new Object(); 78 79 @VisibleForTesting 80 Choreographer mChoreographer; 81 82 private final Handler mAnimationThreadHandler = AnimationThread.getHandler(); 83 private final Handler mSurfaceAnimationHandler = SurfaceAnimationThread.getHandler(); 84 private final Runnable mApplyTransactionRunnable = this::applyTransaction; 85 private final AnimationHandler mAnimationHandler; 86 private final Transaction mFrameTransaction; 87 private final AnimatorFactory mAnimatorFactory; 88 private final PowerManagerInternal mPowerManagerInternal; 89 private boolean mApplyScheduled; 90 91 // Executor to perform the edge extension. 92 // With two threads because in practice we will want to extend two surfaces in one animation, 93 // in which case we want to be able to parallelize those two extensions to cut down latency in 94 // starting the animation. 95 private final ExecutorService mEdgeExtensionExecutor = Executors.newFixedThreadPool(2); 96 97 @GuardedBy("mLock") 98 @VisibleForTesting 99 final ArrayMap<SurfaceControl, RunningAnimation> mPendingAnimations = new ArrayMap<>(); 100 101 @GuardedBy("mLock") 102 @VisibleForTesting 103 final ArrayMap<SurfaceControl, RunningAnimation> mPreProcessingAnimations = new ArrayMap<>(); 104 105 @GuardedBy("mLock") 106 @VisibleForTesting 107 final ArrayMap<SurfaceControl, RunningAnimation> mRunningAnimations = new ArrayMap<>(); 108 109 @GuardedBy("mLock") 110 private boolean mAnimationStartDeferred; 111 112 // Mapping animation leashes to a list of edge extension surfaces associated with them 113 @GuardedBy("mEdgeExtensionLock") 114 private final ArrayMap<SurfaceControl, ArrayList<SurfaceControl>> mEdgeExtensions = 115 new ArrayMap<>(); 116 117 /** 118 * There should only ever be one instance of this class. Usual spot for it is with 119 * {@link WindowManagerService} 120 */ SurfaceAnimationRunner(Supplier<Transaction> transactionFactory, PowerManagerInternal powerManagerInternal)121 SurfaceAnimationRunner(Supplier<Transaction> transactionFactory, 122 PowerManagerInternal powerManagerInternal) { 123 this(null /* callbackProvider */, null /* animatorFactory */, 124 transactionFactory.get(), powerManagerInternal); 125 } 126 127 @VisibleForTesting SurfaceAnimationRunner(@ullable AnimationFrameCallbackProvider callbackProvider, AnimatorFactory animatorFactory, Transaction frameTransaction, PowerManagerInternal powerManagerInternal)128 SurfaceAnimationRunner(@Nullable AnimationFrameCallbackProvider callbackProvider, 129 AnimatorFactory animatorFactory, Transaction frameTransaction, 130 PowerManagerInternal powerManagerInternal) { 131 mSurfaceAnimationHandler.runWithScissors(() -> mChoreographer = getSfInstance(), 132 0 /* timeout */); 133 mFrameTransaction = frameTransaction; 134 mAnimationHandler = new AnimationHandler(); 135 mAnimationHandler.setProvider(callbackProvider != null 136 ? callbackProvider 137 : new SfVsyncFrameCallbackProvider(mChoreographer)); 138 mAnimatorFactory = animatorFactory != null 139 ? animatorFactory 140 : SfValueAnimator::new; 141 mPowerManagerInternal = powerManagerInternal; 142 } 143 144 /** 145 * Defers starting of animations until {@link #continueStartingAnimations} is called. This 146 * method is NOT nestable. 147 * 148 * @see #continueStartingAnimations 149 */ deferStartingAnimations()150 void deferStartingAnimations() { 151 synchronized (mLock) { 152 mAnimationStartDeferred = true; 153 } 154 } 155 156 /** 157 * Continues starting of animations. 158 * 159 * @see #deferStartingAnimations 160 */ continueStartingAnimations()161 void continueStartingAnimations() { 162 synchronized (mLock) { 163 mAnimationStartDeferred = false; 164 if (!mPendingAnimations.isEmpty() && mPreProcessingAnimations.isEmpty()) { 165 mChoreographer.postFrameCallback(this::startAnimations); 166 } 167 } 168 } 169 startAnimation(AnimationSpec a, SurfaceControl animationLeash, Transaction t, Runnable finishCallback)170 void startAnimation(AnimationSpec a, SurfaceControl animationLeash, Transaction t, 171 Runnable finishCallback) { 172 synchronized (mLock) { 173 final RunningAnimation runningAnim = new RunningAnimation(a, animationLeash, 174 finishCallback); 175 boolean requiresEdgeExtension = requiresEdgeExtension(a); 176 177 if (requiresEdgeExtension) { 178 final ArrayList<SurfaceControl> extensionSurfaces = new ArrayList<>(); 179 synchronized (mEdgeExtensionLock) { 180 mEdgeExtensions.put(animationLeash, extensionSurfaces); 181 } 182 183 mPreProcessingAnimations.put(animationLeash, runningAnim); 184 185 // We must wait for t to be committed since otherwise the leash doesn't have the 186 // windows we want to screenshot and extend as children. 187 t.addTransactionCommittedListener(mEdgeExtensionExecutor, () -> { 188 final WindowAnimationSpec animationSpec = a.asWindowAnimationSpec(); 189 190 final Transaction edgeExtensionCreationTransaction = new Transaction(); 191 edgeExtendWindow(animationLeash, 192 animationSpec.getRootTaskBounds(), animationSpec.getAnimation(), 193 edgeExtensionCreationTransaction); 194 195 synchronized (mLock) { 196 // only run if animation is not yet canceled by this point 197 if (mPreProcessingAnimations.get(animationLeash) == runningAnim) { 198 // In the case the animation is cancelled, edge extensions are removed 199 // onAnimationLeashLost which is called before onAnimationCancelled. 200 // So we need to check if the edge extensions have already been removed 201 // or not, and if so we don't want to apply the transaction. 202 synchronized (mEdgeExtensionLock) { 203 if (!mEdgeExtensions.isEmpty()) { 204 edgeExtensionCreationTransaction.apply(); 205 } 206 } 207 208 mPreProcessingAnimations.remove(animationLeash); 209 mPendingAnimations.put(animationLeash, runningAnim); 210 if (!mAnimationStartDeferred && mPreProcessingAnimations.isEmpty()) { 211 mChoreographer.postFrameCallback(this::startAnimations); 212 } 213 } 214 } 215 }); 216 } 217 218 if (!requiresEdgeExtension) { 219 mPendingAnimations.put(animationLeash, runningAnim); 220 if (!mAnimationStartDeferred && mPreProcessingAnimations.isEmpty()) { 221 mChoreographer.postFrameCallback(this::startAnimations); 222 } 223 224 // Some animations (e.g. move animations) require the initial transform to be 225 // applied immediately. 226 applyTransformation(runningAnim, t, 0 /* currentPlayTime */); 227 } 228 } 229 } 230 requiresEdgeExtension(AnimationSpec a)231 private boolean requiresEdgeExtension(AnimationSpec a) { 232 return a.asWindowAnimationSpec() != null && a.asWindowAnimationSpec().hasExtension(); 233 } 234 onAnimationCancelled(SurfaceControl leash)235 void onAnimationCancelled(SurfaceControl leash) { 236 synchronized (mLock) { 237 if (mPendingAnimations.containsKey(leash)) { 238 mPendingAnimations.remove(leash); 239 return; 240 } 241 if (mPreProcessingAnimations.containsKey(leash)) { 242 mPreProcessingAnimations.remove(leash); 243 return; 244 } 245 final RunningAnimation anim = mRunningAnimations.get(leash); 246 if (anim != null) { 247 mRunningAnimations.remove(leash); 248 synchronized (mCancelLock) { 249 anim.mCancelled = true; 250 } 251 mSurfaceAnimationHandler.post(() -> { 252 anim.mAnim.cancel(); 253 applyTransaction(); 254 }); 255 } 256 } 257 } 258 259 @GuardedBy("mLock") startPendingAnimationsLocked()260 private void startPendingAnimationsLocked() { 261 for (int i = mPendingAnimations.size() - 1; i >= 0; i--) { 262 startAnimationLocked(mPendingAnimations.valueAt(i)); 263 } 264 mPendingAnimations.clear(); 265 } 266 267 @GuardedBy("mLock") startAnimationLocked(RunningAnimation a)268 private void startAnimationLocked(RunningAnimation a) { 269 final ValueAnimator anim = mAnimatorFactory.makeAnimator(); 270 271 // Animation length is already expected to be scaled. 272 anim.overrideDurationScale(1.0f); 273 anim.setDuration(a.mAnimSpec.getDuration()); 274 anim.addUpdateListener(animation -> { 275 synchronized (mCancelLock) { 276 if (!a.mCancelled) { 277 final long duration = anim.getDuration(); 278 long currentPlayTime = anim.getCurrentPlayTime(); 279 if (currentPlayTime > duration) { 280 currentPlayTime = duration; 281 } 282 applyTransformation(a, mFrameTransaction, currentPlayTime); 283 } 284 } 285 286 // Transaction will be applied in the commit phase. 287 scheduleApplyTransaction(); 288 }); 289 290 anim.addListener(new AnimatorListenerAdapter() { 291 @Override 292 public void onAnimationStart(Animator animation) { 293 synchronized (mCancelLock) { 294 if (!a.mCancelled) { 295 // TODO: change this back to use show instead of alpha when b/138459974 is 296 // fixed. 297 mFrameTransaction.setAlpha(a.mLeash, 1); 298 } 299 } 300 } 301 302 @Override 303 public void onAnimationEnd(Animator animation) { 304 synchronized (mLock) { 305 mRunningAnimations.remove(a.mLeash); 306 synchronized (mCancelLock) { 307 if (!a.mCancelled) { 308 309 // Post on other thread that we can push final state without jank. 310 mAnimationThreadHandler.post(a.mFinishCallback); 311 } 312 } 313 } 314 } 315 }); 316 a.mAnim = anim; 317 mRunningAnimations.put(a.mLeash, a); 318 319 anim.start(); 320 if (a.mAnimSpec.canSkipFirstFrame()) { 321 // If we can skip the first frame, we start one frame later. 322 anim.setCurrentPlayTime(mChoreographer.getFrameIntervalNanos() / NANOS_PER_MS); 323 } 324 325 // Immediately start the animation by manually applying an animation frame. Otherwise, the 326 // start time would only be set in the next frame, leading to a delay. 327 anim.doAnimationFrame(mChoreographer.getFrameTime()); 328 } 329 applyTransformation(RunningAnimation a, Transaction t, long currentPlayTime)330 private void applyTransformation(RunningAnimation a, Transaction t, long currentPlayTime) { 331 a.mAnimSpec.apply(t, a.mLeash, currentPlayTime); 332 } 333 startAnimations(long frameTimeNanos)334 private void startAnimations(long frameTimeNanos) { 335 synchronized (mLock) { 336 if (!mPreProcessingAnimations.isEmpty()) { 337 // We only want to start running animations once all mPreProcessingAnimations have 338 // been processed to ensure preprocessed animations start in sync. 339 // NOTE: This means we might delay running animations that require preprocessing if 340 // new animations that also require preprocessing are requested before the previous 341 // ones have finished (see b/227449117). 342 return; 343 } 344 startPendingAnimationsLocked(); 345 } 346 mPowerManagerInternal.setPowerBoost(Boost.INTERACTION, 0); 347 } 348 scheduleApplyTransaction()349 private void scheduleApplyTransaction() { 350 if (!mApplyScheduled) { 351 mChoreographer.postCallback(CALLBACK_TRAVERSAL, mApplyTransactionRunnable, 352 null /* token */); 353 mApplyScheduled = true; 354 } 355 } 356 applyTransaction()357 private void applyTransaction() { 358 mFrameTransaction.setAnimationTransaction(); 359 mFrameTransaction.setFrameTimelineVsync(mChoreographer.getVsyncId()); 360 mFrameTransaction.apply(); 361 mApplyScheduled = false; 362 } 363 edgeExtendWindow(SurfaceControl leash, Rect bounds, Animation a, Transaction transaction)364 private void edgeExtendWindow(SurfaceControl leash, Rect bounds, Animation a, 365 Transaction transaction) { 366 final Transformation transformationAtStart = new Transformation(); 367 a.getTransformationAt(0, transformationAtStart); 368 final Transformation transformationAtEnd = new Transformation(); 369 a.getTransformationAt(1, transformationAtEnd); 370 371 // We want to create an extension surface that is the maximal size and the animation will 372 // take care of cropping any part that overflows. 373 final Insets maxExtensionInsets = Insets.min( 374 transformationAtStart.getInsets(), transformationAtEnd.getInsets()); 375 376 final int targetSurfaceHeight = bounds.height(); 377 final int targetSurfaceWidth = bounds.width(); 378 379 if (maxExtensionInsets.left < 0) { 380 final Rect edgeBounds = new Rect(bounds.left, bounds.top, bounds.left + 1, 381 bounds.bottom); 382 final Rect extensionRect = new Rect(0, 0, 383 -maxExtensionInsets.left, targetSurfaceHeight); 384 final int xPos = bounds.left + maxExtensionInsets.left; 385 final int yPos = bounds.top; 386 createExtensionSurface(leash, edgeBounds, 387 extensionRect, xPos, yPos, "Left Edge Extension", transaction); 388 } 389 390 if (maxExtensionInsets.top < 0) { 391 final Rect edgeBounds = new Rect(bounds.left, bounds.top, targetSurfaceWidth, 392 bounds.top + 1); 393 final Rect extensionRect = new Rect(0, 0, 394 targetSurfaceWidth, -maxExtensionInsets.top); 395 final int xPos = bounds.left; 396 final int yPos = bounds.top + maxExtensionInsets.top; 397 createExtensionSurface(leash, edgeBounds, 398 extensionRect, xPos, yPos, "Top Edge Extension", transaction); 399 } 400 401 if (maxExtensionInsets.right < 0) { 402 final Rect edgeBounds = new Rect(bounds.right - 1, bounds.top, bounds.right, 403 bounds.bottom); 404 final Rect extensionRect = new Rect(0, 0, 405 -maxExtensionInsets.right, targetSurfaceHeight); 406 final int xPos = bounds.right; 407 final int yPos = bounds.top; 408 createExtensionSurface(leash, edgeBounds, 409 extensionRect, xPos, yPos, "Right Edge Extension", transaction); 410 } 411 412 if (maxExtensionInsets.bottom < 0) { 413 final Rect edgeBounds = new Rect(bounds.left, bounds.bottom - 1, 414 bounds.right, bounds.bottom); 415 final Rect extensionRect = new Rect(0, 0, 416 targetSurfaceWidth, -maxExtensionInsets.bottom); 417 final int xPos = bounds.left; 418 final int yPos = bounds.bottom; 419 createExtensionSurface(leash, edgeBounds, 420 extensionRect, xPos, yPos, "Bottom Edge Extension", transaction); 421 } 422 } 423 createExtensionSurface(SurfaceControl leash, Rect edgeBounds, Rect extensionRect, int xPos, int yPos, String layerName, Transaction startTransaction)424 private void createExtensionSurface(SurfaceControl leash, Rect edgeBounds, 425 Rect extensionRect, int xPos, int yPos, String layerName, 426 Transaction startTransaction) { 427 Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "createExtensionSurface"); 428 doCreateExtensionSurface(leash, edgeBounds, extensionRect, xPos, yPos, layerName, 429 startTransaction); 430 Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); 431 } 432 doCreateExtensionSurface(SurfaceControl leash, Rect edgeBounds, Rect extensionRect, int xPos, int yPos, String layerName, Transaction startTransaction)433 private void doCreateExtensionSurface(SurfaceControl leash, Rect edgeBounds, 434 Rect extensionRect, int xPos, int yPos, String layerName, 435 Transaction startTransaction) { 436 SurfaceControl.LayerCaptureArgs captureArgs = 437 new SurfaceControl.LayerCaptureArgs.Builder(leash /* surfaceToExtend */) 438 .setSourceCrop(edgeBounds) 439 .setFrameScale(1) 440 .setPixelFormat(PixelFormat.RGBA_8888) 441 .setChildrenOnly(true) 442 .setAllowProtected(true) 443 .setCaptureSecureLayers(true) 444 .build(); 445 final SurfaceControl.ScreenshotHardwareBuffer edgeBuffer = 446 SurfaceControl.captureLayers(captureArgs); 447 448 if (edgeBuffer == null) { 449 // The leash we are trying to screenshot may have been removed by this point, which is 450 // likely the reason for ending up with a null edgeBuffer, in which case we just want to 451 // return and do nothing. 452 Log.e("SurfaceAnimationRunner", "Failed to create edge extension - " 453 + "edge buffer is null"); 454 return; 455 } 456 457 final SurfaceControl edgeExtensionLayer = new SurfaceControl.Builder() 458 .setName(layerName) 459 .setHidden(true) 460 .setCallsite("DefaultTransitionHandler#startAnimation") 461 .setOpaque(true) 462 .setBufferSize(extensionRect.width(), extensionRect.height()) 463 .build(); 464 465 BitmapShader shader = new BitmapShader(edgeBuffer.asBitmap(), 466 android.graphics.Shader.TileMode.CLAMP, 467 android.graphics.Shader.TileMode.CLAMP); 468 final Paint paint = new Paint(); 469 paint.setShader(shader); 470 471 final Surface surface = new Surface(edgeExtensionLayer); 472 Canvas c = surface.lockHardwareCanvas(); 473 c.drawRect(extensionRect, paint); 474 surface.unlockCanvasAndPost(c); 475 surface.release(); 476 477 synchronized (mEdgeExtensionLock) { 478 if (!mEdgeExtensions.containsKey(leash)) { 479 // The animation leash has already been removed, so we don't want to attach the 480 // edgeExtension layer and should immediately remove it instead. 481 startTransaction.remove(edgeExtensionLayer); 482 return; 483 } 484 485 startTransaction.reparent(edgeExtensionLayer, leash); 486 startTransaction.setLayer(edgeExtensionLayer, Integer.MIN_VALUE); 487 startTransaction.setPosition(edgeExtensionLayer, xPos, yPos); 488 startTransaction.setVisibility(edgeExtensionLayer, true); 489 490 mEdgeExtensions.get(leash).add(edgeExtensionLayer); 491 } 492 } 493 getScaleXForExtensionSurface(Rect edgeBounds, Rect extensionRect)494 private float getScaleXForExtensionSurface(Rect edgeBounds, Rect extensionRect) { 495 if (edgeBounds.width() == extensionRect.width()) { 496 // Top or bottom edge extension, no need to scale the X axis of the extension surface. 497 return 1; 498 } 499 if (edgeBounds.width() == 1) { 500 // Left or right edge extension, scale the surface to be the extensionRect's width. 501 return extensionRect.width(); 502 } 503 504 throw new RuntimeException("Unexpected edgeBounds and extensionRect widths"); 505 } 506 getScaleYForExtensionSurface(Rect edgeBounds, Rect extensionRect)507 private float getScaleYForExtensionSurface(Rect edgeBounds, Rect extensionRect) { 508 if (edgeBounds.height() == extensionRect.height()) { 509 // Left or right edge extension, no need to scale the Y axis of the extension surface. 510 return 1; 511 } 512 if (edgeBounds.height() == 1) { 513 // Top or bottom edge extension, scale the surface to be the extensionRect's height. 514 return extensionRect.height(); 515 } 516 517 throw new RuntimeException("Unexpected edgeBounds and extensionRect heights"); 518 } 519 520 private static final class RunningAnimation { 521 final AnimationSpec mAnimSpec; 522 final SurfaceControl mLeash; 523 final Runnable mFinishCallback; 524 ValueAnimator mAnim; 525 526 @GuardedBy("mCancelLock") 527 private boolean mCancelled; 528 RunningAnimation(AnimationSpec animSpec, SurfaceControl leash, Runnable finishCallback)529 RunningAnimation(AnimationSpec animSpec, SurfaceControl leash, Runnable finishCallback) { 530 mAnimSpec = animSpec; 531 mLeash = leash; 532 mFinishCallback = finishCallback; 533 } 534 } 535 onAnimationLeashLost(SurfaceControl animationLeash, Transaction t)536 protected void onAnimationLeashLost(SurfaceControl animationLeash, 537 Transaction t) { 538 synchronized (mEdgeExtensionLock) { 539 if (!mEdgeExtensions.containsKey(animationLeash)) { 540 return; 541 } 542 543 final ArrayList<SurfaceControl> edgeExtensions = mEdgeExtensions.get(animationLeash); 544 for (int i = 0; i < edgeExtensions.size(); i++) { 545 final SurfaceControl extension = edgeExtensions.get(i); 546 t.remove(extension); 547 } 548 mEdgeExtensions.remove(animationLeash); 549 } 550 } 551 552 @VisibleForTesting 553 interface AnimatorFactory { makeAnimator()554 ValueAnimator makeAnimator(); 555 } 556 557 /** 558 * Value animator that uses sf-vsync signal to tick. 559 */ 560 private class SfValueAnimator extends ValueAnimator { 561 SfValueAnimator()562 SfValueAnimator() { 563 setFloatValues(0f, 1f); 564 } 565 566 @Override getAnimationHandler()567 public AnimationHandler getAnimationHandler() { 568 return mAnimationHandler; 569 } 570 } 571 }