• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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