1 /*
2  * Copyright (C) 2018 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.systemui.shared.system;
18 
19 import android.graphics.HardwareRenderer;
20 import android.graphics.Matrix;
21 import android.graphics.Rect;
22 import android.os.Handler;
23 import android.os.Handler.Callback;
24 import android.os.Message;
25 import android.os.Trace;
26 import android.view.SurfaceControl;
27 import android.view.SurfaceControl.Transaction;
28 import android.view.View;
29 import android.view.ViewRootImpl;
30 
31 import java.util.function.Consumer;
32 
33 /**
34  * Helper class to apply surface transactions in sync with RenderThread.
35  *
36  * NOTE: This is a modification of {@link android.view.SyncRtSurfaceTransactionApplier}, we can't
37  *       currently reference that class from the shared lib as it is hidden.
38  */
39 public class SyncRtSurfaceTransactionApplierCompat {
40 
41     public static final int FLAG_ALL = 0xffffffff;
42     public static final int FLAG_ALPHA = 1;
43     public static final int FLAG_MATRIX = 1 << 1;
44     public static final int FLAG_WINDOW_CROP = 1 << 2;
45     public static final int FLAG_LAYER = 1 << 3;
46     public static final int FLAG_CORNER_RADIUS = 1 << 4;
47     public static final int FLAG_BACKGROUND_BLUR_RADIUS = 1 << 5;
48     public static final int FLAG_VISIBILITY = 1 << 6;
49     public static final int FLAG_RELATIVE_LAYER = 1 << 7;
50     public static final int FLAG_SHADOW_RADIUS = 1 << 8;
51 
52     private static final int MSG_UPDATE_SEQUENCE_NUMBER = 0;
53 
54     private final SurfaceControl mBarrierSurfaceControl;
55     private final ViewRootImpl mTargetViewRootImpl;
56     private final Handler mApplyHandler;
57 
58     private int mSequenceNumber = 0;
59     private int mPendingSequenceNumber = 0;
60     private Runnable mAfterApplyCallback;
61 
62     /**
63      * @param targetView The view in the surface that acts as synchronization anchor.
64      */
SyncRtSurfaceTransactionApplierCompat(View targetView)65     public SyncRtSurfaceTransactionApplierCompat(View targetView) {
66         mTargetViewRootImpl = targetView != null ? targetView.getViewRootImpl() : null;
67         mBarrierSurfaceControl = mTargetViewRootImpl != null
68             ? mTargetViewRootImpl.getSurfaceControl() : null;
69 
70         mApplyHandler = new Handler(new Callback() {
71             @Override
72             public boolean handleMessage(Message msg) {
73                 if (msg.what == MSG_UPDATE_SEQUENCE_NUMBER) {
74                     onApplyMessage(msg.arg1);
75                     return true;
76                 }
77                 return false;
78             }
79         });
80     }
81 
onApplyMessage(int seqNo)82     private void onApplyMessage(int seqNo) {
83         mSequenceNumber = seqNo;
84         if (mSequenceNumber == mPendingSequenceNumber && mAfterApplyCallback != null) {
85             Runnable r = mAfterApplyCallback;
86             mAfterApplyCallback = null;
87             r.run();
88         }
89     }
90 
91     /**
92      * Schedules applying surface parameters on the next frame.
93      *
94      * @param params The surface parameters to apply. DO NOT MODIFY the list after passing into
95      *               this method to avoid synchronization issues.
96      */
scheduleApply(final SyncRtSurfaceTransactionApplierCompat.SurfaceParams... params)97     public void scheduleApply(final SyncRtSurfaceTransactionApplierCompat.SurfaceParams... params) {
98         if (mTargetViewRootImpl == null || mTargetViewRootImpl.getView() == null) {
99             return;
100         }
101 
102         mPendingSequenceNumber++;
103         final int toApplySeqNo = mPendingSequenceNumber;
104         mTargetViewRootImpl.registerRtFrameCallback(new HardwareRenderer.FrameDrawingCallback() {
105             @Override
106             public void onFrameDraw(long frame) {
107                 if (mBarrierSurfaceControl == null || !mBarrierSurfaceControl.isValid()) {
108                     Message.obtain(mApplyHandler, MSG_UPDATE_SEQUENCE_NUMBER, toApplySeqNo, 0)
109                             .sendToTarget();
110                     return;
111                 }
112                 Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Sync transaction frameNumber=" + frame);
113                 Transaction t = new Transaction();
114                 for (int i = params.length - 1; i >= 0; i--) {
115                     SyncRtSurfaceTransactionApplierCompat.SurfaceParams surfaceParams =
116                             params[i];
117                     surfaceParams.applyTo(t);
118                 }
119                 if (mTargetViewRootImpl != null) {
120                     mTargetViewRootImpl.mergeWithNextTransaction(t, frame);
121                 } else {
122                     t.apply();
123                 }
124                 Trace.traceEnd(Trace.TRACE_TAG_VIEW);
125                 Message.obtain(mApplyHandler, MSG_UPDATE_SEQUENCE_NUMBER, toApplySeqNo, 0)
126                         .sendToTarget();
127             }
128         });
129 
130         // Make sure a frame gets scheduled.
131         mTargetViewRootImpl.getView().invalidate();
132     }
133 
134     /**
135      * Calls the runnable when any pending apply calls have completed
136      */
addAfterApplyCallback(final Runnable afterApplyCallback)137     public void addAfterApplyCallback(final Runnable afterApplyCallback) {
138         if (mSequenceNumber == mPendingSequenceNumber) {
139             afterApplyCallback.run();
140         } else {
141             if (mAfterApplyCallback == null) {
142                 mAfterApplyCallback = afterApplyCallback;
143             } else {
144                 final Runnable oldCallback = mAfterApplyCallback;
145                 mAfterApplyCallback = new Runnable() {
146                     @Override
147                     public void run() {
148                         afterApplyCallback.run();
149                         oldCallback.run();
150                     }
151                 };
152             }
153         }
154     }
155 
applyParams(TransactionCompat t, SyncRtSurfaceTransactionApplierCompat.SurfaceParams params)156     public static void applyParams(TransactionCompat t,
157             SyncRtSurfaceTransactionApplierCompat.SurfaceParams params) {
158         params.applyTo(t.mTransaction);
159     }
160 
161     /**
162      * Creates an instance of SyncRtSurfaceTransactionApplier, deferring until the target view is
163      * attached if necessary.
164      */
create(final View targetView, final Consumer<SyncRtSurfaceTransactionApplierCompat> callback)165     public static void create(final View targetView,
166             final Consumer<SyncRtSurfaceTransactionApplierCompat> callback) {
167         if (targetView == null) {
168             // No target view, no applier
169             callback.accept(null);
170         } else if (targetView.getViewRootImpl() != null) {
171             // Already attached, we're good to go
172             callback.accept(new SyncRtSurfaceTransactionApplierCompat(targetView));
173         } else {
174             // Haven't been attached before we can get the view root
175             targetView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
176                 @Override
177                 public void onViewAttachedToWindow(View v) {
178                     targetView.removeOnAttachStateChangeListener(this);
179                     callback.accept(new SyncRtSurfaceTransactionApplierCompat(targetView));
180                 }
181 
182                 @Override
183                 public void onViewDetachedFromWindow(View v) {
184                     // Do nothing
185                 }
186             });
187         }
188     }
189 
190     public static class SurfaceParams {
191         public static class Builder {
192             final SurfaceControl surface;
193             int flags;
194             float alpha;
195             float cornerRadius;
196             int backgroundBlurRadius;
197             Matrix matrix;
198             Rect windowCrop;
199             int layer;
200             SurfaceControl relativeTo;
201             int relativeLayer;
202             boolean visible;
203             float shadowRadius;
204 
205             /**
206              * @param surface The surface to modify.
207              */
Builder(SurfaceControlCompat surface)208             public Builder(SurfaceControlCompat surface) {
209                 this(surface.mSurfaceControl);
210             }
211 
212             /**
213              * @param surface The surface to modify.
214              */
Builder(SurfaceControl surface)215             public Builder(SurfaceControl surface) {
216                 this.surface = surface;
217             }
218 
219             /**
220              * @param alpha The alpha value to apply to the surface.
221              * @return this Builder
222              */
withAlpha(float alpha)223             public Builder withAlpha(float alpha) {
224                 this.alpha = alpha;
225                 flags |= FLAG_ALPHA;
226                 return this;
227             }
228 
229             /**
230              * @param matrix The matrix to apply to the surface.
231              * @return this Builder
232              */
withMatrix(Matrix matrix)233             public Builder withMatrix(Matrix matrix) {
234                 this.matrix = new Matrix(matrix);
235                 flags |= FLAG_MATRIX;
236                 return this;
237             }
238 
239             /**
240              * @param windowCrop The window crop to apply to the surface.
241              * @return this Builder
242              */
withWindowCrop(Rect windowCrop)243             public Builder withWindowCrop(Rect windowCrop) {
244                 this.windowCrop = new Rect(windowCrop);
245                 flags |= FLAG_WINDOW_CROP;
246                 return this;
247             }
248 
249             /**
250              * @param layer The layer to assign the surface.
251              * @return this Builder
252              */
withLayer(int layer)253             public Builder withLayer(int layer) {
254                 this.layer = layer;
255                 flags |= FLAG_LAYER;
256                 return this;
257             }
258 
259             /**
260              * @param relativeTo The surface that's set relative layer to.
261              * @param relativeLayer The relative layer.
262              * @return this Builder
263              */
withRelativeLayerTo(SurfaceControl relativeTo, int relativeLayer)264             public Builder withRelativeLayerTo(SurfaceControl relativeTo, int relativeLayer) {
265                 this.relativeTo = relativeTo;
266                 this.relativeLayer = relativeLayer;
267                 flags |= FLAG_RELATIVE_LAYER;
268                 return this;
269             }
270 
271             /**
272              * @param radius the Radius for rounded corners to apply to the surface.
273              * @return this Builder
274              */
withCornerRadius(float radius)275             public Builder withCornerRadius(float radius) {
276                 this.cornerRadius = radius;
277                 flags |= FLAG_CORNER_RADIUS;
278                 return this;
279             }
280 
281             /**
282              * @param radius the Radius for the shadows to apply to the surface.
283              * @return this Builder
284              */
withShadowRadius(float radius)285             public Builder withShadowRadius(float radius) {
286                 this.shadowRadius = radius;
287                 flags |= FLAG_SHADOW_RADIUS;
288                 return this;
289             }
290 
291             /**
292              * @param radius the Radius for blur to apply to the background surfaces.
293              * @return this Builder
294              */
withBackgroundBlur(int radius)295             public Builder withBackgroundBlur(int radius) {
296                 this.backgroundBlurRadius = radius;
297                 flags |= FLAG_BACKGROUND_BLUR_RADIUS;
298                 return this;
299             }
300 
301             /**
302              * @param visible The visibility to apply to the surface.
303              * @return this Builder
304              */
withVisibility(boolean visible)305             public Builder withVisibility(boolean visible) {
306                 this.visible = visible;
307                 flags |= FLAG_VISIBILITY;
308                 return this;
309             }
310 
311             /**
312              * @return a new SurfaceParams instance
313              */
build()314             public SurfaceParams build() {
315                 return new SurfaceParams(surface, flags, alpha, matrix, windowCrop, layer,
316                         relativeTo, relativeLayer, cornerRadius, backgroundBlurRadius, visible,
317                         shadowRadius);
318             }
319         }
320 
SurfaceParams(SurfaceControl surface, int flags, float alpha, Matrix matrix, Rect windowCrop, int layer, SurfaceControl relativeTo, int relativeLayer, float cornerRadius, int backgroundBlurRadius, boolean visible, float shadowRadius)321         private SurfaceParams(SurfaceControl surface, int flags, float alpha, Matrix matrix,
322                 Rect windowCrop, int layer, SurfaceControl relativeTo, int relativeLayer,
323                 float cornerRadius, int backgroundBlurRadius, boolean visible, float shadowRadius) {
324             this.flags = flags;
325             this.surface = surface;
326             this.alpha = alpha;
327             this.matrix = matrix;
328             this.windowCrop = windowCrop;
329             this.layer = layer;
330             this.relativeTo = relativeTo;
331             this.relativeLayer = relativeLayer;
332             this.cornerRadius = cornerRadius;
333             this.backgroundBlurRadius = backgroundBlurRadius;
334             this.visible = visible;
335             this.shadowRadius = shadowRadius;
336         }
337 
338         private final int flags;
339         private final float[] mTmpValues = new float[9];
340 
341         public final SurfaceControl surface;
342         public final float alpha;
343         public final float cornerRadius;
344         public final int backgroundBlurRadius;
345         public final Matrix matrix;
346         public final Rect windowCrop;
347         public final int layer;
348         public final SurfaceControl relativeTo;
349         public final int relativeLayer;
350         public final boolean visible;
351         public final float shadowRadius;
352 
applyTo(SurfaceControl.Transaction t)353         public void applyTo(SurfaceControl.Transaction t) {
354             if ((flags & FLAG_MATRIX) != 0) {
355                 t.setMatrix(surface, matrix, mTmpValues);
356             }
357             if ((flags & FLAG_WINDOW_CROP) != 0) {
358                 t.setWindowCrop(surface, windowCrop);
359             }
360             if ((flags & FLAG_ALPHA) != 0) {
361                 t.setAlpha(surface, alpha);
362             }
363             if ((flags & FLAG_LAYER) != 0) {
364                 t.setLayer(surface, layer);
365             }
366             if ((flags & FLAG_CORNER_RADIUS) != 0) {
367                 t.setCornerRadius(surface, cornerRadius);
368             }
369             if ((flags & FLAG_BACKGROUND_BLUR_RADIUS) != 0) {
370                 t.setBackgroundBlurRadius(surface, backgroundBlurRadius);
371             }
372             if ((flags & FLAG_VISIBILITY) != 0) {
373                 if (visible) {
374                     t.show(surface);
375                 } else {
376                     t.hide(surface);
377                 }
378             }
379             if ((flags & FLAG_RELATIVE_LAYER) != 0) {
380                 t.setRelativeLayer(surface, relativeTo, relativeLayer);
381             }
382             if ((flags & FLAG_SHADOW_RADIUS) != 0) {
383                 t.setShadowRadius(surface, shadowRadius);
384             }
385         }
386     }
387 }
388