1 /* 2 * Copyright (C) 2020 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.internal.protolog.ProtoLogGroup.WM_SHOW_TRANSACTIONS; 20 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_SCREEN_ROTATION; 21 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.graphics.GraphicBuffer; 25 import android.graphics.PixelFormat; 26 import android.graphics.Point; 27 import android.graphics.Rect; 28 import android.hardware.HardwareBuffer; 29 import android.util.Slog; 30 import android.view.SurfaceControl; 31 32 import com.android.internal.annotations.VisibleForTesting; 33 import com.android.internal.protolog.common.ProtoLog; 34 35 /** 36 * This class handles "freezing" of an Animatable. The Animatable in question should implement 37 * Freezable. 38 * 39 * The point of this is to enable WindowContainers to each be capable of freezing themselves. 40 * Freezing means taking a snapshot and placing it above everything in the sub-hierarchy. 41 * The "placing above" requires that a parent surface be inserted above the target surface so that 42 * the target surface and the snapshot are siblings. 43 * 44 * The overall flow for a transition using this would be: 45 * 1. Set transition and record animatable in mChangingApps 46 * 2. Call {@link #freeze} to set-up the leashes and cover with a snapshot. 47 * 3. When transition participants are ready, start SurfaceAnimator with this as a parameter 48 * 4. SurfaceAnimator will then {@link #takeLeashForAnimation} instead of creating another leash. 49 * 5. The animation system should eventually clean this up via {@link #unfreeze}. 50 */ 51 class SurfaceFreezer { 52 53 private static final String TAG = "SurfaceFreezer"; 54 55 private final @NonNull Freezable mAnimatable; 56 private final @NonNull WindowManagerService mWmService; 57 @VisibleForTesting 58 SurfaceControl mLeash; 59 Snapshot mSnapshot = null; 60 final Rect mFreezeBounds = new Rect(); 61 62 /** 63 * @param animatable The object to animate. 64 */ SurfaceFreezer(@onNull Freezable animatable, @NonNull WindowManagerService service)65 SurfaceFreezer(@NonNull Freezable animatable, @NonNull WindowManagerService service) { 66 mAnimatable = animatable; 67 mWmService = service; 68 } 69 70 /** 71 * Freeze the target surface. This is done by creating a leash (inserting a parent surface 72 * above the target surface) and then taking a snapshot and placing it over the target surface. 73 * 74 * @param startBounds The original bounds (on screen) of the surface we are snapshotting. 75 * @param relativePosition The related position of the snapshot surface to its parent. 76 * @param freezeTarget The surface to take snapshot from. If {@code null}, we will take a 77 * snapshot from the {@link #mAnimatable} surface. 78 */ freeze(SurfaceControl.Transaction t, Rect startBounds, Point relativePosition, @Nullable SurfaceControl freezeTarget)79 void freeze(SurfaceControl.Transaction t, Rect startBounds, Point relativePosition, 80 @Nullable SurfaceControl freezeTarget) { 81 reset(t); 82 mFreezeBounds.set(startBounds); 83 84 mLeash = SurfaceAnimator.createAnimationLeash(mAnimatable, mAnimatable.getSurfaceControl(), 85 t, ANIMATION_TYPE_SCREEN_ROTATION, startBounds.width(), startBounds.height(), 86 relativePosition.x, relativePosition.y, false /* hidden */, 87 mWmService.mTransactionFactory); 88 mAnimatable.onAnimationLeashCreated(t, mLeash); 89 90 freezeTarget = freezeTarget != null ? freezeTarget : mAnimatable.getFreezeSnapshotTarget(); 91 if (freezeTarget != null) { 92 SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer = createSnapshotBufferInner( 93 freezeTarget, startBounds); 94 final HardwareBuffer buffer = screenshotBuffer == null ? null 95 : screenshotBuffer.getHardwareBuffer(); 96 if (buffer == null || buffer.getWidth() <= 1 || buffer.getHeight() <= 1) { 97 // This can happen when display is not ready. 98 Slog.w(TAG, "Failed to capture screenshot for " + mAnimatable); 99 unfreeze(t); 100 return; 101 } 102 mSnapshot = new Snapshot(t, screenshotBuffer, mLeash); 103 } 104 } 105 106 /** 107 * Used by {@link SurfaceAnimator}. This "transfers" the leash to be used for animation. 108 * By transferring the leash, this will no longer try to clean-up the leash when finished. 109 */ takeLeashForAnimation()110 SurfaceControl takeLeashForAnimation() { 111 SurfaceControl out = mLeash; 112 mLeash = null; 113 return out; 114 } 115 116 /** 117 * Used by {@link SurfaceAnimator}. This "transfers" the snapshot leash to be used for 118 * animation. By transferring the leash, this will no longer try to clean-up the leash when 119 * finished. 120 */ 121 @Nullable takeSnapshotForAnimation()122 Snapshot takeSnapshotForAnimation() { 123 final Snapshot out = mSnapshot; 124 mSnapshot = null; 125 return out; 126 } 127 128 /** 129 * Clean-up the snapshot and remove leash. If the leash was taken, this just cleans-up the 130 * snapshot. 131 */ unfreeze(SurfaceControl.Transaction t)132 void unfreeze(SurfaceControl.Transaction t) { 133 unfreezeInner(t); 134 mAnimatable.onUnfrozen(); 135 } 136 unfreezeInner(SurfaceControl.Transaction t)137 private void unfreezeInner(SurfaceControl.Transaction t) { 138 if (mSnapshot != null) { 139 mSnapshot.cancelAnimation(t, false /* restarting */); 140 mSnapshot = null; 141 } 142 if (mLeash == null) { 143 return; 144 } 145 SurfaceControl leash = mLeash; 146 mLeash = null; 147 final boolean scheduleAnim = SurfaceAnimator.removeLeash(t, mAnimatable, leash, 148 true /* destroy */); 149 if (scheduleAnim) { 150 mWmService.scheduleAnimationLocked(); 151 } 152 } 153 154 /** Resets the snapshot before taking another one if the animation hasn't been started yet. */ reset(SurfaceControl.Transaction t)155 private void reset(SurfaceControl.Transaction t) { 156 // Those would have been taken by the SurfaceAnimator if the animation has been started, so 157 // we can remove the leash directly. 158 // No need to reset the mAnimatable leash, as this is called before a new animation leash is 159 // created, so another #onAnimationLeashCreated will be called. 160 if (mSnapshot != null) { 161 mSnapshot.destroy(t); 162 mSnapshot = null; 163 } 164 if (mLeash != null) { 165 t.remove(mLeash); 166 mLeash = null; 167 } 168 } 169 setLayer(SurfaceControl.Transaction t, int layer)170 void setLayer(SurfaceControl.Transaction t, int layer) { 171 if (mLeash != null) { 172 t.setLayer(mLeash, layer); 173 } 174 } 175 setRelativeLayer(SurfaceControl.Transaction t, SurfaceControl relativeTo, int layer)176 void setRelativeLayer(SurfaceControl.Transaction t, SurfaceControl relativeTo, int layer) { 177 if (mLeash != null) { 178 t.setRelativeLayer(mLeash, relativeTo, layer); 179 } 180 } 181 hasLeash()182 boolean hasLeash() { 183 return mLeash != null; 184 } 185 createSnapshotBuffer( @onNull SurfaceControl target, @Nullable Rect bounds)186 private static SurfaceControl.ScreenshotHardwareBuffer createSnapshotBuffer( 187 @NonNull SurfaceControl target, @Nullable Rect bounds) { 188 Rect cropBounds = null; 189 if (bounds != null) { 190 cropBounds = new Rect(bounds); 191 cropBounds.offsetTo(0, 0); 192 } 193 SurfaceControl.LayerCaptureArgs captureArgs = 194 new SurfaceControl.LayerCaptureArgs.Builder(target) 195 .setSourceCrop(cropBounds) 196 .setCaptureSecureLayers(true) 197 .setAllowProtected(true) 198 .build(); 199 return SurfaceControl.captureLayers(captureArgs); 200 } 201 202 @VisibleForTesting createSnapshotBufferInner( SurfaceControl target, Rect bounds)203 SurfaceControl.ScreenshotHardwareBuffer createSnapshotBufferInner( 204 SurfaceControl target, Rect bounds) { 205 return createSnapshotBuffer(target, bounds); 206 } 207 208 @VisibleForTesting createFromHardwareBufferInner( SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer)209 GraphicBuffer createFromHardwareBufferInner( 210 SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer) { 211 return GraphicBuffer.createFromHardwareBuffer(screenshotBuffer.getHardwareBuffer()); 212 } 213 214 class Snapshot { 215 private SurfaceControl mSurfaceControl; 216 private AnimationAdapter mAnimation; 217 218 /** 219 * @param t Transaction to create the thumbnail in. 220 * @param screenshotBuffer A thumbnail or placeholder for thumbnail to initialize with. 221 */ Snapshot(SurfaceControl.Transaction t, SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer, SurfaceControl parent)222 Snapshot(SurfaceControl.Transaction t, 223 SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer, SurfaceControl parent) { 224 GraphicBuffer graphicBuffer = createFromHardwareBufferInner(screenshotBuffer); 225 226 mSurfaceControl = mAnimatable.makeAnimationLeash() 227 .setName("snapshot anim: " + mAnimatable.toString()) 228 .setFormat(PixelFormat.TRANSLUCENT) 229 .setParent(parent) 230 .setSecure(screenshotBuffer.containsSecureLayers()) 231 .setCallsite("SurfaceFreezer.Snapshot") 232 .setBLASTLayer() 233 .build(); 234 235 ProtoLog.i(WM_SHOW_TRANSACTIONS, " THUMBNAIL %s: CREATE", mSurfaceControl); 236 237 t.setBuffer(mSurfaceControl, graphicBuffer); 238 t.setColorSpace(mSurfaceControl, screenshotBuffer.getColorSpace()); 239 t.show(mSurfaceControl); 240 241 // We parent the thumbnail to the container, and just place it on top of anything else 242 // in the container. 243 t.setLayer(mSurfaceControl, Integer.MAX_VALUE); 244 } 245 destroy(SurfaceControl.Transaction t)246 void destroy(SurfaceControl.Transaction t) { 247 if (mSurfaceControl == null) { 248 return; 249 } 250 t.remove(mSurfaceControl); 251 mSurfaceControl = null; 252 } 253 254 /** 255 * Starts an animation. 256 * 257 * @param anim The object that bridges the controller, {@link SurfaceAnimator}, with the 258 * component responsible for running the animation. It runs the animation with 259 * {@link AnimationAdapter#startAnimation} once the hierarchy with 260 * the Leash has been set up. 261 */ startAnimation(SurfaceControl.Transaction t, AnimationAdapter anim, int type)262 void startAnimation(SurfaceControl.Transaction t, AnimationAdapter anim, int type) { 263 cancelAnimation(t, true /* restarting */); 264 mAnimation = anim; 265 if (mSurfaceControl == null) { 266 cancelAnimation(t, false /* restarting */); 267 return; 268 } 269 mAnimation.startAnimation(mSurfaceControl, t, type, (typ, ani) -> { }); 270 } 271 272 /** 273 * Cancels the animation, and resets the leash. 274 * 275 * @param t The transaction to use for all cancelling surface operations. 276 * @param restarting Whether we are restarting the animation. 277 */ cancelAnimation(SurfaceControl.Transaction t, boolean restarting)278 void cancelAnimation(SurfaceControl.Transaction t, boolean restarting) { 279 final SurfaceControl leash = mSurfaceControl; 280 final AnimationAdapter animation = mAnimation; 281 mAnimation = null; 282 if (animation != null) { 283 animation.onAnimationCancelled(leash); 284 } 285 if (!restarting) { 286 destroy(t); 287 } 288 } 289 } 290 291 /** freezable */ 292 public interface Freezable extends SurfaceAnimator.Animatable { 293 /** 294 * @return The surface to take a snapshot of. If this returns {@code null}, no snapshot 295 * will be generated (but the rest of the freezing logic will still happen). 296 */ getFreezeSnapshotTarget()297 @Nullable SurfaceControl getFreezeSnapshotTarget(); 298 299 /** Called when the {@link #unfreeze(SurfaceControl.Transaction)} is called. */ onUnfrozen()300 void onUnfrozen(); 301 } 302 } 303