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