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