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