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