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