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 com.android.server.wm.SurfaceAnimatorProto.ANIMATION_ADAPTER; 20 import static com.android.server.wm.SurfaceAnimatorProto.ANIMATION_START_DELAYED; 21 import static com.android.server.wm.SurfaceAnimatorProto.LEASH; 22 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM; 23 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; 24 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 25 26 import android.annotation.NonNull; 27 import android.annotation.Nullable; 28 import android.util.Slog; 29 import android.util.proto.ProtoOutputStream; 30 import android.view.SurfaceControl; 31 import android.view.SurfaceControl.Transaction; 32 33 import com.android.internal.annotations.VisibleForTesting; 34 35 import java.io.PrintWriter; 36 37 /** 38 * A class that can run animations on objects that have a set of child surfaces. We do this by 39 * reparenting all child surfaces of an object onto a new surface, called the "Leash". The Leash 40 * gets attached in the surface hierarchy where the the children were attached to. We then hand off 41 * the Leash to the component handling the animation, which is specified by the 42 * {@link AnimationAdapter}. When the animation is done animating, our callback to finish the 43 * animation will be invoked, at which we reparent the children back to the original parent. 44 */ 45 class SurfaceAnimator { 46 47 private static final String TAG = TAG_WITH_CLASS_NAME ? "SurfaceAnimator" : TAG_WM; 48 private final WindowManagerService mService; 49 private AnimationAdapter mAnimation; 50 51 @VisibleForTesting 52 SurfaceControl mLeash; 53 @VisibleForTesting 54 final Animatable mAnimatable; 55 private final OnAnimationFinishedCallback mInnerAnimationFinishedCallback; 56 @VisibleForTesting 57 final Runnable mAnimationFinishedCallback; 58 private boolean mAnimationStartDelayed; 59 60 /** 61 * @param animatable The object to animate. 62 * @param animationFinishedCallback Callback to invoke when an animation has finished running. 63 */ SurfaceAnimator(Animatable animatable, @Nullable Runnable animationFinishedCallback, WindowManagerService service)64 SurfaceAnimator(Animatable animatable, @Nullable Runnable animationFinishedCallback, 65 WindowManagerService service) { 66 mAnimatable = animatable; 67 mService = service; 68 mAnimationFinishedCallback = animationFinishedCallback; 69 mInnerAnimationFinishedCallback = getFinishedCallback(animationFinishedCallback); 70 } 71 getFinishedCallback( @ullable Runnable animationFinishedCallback)72 private OnAnimationFinishedCallback getFinishedCallback( 73 @Nullable Runnable animationFinishedCallback) { 74 return anim -> { 75 synchronized (mService.mGlobalLock) { 76 final SurfaceAnimator target = mService.mAnimationTransferMap.remove(anim); 77 if (target != null) { 78 target.mInnerAnimationFinishedCallback.onAnimationFinished(anim); 79 return; 80 } 81 82 if (anim != mAnimation) { 83 return; 84 } 85 final Runnable resetAndInvokeFinish = () -> { 86 // We need to check again if the animation has been replaced with a new 87 // animation because the animatable may defer to finish. 88 if (anim != mAnimation) { 89 return; 90 } 91 reset(mAnimatable.getPendingTransaction(), true /* destroyLeash */); 92 if (animationFinishedCallback != null) { 93 animationFinishedCallback.run(); 94 } 95 }; 96 if (!mAnimatable.shouldDeferAnimationFinish(resetAndInvokeFinish)) { 97 resetAndInvokeFinish.run(); 98 } 99 } 100 }; 101 } 102 103 /** 104 * Starts an animation. 105 * 106 * @param anim The object that bridges the controller, {@link SurfaceAnimator}, with the 107 * component responsible for running the animation. It runs the animation with 108 * {@link AnimationAdapter#startAnimation} once the hierarchy with 109 * the Leash has been set up. 110 * @param hidden Whether the container holding the child surfaces is currently visible or not. 111 * This is important as it will start with the leash hidden or visible before 112 * handing it to the component that is responsible to run the animation. 113 */ 114 void startAnimation(Transaction t, AnimationAdapter anim, boolean hidden) { 115 cancelAnimation(t, true /* restarting */, true /* forwardCancel */); 116 mAnimation = anim; 117 final SurfaceControl surface = mAnimatable.getSurfaceControl(); 118 if (surface == null) { 119 Slog.w(TAG, "Unable to start animation, surface is null or no children."); 120 cancelAnimation(); 121 return; 122 } 123 mLeash = createAnimationLeash(surface, t, 124 mAnimatable.getSurfaceWidth(), mAnimatable.getSurfaceHeight(), hidden); 125 mAnimatable.onAnimationLeashCreated(t, mLeash); 126 if (mAnimationStartDelayed) { 127 if (DEBUG_ANIM) Slog.i(TAG, "Animation start delayed"); 128 return; 129 } 130 mAnimation.startAnimation(mLeash, t, mInnerAnimationFinishedCallback); 131 } 132 133 /** 134 * Begins with delaying all animations to start. Any subsequent call to {@link #startAnimation} 135 * will not start the animation until {@link #endDelayingAnimationStart} is called. When an 136 * animation start is being delayed, the animator is considered animating already. 137 */ 138 void startDelayingAnimationStart() { 139 140 // We only allow delaying animation start we are not currently animating 141 if (!isAnimating()) { 142 mAnimationStartDelayed = true; 143 } 144 } 145 146 /** 147 * See {@link #startDelayingAnimationStart}. 148 */ 149 void endDelayingAnimationStart() { 150 final boolean delayed = mAnimationStartDelayed; 151 mAnimationStartDelayed = false; 152 if (delayed && mAnimation != null) { 153 mAnimation.startAnimation(mLeash, mAnimatable.getPendingTransaction(), 154 mInnerAnimationFinishedCallback); 155 mAnimatable.commitPendingTransaction(); 156 } 157 } 158 159 /** 160 * @return Whether we are currently running an animation, or we have a pending animation that 161 * is waiting to be started with {@link #endDelayingAnimationStart} 162 */ 163 boolean isAnimating() { 164 return mAnimation != null; 165 } 166 167 /** 168 * @return The current animation spec if we are running an animation, or {@code null} otherwise. 169 */ 170 AnimationAdapter getAnimation() { 171 return mAnimation; 172 } 173 174 /** 175 * Cancels any currently running animation. 176 */ 177 void cancelAnimation() { 178 cancelAnimation(mAnimatable.getPendingTransaction(), false /* restarting */, 179 true /* forwardCancel */); 180 mAnimatable.commitPendingTransaction(); 181 } 182 183 /** 184 * Sets the layer of the surface. 185 * <p> 186 * When the layer of the surface needs to be adjusted, we need to set it on the leash if the 187 * surface is reparented to the leash. This method takes care of that. 188 */ 189 void setLayer(Transaction t, int layer) { 190 t.setLayer(mLeash != null ? mLeash : mAnimatable.getSurfaceControl(), layer); 191 } 192 193 /** 194 * Sets the surface to be relatively layered. 195 * 196 * @see #setLayer 197 */ 198 void setRelativeLayer(Transaction t, SurfaceControl relativeTo, int layer) { 199 t.setRelativeLayer(mLeash != null ? mLeash : mAnimatable.getSurfaceControl(), relativeTo, layer); 200 } 201 202 /** 203 * Reparents the surface. 204 * 205 * @see #setLayer 206 */ 207 void reparent(Transaction t, SurfaceControl newParent) { 208 t.reparent(mLeash != null ? mLeash : mAnimatable.getSurfaceControl(), newParent); 209 } 210 211 /** 212 * @return True if the surface is attached to the leash; false otherwise. 213 */ 214 boolean hasLeash() { 215 return mLeash != null; 216 } 217 218 void transferAnimation(SurfaceAnimator from) { 219 if (from.mLeash == null) { 220 return; 221 } 222 final SurfaceControl surface = mAnimatable.getSurfaceControl(); 223 final SurfaceControl parent = mAnimatable.getAnimationLeashParent(); 224 if (surface == null || parent == null) { 225 Slog.w(TAG, "Unable to transfer animation, surface or parent is null"); 226 cancelAnimation(); 227 return; 228 } 229 endDelayingAnimationStart(); 230 final Transaction t = mAnimatable.getPendingTransaction(); 231 cancelAnimation(t, true /* restarting */, true /* forwardCancel */); 232 mLeash = from.mLeash; 233 mAnimation = from.mAnimation; 234 235 // Cancel source animation, but don't let animation runner cancel the animation. 236 from.cancelAnimation(t, false /* restarting */, false /* forwardCancel */); 237 t.reparent(surface, mLeash); 238 t.reparent(mLeash, parent); 239 mAnimatable.onAnimationLeashCreated(t, mLeash); 240 mService.mAnimationTransferMap.put(mAnimation, this); 241 } 242 243 boolean isAnimationStartDelayed() { 244 return mAnimationStartDelayed; 245 } 246 247 /** 248 * Cancels the animation, and resets the leash. 249 * 250 * @param t The transaction to use for all cancelling surface operations. 251 * @param restarting Whether we are restarting the animation. 252 * @param forwardCancel Whether to forward the cancel signal to the adapter executing the 253 * animation. This will be set to false when just transferring an animation 254 * to another animator. 255 */ 256 private void cancelAnimation(Transaction t, boolean restarting, boolean forwardCancel) { 257 if (DEBUG_ANIM) Slog.i(TAG, "Cancelling animation restarting=" + restarting); 258 final SurfaceControl leash = mLeash; 259 final AnimationAdapter animation = mAnimation; 260 reset(t, false); 261 if (animation != null) { 262 if (!mAnimationStartDelayed && forwardCancel) { 263 animation.onAnimationCancelled(leash); 264 } 265 if (!restarting) { 266 mAnimationFinishedCallback.run(); 267 } 268 } 269 270 if (forwardCancel && leash != null) { 271 t.remove(leash); 272 mService.scheduleAnimationLocked(); 273 } 274 275 if (!restarting) { 276 mAnimationStartDelayed = false; 277 } 278 } 279 280 private void reset(Transaction t, boolean destroyLeash) { 281 final SurfaceControl surface = mAnimatable.getSurfaceControl(); 282 final SurfaceControl parent = mAnimatable.getParentSurfaceControl(); 283 284 boolean scheduleAnim = false; 285 286 // If the surface was destroyed or the leash is invalid, we don't care to reparent it back. 287 // Note that we also set this variable to true even if the parent isn't valid anymore, in 288 // order to ensure onAnimationLeashLost still gets called in this case. 289 final boolean reparent = mLeash != null && surface != null; 290 if (reparent) { 291 if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to original parent: " + parent); 292 // We shouldn't really need these isValid checks but we do 293 // b/130364451 294 if (surface.isValid() && parent != null && parent.isValid()) { 295 t.reparent(surface, parent); 296 scheduleAnim = true; 297 } 298 } 299 mService.mAnimationTransferMap.remove(mAnimation); 300 if (mLeash != null && destroyLeash) { 301 t.remove(mLeash); 302 scheduleAnim = true; 303 } 304 mLeash = null; 305 mAnimation = null; 306 307 if (reparent) { 308 // Make sure to inform the animatable after the surface was reparented (or reparent 309 // wasn't possible, but we still need to invoke the callback) 310 mAnimatable.onAnimationLeashLost(t); 311 scheduleAnim = true; 312 } 313 314 if (scheduleAnim) { 315 mService.scheduleAnimationLocked(); 316 } 317 } 318 319 private SurfaceControl createAnimationLeash(SurfaceControl surface, Transaction t, int width, 320 int height, boolean hidden) { 321 if (DEBUG_ANIM) Slog.i(TAG, "Reparenting to leash"); 322 final SurfaceControl.Builder builder = mAnimatable.makeAnimationLeash() 323 .setParent(mAnimatable.getAnimationLeashParent()) 324 .setName(surface + " - animation-leash"); 325 final SurfaceControl leash = builder.build(); 326 t.setWindowCrop(leash, width, height); 327 if (!hidden) { 328 t.show(leash); 329 } 330 t.reparent(surface, leash); 331 return leash; 332 } 333 334 /** 335 * Write to a protocol buffer output stream. Protocol buffer message definition is at {@link 336 * com.android.server.wm.SurfaceAnimatorProto}. 337 * 338 * @param proto Stream to write the SurfaceAnimator object to. 339 * @param fieldId Field Id of the SurfaceAnimator as defined in the parent message. 340 * @hide 341 */ 342 void writeToProto(ProtoOutputStream proto, long fieldId) { 343 final long token = proto.start(fieldId); 344 if (mAnimation != null) { 345 mAnimation.writeToProto(proto, ANIMATION_ADAPTER); 346 } 347 if (mLeash != null) { 348 mLeash.writeToProto(proto, LEASH); 349 } 350 proto.write(ANIMATION_START_DELAYED, mAnimationStartDelayed); 351 proto.end(token); 352 } 353 354 void dump(PrintWriter pw, String prefix) { 355 pw.print(prefix); pw.print("mLeash="); pw.print(mLeash); 356 if (mAnimationStartDelayed) { 357 pw.print(" mAnimationStartDelayed="); pw.println(mAnimationStartDelayed); 358 } else { 359 pw.println(); 360 } 361 pw.print(prefix); pw.println("Animation:"); 362 if (mAnimation != null) { 363 mAnimation.dump(pw, prefix + " "); 364 } else { 365 pw.print(prefix); pw.println("null"); 366 } 367 } 368 369 /** 370 * Callback to be passed into {@link AnimationAdapter#startAnimation} to be invoked by the 371 * component that is running the animation when the animation is finished. 372 */ 373 interface OnAnimationFinishedCallback { 374 void onAnimationFinished(AnimationAdapter anim); 375 } 376 377 /** 378 * Interface to be animated by {@link SurfaceAnimator}. 379 */ 380 interface Animatable { 381 382 /** 383 * @return The pending transaction that will be committed in the next frame. 384 */ 385 @NonNull Transaction getPendingTransaction(); 386 387 /** 388 * Schedules a commit of the pending transaction. 389 */ 390 void commitPendingTransaction(); 391 392 /** 393 * Called when the was created. 394 * 395 * @param t The transaction to use to apply any necessary changes. 396 * @param leash The leash that was created. 397 */ 398 void onAnimationLeashCreated(Transaction t, SurfaceControl leash); 399 400 /** 401 * Called when the leash is being destroyed, or when the leash is being transferred to 402 * another SurfaceAnimator. 403 * 404 * @param t The transaction to use to apply any necessary changes. 405 */ 406 void onAnimationLeashLost(Transaction t); 407 408 /** 409 * @return A new surface to be used for the animation leash, inserted at the correct 410 * position in the hierarchy. 411 */ 412 SurfaceControl.Builder makeAnimationLeash(); 413 414 /** 415 * @return The parent that should be used for the animation leash. 416 */ 417 @Nullable SurfaceControl getAnimationLeashParent(); 418 419 /** 420 * @return The surface of the object to be animated. 421 * This SurfaceControl must be valid if non-null. 422 */ 423 @Nullable SurfaceControl getSurfaceControl(); 424 425 /** 426 * @return The parent of the surface object to be animated. 427 * This SurfaceControl must be valid if non-null. 428 */ 429 @Nullable SurfaceControl getParentSurfaceControl(); 430 431 /** 432 * @return The width of the surface to be animated. 433 */ 434 int getSurfaceWidth(); 435 436 /** 437 * @return The height of the surface to be animated. 438 */ 439 int getSurfaceHeight(); 440 441 /** 442 * Gets called when the animation is about to finish and gives the client the opportunity to 443 * defer finishing the animation, i.e. it keeps the leash around until the client calls 444 * {@link #cancelAnimation}. 445 * 446 * @param endDeferFinishCallback The callback to call when defer finishing should be ended. 447 * @return Whether the client would like to defer the animation finish. 448 */ 449 default boolean shouldDeferAnimationFinish(Runnable endDeferFinishCallback) { 450 return false; 451 } 452 } 453 } 454