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_APP_TRANSITION; 21 import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_SCREEN_ROTATION; 22 23 import android.annotation.NonNull; 24 import android.annotation.Nullable; 25 import android.graphics.GraphicBuffer; 26 import android.graphics.PixelFormat; 27 import android.graphics.Rect; 28 import android.hardware.HardwareBuffer; 29 import android.view.Surface; 30 import android.view.SurfaceControl; 31 32 import com.android.internal.protolog.common.ProtoLog; 33 34 import java.util.function.Supplier; 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 final Freezable mAnimatable; 55 private final WindowManagerService mWmService; 56 private SurfaceControl mLeash; 57 Snapshot mSnapshot = null; 58 final Rect mFreezeBounds = new Rect(); 59 60 /** 61 * @param animatable The object to animate. 62 */ SurfaceFreezer(Freezable animatable, WindowManagerService service)63 SurfaceFreezer(Freezable animatable, WindowManagerService service) { 64 mAnimatable = animatable; 65 mWmService = service; 66 } 67 68 /** 69 * Freeze the target surface. This is done by creating a leash (inserting a parent surface 70 * above the target surface) and then taking a snapshot and placing it over the target surface. 71 * 72 * @param startBounds The original bounds (on screen) of the surface we are snapshotting. 73 */ freeze(SurfaceControl.Transaction t, Rect startBounds)74 void freeze(SurfaceControl.Transaction t, Rect startBounds) { 75 mFreezeBounds.set(startBounds); 76 77 mLeash = SurfaceAnimator.createAnimationLeash(mAnimatable, mAnimatable.getSurfaceControl(), 78 t, ANIMATION_TYPE_SCREEN_ROTATION, startBounds.width(), startBounds.height(), 79 startBounds.left, startBounds.top, false /* hidden */, 80 mWmService.mTransactionFactory); 81 mAnimatable.onAnimationLeashCreated(t, mLeash); 82 83 SurfaceControl freezeTarget = mAnimatable.getFreezeSnapshotTarget(); 84 if (freezeTarget != null) { 85 SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer = createSnapshotBuffer( 86 freezeTarget, startBounds); 87 final HardwareBuffer buffer = screenshotBuffer == null ? null 88 : screenshotBuffer.getHardwareBuffer(); 89 if (buffer == null || buffer.getWidth() <= 1 || buffer.getHeight() <= 1) { 90 return; 91 } 92 mSnapshot = new Snapshot(mWmService.mSurfaceFactory, t, screenshotBuffer, mLeash); 93 } 94 } 95 96 /** 97 * Used by {@link SurfaceAnimator}. This "transfers" the leash to be used for animation. 98 * By transferring the leash, this will no longer try to clean-up the leash when finished. 99 */ takeLeashForAnimation()100 SurfaceControl takeLeashForAnimation() { 101 SurfaceControl out = mLeash; 102 mLeash = null; 103 return out; 104 } 105 106 /** 107 * Clean-up the snapshot and remove leash. If the leash was taken, this just cleans-up the 108 * snapshot. 109 */ unfreeze(SurfaceControl.Transaction t)110 void unfreeze(SurfaceControl.Transaction t) { 111 if (mSnapshot != null) { 112 mSnapshot.cancelAnimation(t, false /* restarting */); 113 } 114 if (mLeash == null) { 115 return; 116 } 117 SurfaceControl leash = mLeash; 118 mLeash = null; 119 final boolean scheduleAnim = SurfaceAnimator.removeLeash(t, mAnimatable, leash, 120 false /* destroy */); 121 if (scheduleAnim) { 122 mWmService.scheduleAnimationLocked(); 123 } 124 } 125 hasLeash()126 boolean hasLeash() { 127 return mLeash != null; 128 } 129 createSnapshotBuffer( @onNull SurfaceControl target, @Nullable Rect bounds)130 private static SurfaceControl.ScreenshotHardwareBuffer createSnapshotBuffer( 131 @NonNull SurfaceControl target, @Nullable Rect bounds) { 132 Rect cropBounds = null; 133 if (bounds != null) { 134 cropBounds = new Rect(bounds); 135 cropBounds.offsetTo(0, 0); 136 } 137 SurfaceControl.LayerCaptureArgs captureArgs = 138 new SurfaceControl.LayerCaptureArgs.Builder(target) 139 .setSourceCrop(cropBounds) 140 .setCaptureSecureLayers(true) 141 .setAllowProtected(true) 142 .build(); 143 return SurfaceControl.captureLayers(captureArgs); 144 } 145 146 class Snapshot { 147 private SurfaceControl mSurfaceControl; 148 private AnimationAdapter mAnimation; 149 private SurfaceAnimator.OnAnimationFinishedCallback mFinishedCallback; 150 151 /** 152 * @param t Transaction to create the thumbnail in. 153 * @param screenshotBuffer A thumbnail or placeholder for thumbnail to initialize with. 154 */ Snapshot(Supplier<Surface> surfaceFactory, SurfaceControl.Transaction t, SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer, SurfaceControl parent)155 Snapshot(Supplier<Surface> surfaceFactory, SurfaceControl.Transaction t, 156 SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer, SurfaceControl parent) { 157 // We can't use a delegating constructor since we need to 158 // reference this::onAnimationFinished 159 GraphicBuffer graphicBuffer = GraphicBuffer.createFromHardwareBuffer( 160 screenshotBuffer.getHardwareBuffer()); 161 162 mSurfaceControl = mAnimatable.makeAnimationLeash() 163 .setName("snapshot anim: " + mAnimatable.toString()) 164 .setFormat(PixelFormat.TRANSLUCENT) 165 .setParent(parent) 166 .setSecure(screenshotBuffer.containsSecureLayers()) 167 .setCallsite("SurfaceFreezer.Snapshot") 168 .setBLASTLayer() 169 .build(); 170 171 ProtoLog.i(WM_SHOW_TRANSACTIONS, " THUMBNAIL %s: CREATE", mSurfaceControl); 172 173 t.setBuffer(mSurfaceControl, graphicBuffer); 174 t.setColorSpace(mSurfaceControl, screenshotBuffer.getColorSpace()); 175 t.show(mSurfaceControl); 176 177 // We parent the thumbnail to the container, and just place it on top of anything else 178 // in the container. 179 t.setLayer(mSurfaceControl, Integer.MAX_VALUE); 180 } 181 destroy(SurfaceControl.Transaction t)182 void destroy(SurfaceControl.Transaction t) { 183 if (mSurfaceControl == null) { 184 return; 185 } 186 t.remove(mSurfaceControl); 187 mSurfaceControl = null; 188 } 189 190 /** 191 * Starts an animation. 192 * 193 * @param anim The object that bridges the controller, {@link SurfaceAnimator}, with the 194 * component responsible for running the animation. It runs the animation with 195 * {@link AnimationAdapter#startAnimation} once the hierarchy with 196 * the Leash has been set up. 197 * @param animationFinishedCallback The callback being triggered when the animation 198 * finishes. 199 */ startAnimation(SurfaceControl.Transaction t, AnimationAdapter anim, int type, @Nullable SurfaceAnimator.OnAnimationFinishedCallback animationFinishedCallback)200 void startAnimation(SurfaceControl.Transaction t, AnimationAdapter anim, int type, 201 @Nullable SurfaceAnimator.OnAnimationFinishedCallback animationFinishedCallback) { 202 cancelAnimation(t, true /* restarting */); 203 mAnimation = anim; 204 mFinishedCallback = animationFinishedCallback; 205 if (mSurfaceControl == null) { 206 cancelAnimation(t, false /* restarting */); 207 return; 208 } 209 mAnimation.startAnimation(mSurfaceControl, t, type, animationFinishedCallback); 210 } 211 212 /** 213 * Cancels the animation, and resets the leash. 214 * 215 * @param t The transaction to use for all cancelling surface operations. 216 * @param restarting Whether we are restarting the animation. 217 */ cancelAnimation(SurfaceControl.Transaction t, boolean restarting)218 void cancelAnimation(SurfaceControl.Transaction t, boolean restarting) { 219 final SurfaceControl leash = mSurfaceControl; 220 final AnimationAdapter animation = mAnimation; 221 final SurfaceAnimator.OnAnimationFinishedCallback animationFinishedCallback = 222 mFinishedCallback; 223 mAnimation = null; 224 mFinishedCallback = null; 225 if (animation != null) { 226 animation.onAnimationCancelled(leash); 227 if (!restarting) { 228 if (animationFinishedCallback != null) { 229 animationFinishedCallback.onAnimationFinished( 230 ANIMATION_TYPE_APP_TRANSITION, animation); 231 } 232 } 233 } 234 if (!restarting) { 235 destroy(t); 236 } 237 } 238 } 239 240 /** freezable */ 241 public interface Freezable extends SurfaceAnimator.Animatable { 242 /** 243 * @return The surface to take a snapshot of. If this returns {@code null}, no snapshot 244 * will be generated (but the rest of the freezing logic will still happen). 245 */ getFreezeSnapshotTarget()246 @Nullable SurfaceControl getFreezeSnapshotTarget(); 247 } 248 } 249