1 /*
2  * Copyright 2021 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 androidx.graphics.surface
18 
19 import android.graphics.Rect
20 import android.graphics.Region
21 import android.hardware.HardwareBuffer
22 import android.os.Build
23 import android.view.AttachedSurfaceControl
24 import android.view.Surface
25 import android.view.SurfaceControl
26 import android.view.SurfaceView
27 import android.view.Window
28 import androidx.annotation.FloatRange
29 import androidx.annotation.IntDef
30 import androidx.annotation.RequiresApi
31 import androidx.graphics.lowlatency.FrontBufferUtils
32 import androidx.graphics.utils.JniVisible
33 import androidx.hardware.DataSpace
34 import androidx.hardware.SyncFenceCompat
35 import java.util.concurrent.Executor
36 
37 /**
38  * Handle to an on-screen Surface managed by the system compositor. [SurfaceControlCompat] is a
39  * combination of a buffer source, and metadata about how to display the buffers. By constructing a
40  * [Surface] from this [SurfaceControl] you can submit buffers to be composited. Using
41  * [SurfaceControlCompat.Transaction] you can manipulate various properties of how the buffer will
42  * be displayed on-screen. [SurfaceControlCompat]s are arranged into a scene-graph like hierarchy,
43  * and as such any [SurfaceControlCompat] may have a parent. Geometric properties like transform,
44  * crop, and Z-ordering will be inherited from the parent, as if the child were content in the
45  * parents buffer stream.
46  *
47  * This class differs slightly than [SurfaceControl] in that it backports some functionality to
48  * Android R and above by delegating to the related APIs available in the NDK. For newer Android
49  * versions, this leverages the equivalent [SurfaceControl] API available in the SDK
50  */
51 @RequiresApi(Build.VERSION_CODES.Q)
52 class SurfaceControlCompat internal constructor(internal val scImpl: SurfaceControlImpl) {
53 
54     companion object {
55 
56         /**
57          * Constants for [Transaction.setBufferTransform].
58          *
59          * Various transformations that can be applied to a buffer.
60          */
61         @IntDef(
62             value =
63                 [
64                     BUFFER_TRANSFORM_IDENTITY,
65                     BUFFER_TRANSFORM_MIRROR_HORIZONTAL,
66                     BUFFER_TRANSFORM_MIRROR_VERTICAL,
67                     BUFFER_TRANSFORM_ROTATE_180,
68                     BUFFER_TRANSFORM_ROTATE_90,
69                     BUFFER_TRANSFORM_ROTATE_270
70                 ]
71         )
72         internal annotation class BufferTransform
73 
74         /** The identity transformation. Maps a coordinate (x, y) onto itself. */
75         const val BUFFER_TRANSFORM_IDENTITY = 0
76 
77         /** Mirrors the buffer horizontally. Maps a point (x, y) to (-x, y) */
78         const val BUFFER_TRANSFORM_MIRROR_HORIZONTAL = 1
79 
80         /** Mirrors the buffer vertically. Maps a point (x, y) to (x, -y) */
81         const val BUFFER_TRANSFORM_MIRROR_VERTICAL = 2
82 
83         /** Rotates the buffer 180 degrees clockwise. Maps a point (x, y) to (-x, -y) */
84         const val BUFFER_TRANSFORM_ROTATE_180 = 3
85 
86         /** Rotates the buffer 90 degrees clockwise. Maps a point (x, y) to (-y, x) */
87         const val BUFFER_TRANSFORM_ROTATE_90 = 4
88 
89         /** Rotates the buffer 270 degrees clockwise. Maps a point (x, y) to (y, -x) */
90         const val BUFFER_TRANSFORM_ROTATE_270 = 7
91 
92         /** Constants for [Transaction.setFrameRate] */
93         @IntDef(value = [CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS, CHANGE_FRAME_RATE_ALWAYS])
94         internal annotation class ChangeFrameRateStrategy
95 
96         /** Change the frame rate only if the transition is going to be seamless. */
97         const val CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS = 0
98 
99         /**
100          * Change the frame rate even if the transition is going to be non-seamless, i.e. with
101          * visual interruptions for the user.
102          */
103         const val CHANGE_FRAME_RATE_ALWAYS = 1
104 
105         /** Constants for configuring compatibility for [Transaction.setFrameRate] */
106         @IntDef(value = [FRAME_RATE_COMPATIBILITY_DEFAULT, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE])
107         internal annotation class FrameRateCompatibility
108 
109         /**
110          * There are no inherent restrictions on the frame rate. When the system selects a frame
111          * rate other than what the app requested, the app will be able to run at the system frame
112          * rate without requiring pull down (the mechanical process of "pulling", physically moving,
113          * frame content downward to advance it from one frame to the next at a repetitive rate).
114          * This value should be used when displaying game content, UIs, and anything that isn't
115          * video.
116          */
117         const val FRAME_RATE_COMPATIBILITY_DEFAULT = 0
118 
119         /**
120          * This compositing layer is being used to display content with an inherently fixed frame
121          * rate, e.g. a video that has a specific frame rate. When the system selects a frame rate
122          * other than what the app requested, the app will need to do pull down or use some other
123          * technique to adapt to the system's frame rate. Pull down involves the mechanical process
124          * of "pulling", physically moving, frame content downward to advance it from one frame to
125          * the next at a repetitive rate). The user experience is likely to be worse (e.g. more
126          * frame stuttering) than it would be if the system had chosen the app's requested frame
127          * rate. This value should be used for video content.
128          */
129         const val FRAME_RATE_COMPATIBILITY_FIXED_SOURCE = 1
130     }
131 
132     /**
133      * Check whether this instance points to a valid layer with the system-compositor. For example
134      * this may be false if the layer was released ([release]).
135      */
isValidnull136     fun isValid(): Boolean = scImpl.isValid()
137 
138     /**
139      * Release the local reference to the server-side surface. The [Surface] may continue to exist
140      * on-screen as long as its parent continues to exist. To explicitly remove a [Surface] from the
141      * screen use [Transaction.reparent] with a null-parent. After release, [isValid] will return
142      * false and other methods will throw an exception. Always call [release] when you are done with
143      * a [SurfaceControlCompat] instance.
144      */
145     fun release() {
146         scImpl.release()
147     }
148 
149     /**
150      * Builder class for [SurfaceControlCompat] objects. By default the [Surface] will be hidden,
151      * and have "unset" bounds, meaning it can be as large as the bounds of its parent if a buffer
152      * or child so requires. It is necessary to set at least a name via [Builder.setName]
153      */
154     class Builder {
155 
156         private val mBuilderImpl = createImpl()
157 
158         /**
159          * Set a parent [Surface] from the provided [SurfaceView] for our new
160          * [SurfaceControlCompat]. Child surfaces are constrained to the onscreen region of their
161          * parent. Furthermore they stack relatively in Z order, and inherit the transformation of
162          * the parent.
163          *
164          * @param surfaceView Target [SurfaceView] used to provide the [Surface] this
165          *   [SurfaceControlCompat] is associated with.
166          */
167         @Suppress("MissingGetterMatchingBuilder")
setParentnull168         fun setParent(surfaceView: SurfaceView): Builder {
169             mBuilderImpl.setParent(surfaceView)
170             return this
171         }
172 
173         /**
174          * Set a parent [SurfaceControlCompat] for the new [SurfaceControlCompat] instance.
175          * Furthermore they stack relatively in Z order, and inherit the transformation of the
176          * parent.
177          *
178          * @param surfaceControl Target [SurfaceControlCompat] used as the parent for the newly
179          *   created [SurfaceControlCompat] instance
180          */
181         @Suppress("MissingGetterMatchingBuilder")
setParentnull182         fun setParent(surfaceControl: SurfaceControlCompat): Builder {
183             mBuilderImpl.setParent(surfaceControl)
184             return this
185         }
186 
187         /**
188          * Set a debugging-name for the [SurfaceControlCompat].
189          *
190          * @param name Debugging name configured on the [SurfaceControlCompat] instance.
191          */
192         @Suppress("MissingGetterMatchingBuilder")
setNamenull193         fun setName(name: String): Builder {
194             mBuilderImpl.setName(name)
195             return this
196         }
197 
198         /**
199          * Construct a new [SurfaceControlCompat] with the set parameters. The builder remains valid
200          * after the [SurfaceControlCompat] instance is created.
201          */
buildnull202         fun build(): SurfaceControlCompat = SurfaceControlCompat(mBuilderImpl.build())
203 
204         internal companion object {
205             @RequiresApi(Build.VERSION_CODES.Q)
206             fun createImpl(): SurfaceControlImpl.Builder {
207                 val usePlatformTransaction =
208                     !FrontBufferUtils.UseCompatSurfaceControl &&
209                         Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
210                 return if (usePlatformTransaction) {
211                     SurfaceControlVerificationHelper.createBuilderV33()
212                 } else {
213                     SurfaceControlVerificationHelper.createBuilderV29()
214                 }
215             }
216         }
217     }
218 
219     /**
220      * Interface to handle request to
221      * [SurfaceControlV29.Transaction.addTransactionCompletedListener]
222      */
223     @JniVisible
224     internal interface TransactionCompletedListener {
225         /**
226          * Invoked when a frame including the updates in a transaction was presented.
227          *
228          * Buffers which are replaced or removed from the scene in the transaction invoking this
229          * callback may be reused after this point.
230          */
onTransactionCompletednull231         @JniVisible fun onTransactionCompleted(transactionStats: Long)
232     }
233 
234     /**
235      * Interface to handle request to
236      * [SurfaceControlCompat.Transaction.addTransactionCommittedListener]
237      */
238     @JniVisible
239     interface TransactionCommittedListener {
240         /** Invoked when the transaction has been committed in SurfaceFlinger */
241         @JniVisible fun onTransactionCommitted()
242     }
243 
244     /** An atomic set of changes to a set of [SurfaceControlCompat]. */
245     @RequiresApi(Build.VERSION_CODES.Q)
246     class Transaction : AutoCloseable {
247         /** internal mapping of buffer transforms used for testing purposes */
248         internal val mBufferTransforms = HashMap<SurfaceControlCompat, Int>()
249 
250         private val mImpl = createImpl()
251 
252         /**
253          * Indicates whether the surface must be considered opaque, even if its pixel format is set
254          * to translucent. This can be useful if an application needs full RGBA 8888 support for
255          * instance but will still draw every pixel opaque. This flag only determines whether
256          * opacity will be sampled from the alpha channel. Plane-alpha from calls to setAlpha() can
257          * still result in blended composition regardless of the opaque setting. Combined effects
258          * are (assuming a buffer format with an alpha channel):
259          *
260          * OPAQUE + alpha(1.0) == opaque composition OPAQUE + alpha(0.x) == blended composition
261          * OPAQUE + alpha(0.0) == no composition !OPAQUE + alpha(1.0) == blended composition
262          * !OPAQUE + alpha(0.x) == blended composition !OPAQUE + alpha(0.0) == no composition If the
263          * underlying buffer lacks an alpha channel, it is as if setOpaque(true) were set
264          * automatically.
265          *
266          * @param surfaceControl Target [SurfaceControlCompat] to change the opaque flag for
267          * @param isOpaque Flag indicating if the [SurfaceControlCompat] should be fully opaque or
268          *   transparent
269          */
setOpaquenull270         fun setOpaque(surfaceControl: SurfaceControlCompat, isOpaque: Boolean): Transaction {
271             mImpl.setOpaque(surfaceControl.scImpl, isOpaque)
272             return this
273         }
274 
275         /**
276          * Sets the visibility of a given Layer and it's sub-tree.
277          *
278          * @param surfaceControl Target [SurfaceControlCompat] to change the visibility
279          * @param visible `true` to indicate the [SurfaceControlCompat] should be visible, `false`
280          *   otherwise
281          */
setVisibilitynull282         fun setVisibility(surfaceControl: SurfaceControlCompat, visible: Boolean): Transaction {
283             mImpl.setVisibility(surfaceControl.scImpl, visible)
284             return this
285         }
286 
287         /**
288          * Re-parents a given [SurfaceControlCompat] to a new parent. Children inherit transform
289          * (position, scaling) crop, visibility, and Z-ordering from their parents, as if the
290          * children were pixels within the parent [Surface].
291          *
292          * @param surfaceControl Target [SurfaceControlCompat] instance to reparent
293          * @param newParent Parent [SurfaceControlCompat] that the target [SurfaceControlCompat]
294          *   instance is added to. This can be null indicating that the target
295          *   [SurfaceControlCompat] should be removed from the scene.
296          */
reparentnull297         fun reparent(
298             surfaceControl: SurfaceControlCompat,
299             newParent: SurfaceControlCompat?
300         ): Transaction {
301             mImpl.reparent(surfaceControl.scImpl, newParent?.scImpl)
302             return this
303         }
304 
305         /**
306          * Re-parents a given [SurfaceControlCompat] to be a child of the [AttachedSurfaceControl].
307          * Children inherit transform (position, scaling) crop, visibility, and Z-ordering from
308          * their parents, as if the children were pixels within the parent [Surface].
309          *
310          * @param surfaceControl Target [SurfaceControlCompat] instance to reparent
311          * @param attachedSurfaceControl [AttachedSurfaceControl] instance that acts as the new
312          *   parent of the provided [SurfaceControlCompat] instance.
313          */
314         @RequiresApi(Build.VERSION_CODES.TIRAMISU)
reparentnull315         fun reparent(
316             surfaceControl: SurfaceControlCompat,
317             attachedSurfaceControl: AttachedSurfaceControl
318         ): Transaction {
319             mImpl.reparent(surfaceControl.scImpl, attachedSurfaceControl)
320             return this
321         }
322 
323         /**
324          * Updates the [HardwareBuffer] displayed for the [SurfaceControlCompat]. Note that the
325          * buffer must be allocated with [HardwareBuffer.USAGE_COMPOSER_OVERLAY] as well as
326          * [HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE] as the surface control might be composited using
327          * either an overlay or using the GPU. A presentation fence may be passed to improve
328          * performance by allowing the buffer to complete rendering while it is waiting for the
329          * transaction to be applied. For example, if the buffer is being produced by rendering with
330          * OpenGL ES then a fence created with the eglDupNativeFenceFDANDROID EGL extension API can
331          * be used to allow the GPU rendering to be concurrent with the transaction. The compositor
332          * will wait for the fence to be signaled before the buffer is displayed. If multiple
333          * buffers are set as part of the same transaction, the presentation fences of all of them
334          * must signal before any buffer is displayed. That is, the entire transaction is delayed
335          * until all presentation fences have signaled, ensuring the transaction remains consistent.
336          *
337          * @param surfaceControl Target [SurfaceControlCompat] to configure the provided buffer.
338          * @param buffer [HardwareBuffer] instance to be rendered by the [SurfaceControlCompat]
339          *   instance. Use null to remove the current buffer that was previously configured on this
340          *   [SurfaceControlCompat] instance.
341          * @param fence Optional [SyncFenceCompat] that serves as the presentation fence. If set,
342          *   the [SurfaceControlCompat.Transaction] will not apply until the fence signals.
343          * @param releaseCallback Optional callback invoked when the buffer is ready for re-use
344          *   after being presented to the display. This also includes a [SyncFenceCompat] instance
345          *   that consumers must wait on before consuming the buffer
346          */
347         @JvmOverloads
setBuffernull348         fun setBuffer(
349             surfaceControl: SurfaceControlCompat,
350             buffer: HardwareBuffer?,
351             fence: SyncFenceCompat? = null,
352             releaseCallback: ((SyncFenceCompat) -> Unit)? = null
353         ): Transaction {
354             mImpl.setBuffer(surfaceControl.scImpl, buffer, fence?.mImpl, releaseCallback)
355             return this
356         }
357 
358         /**
359          * Set the Z-order for a given [SurfaceControlCompat], relative to its siblings. If two
360          * siblings share the same Z order the ordering is undefined. [Surface]s with a negative Z
361          * will be placed below the parent [Surface].
362          */
setLayernull363         fun setLayer(surfaceControl: SurfaceControlCompat, z: Int): Transaction {
364             mImpl.setLayer(surfaceControl.scImpl, z)
365             return this
366         }
367 
368         /**
369          * Request to add a [SurfaceControlCompat.TransactionCommittedListener]. The callback is
370          * invoked when transaction is applied and the updates are ready to be presented. Once
371          * applied, any callbacks added before the commit will be cleared from the Transaction. This
372          * callback does not mean buffers have been released! It simply means that any new
373          * transactions applied will not overwrite the transaction for which we are receiving a
374          * callback and instead will be included in the next frame. If you are trying to avoid
375          * dropping frames (overwriting transactions), and unable to use timestamps (Which provide a
376          * more efficient solution), then this method provides a method to pace your transaction
377          * application.
378          *
379          * @param executor [Executor] to provide the thread the callback is invoked on.
380          * @param listener [TransactionCommittedListener] instance that is invoked when the
381          *   transaction has been committed.
382          */
383         @Suppress("PairedRegistration")
384         @RequiresApi(Build.VERSION_CODES.S)
addTransactionCommittedListenernull385         fun addTransactionCommittedListener(
386             executor: Executor,
387             listener: TransactionCommittedListener
388         ): Transaction {
389             mImpl.addTransactionCommittedListener(executor, listener)
390             return this
391         }
392 
393         /**
394          * Updates the region for the content on this surface updated in this transaction. The
395          * damage region is the area of the buffer that has changed since the previously sent
396          * buffer. This can be used to reduce the amount of recomposition that needs to happen when
397          * only a small region of the buffer is being updated, such as for a small blinking cursor
398          * or a loading indicator.
399          *
400          * @param surfaceControl Target [SurfaceControlCompat] to set damage region of.
401          * @param region The region to be set. If null, the entire buffer is assumed dirty. This is
402          *   equivalent to not setting a damage region at all.
403          */
setDamageRegionnull404         fun setDamageRegion(surfaceControl: SurfaceControlCompat, region: Region?): Transaction {
405             mImpl.setDamageRegion(surfaceControl.scImpl, region)
406             return this
407         }
408 
409         /**
410          * Set the alpha for a given surface. If the alpha is non-zero the SurfaceControl will be
411          * blended with the Surfaces under it according to the specified ratio.
412          *
413          * @param surfaceControl Target [SurfaceControlCompat] to set the alpha of.
414          * @param alpha The alpha to set. Value is between 0.0 and 1.0 inclusive.
415          */
setAlphanull416         fun setAlpha(surfaceControl: SurfaceControlCompat, alpha: Float): Transaction {
417             mImpl.setAlpha(surfaceControl.scImpl, alpha)
418             return this
419         }
420 
421         /**
422          * Bounds the surface and its children to the bounds specified. Size of the surface will be
423          * ignored and only the crop and buffer size will be used to determine the bounds of the
424          * surface. If no crop is specified and the surface has no buffer, the surface bounds is
425          * only constrained by the size of its parent bounds.
426          *
427          * @param surfaceControl The [SurfaceControlCompat] to apply the crop to. This value cannot
428          *   be null.
429          * @param crop Bounds of the crop to apply. This value can be null.
430          * @throws IllegalArgumentException if crop is not a valid rectangle.
431          */
setCropnull432         fun setCrop(surfaceControl: SurfaceControlCompat, crop: Rect?): Transaction {
433             mImpl.setCrop(surfaceControl.scImpl, crop)
434             return this
435         }
436 
437         /**
438          * Sets the SurfaceControl to the specified position relative to the parent SurfaceControl
439          *
440          * @param surfaceControl The [SurfaceControlCompat] to change position. This value cannot be
441          *   null
442          * @param x the X position
443          * @param y the Y position
444          */
setPositionnull445         fun setPosition(surfaceControl: SurfaceControlCompat, x: Float, y: Float): Transaction {
446             mImpl.setPosition(surfaceControl.scImpl, x, y)
447             return this
448         }
449 
450         /**
451          * Sets the SurfaceControl to the specified scale with (0, 0) as the center point of the
452          * scale.
453          *
454          * @param surfaceControl The [SurfaceControlCompat] to change scale. This value cannot be
455          *   null.
456          * @param scaleX the X scale
457          * @param scaleY the Y scale
458          */
setScalenull459         fun setScale(
460             surfaceControl: SurfaceControlCompat,
461             scaleX: Float,
462             scaleY: Float
463         ): Transaction {
464             mImpl.setScale(surfaceControl.scImpl, scaleX, scaleY)
465             return this
466         }
467 
468         /**
469          * Sets the buffer transform that should be applied to the current buffer
470          *
471          * @param surfaceControl the [SurfaceControlCompat] to update. This value cannot be null.
472          * @param transformation The transform to apply to the buffer. Value is
473          *   [SurfaceControlCompat.BUFFER_TRANSFORM_IDENTITY],
474          *   [SurfaceControlCompat.BUFFER_TRANSFORM_MIRROR_HORIZONTAL],
475          *   [SurfaceControlCompat.BUFFER_TRANSFORM_MIRROR_VERTICAL],
476          *   [SurfaceControlCompat.BUFFER_TRANSFORM_ROTATE_90],
477          *   [SurfaceControlCompat.BUFFER_TRANSFORM_ROTATE_180],
478          *   [SurfaceControlCompat.BUFFER_TRANSFORM_ROTATE_270],
479          *   [SurfaceControlCompat.BUFFER_TRANSFORM_MIRROR_HORIZONTAL] |
480          *   [SurfaceControlCompat.BUFFER_TRANSFORM_ROTATE_90], or
481          *   [SurfaceControlCompat.BUFFER_TRANSFORM_MIRROR_VERTICAL] |
482          *   [SurfaceControlCompat.BUFFER_TRANSFORM_ROTATE_90]
483          */
setBufferTransformnull484         fun setBufferTransform(
485             surfaceControl: SurfaceControlCompat,
486             @BufferTransform transformation: Int
487         ): Transaction {
488             mBufferTransforms[surfaceControl] = transformation
489             mImpl.setBufferTransform(surfaceControl.scImpl, transformation)
490             return this
491         }
492 
493         /**
494          * Sets the intended frame rate for [SurfaceControlCompat].
495          *
496          * On devices that are capable of running the display at different refresh rates, the system
497          * may choose a display refresh rate to better match this surface's frame rate. Usage of
498          * this API won't directly affect the application's frame production pipeline. However,
499          * because the system may change the display refresh rate, calls to this function may result
500          * in changes to Choreographer callback timings, and changes to the time interval at which
501          * the system releases buffers back to the application.
502          *
503          * This method is only supported on Android R+ and is ignored on older platform versions.
504          *
505          * @param surfaceControl The target [SurfaceControlCompat] that will have it's frame rate
506          *   changed
507          * @param frameRate The intended frame rate of this surface, in frames per second. 0 is a
508          *   special value that indicates the app will accept the system's choice for the display
509          *   frame rate, which is the default behavior if this function isn't called. Must be
510          *   greater than or equal to 0. The frameRate param does not need to be a valid refresh
511          *   rate for this device's display
512          * - e.g., it's fine to pass 30fps to a device that can only run the display at 60fps.
513          *
514          * @param compatibility The frame rate compatibility of this surface. The compatibility
515          *   value may influence the system's choice of display frame rate. This must be either
516          *   [FRAME_RATE_COMPATIBILITY_DEFAULT] or [FRAME_RATE_COMPATIBILITY_FIXED_SOURCE] This
517          *   parameter is ignored when frameRate is 0.
518          * @param changeFrameRateStrategy Whether display refresh rate transitions should be
519          *   seamless. A seamless transition does not have any visual interruptions, such as a black
520          *   screen for a second or two. Must be either [CHANGE_FRAME_RATE_ALWAYS] or
521          *   [CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS]. This parameter is only supported on Android S and
522          *   when [frameRate] is not 0. This is ignored on older Android versions and when
523          *   [frameRate] is 0.
524          */
setFrameRatenull525         fun setFrameRate(
526             surfaceControl: SurfaceControlCompat,
527             frameRate: Float,
528             @FrameRateCompatibility compatibility: Int,
529             @ChangeFrameRateStrategy changeFrameRateStrategy: Int
530         ): Transaction {
531             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
532                 val strategy =
533                     when (changeFrameRateStrategy) {
534                         CHANGE_FRAME_RATE_ALWAYS -> changeFrameRateStrategy
535                         CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS -> changeFrameRateStrategy
536                         else -> CHANGE_FRAME_RATE_ONLY_IF_SEAMLESS
537                     }
538                 mImpl.setFrameRate(surfaceControl.scImpl, frameRate, compatibility, strategy)
539             }
540             return this
541         }
542 
543         /**
544          * Clears the frame rate which was set for the surface SurfaceControl.
545          *
546          * This is equivalent to calling [setFrameRate] with 0 for the framerate and
547          * [FRAME_RATE_COMPATIBILITY_DEFAULT]
548          *
549          * Note that this only has an effect for surfaces presented on the display. If this surface
550          * is consumed by something other than the system compositor, e.g. a media codec, this call
551          * has no effect.
552          *
553          * This is only supported on Android R and above. This is ignored on older Android versions.
554          *
555          * @param surfaceControl [SurfaceControlCompat] to clear the frame rate
556          */
clearFrameRatenull557         fun clearFrameRate(surfaceControl: SurfaceControlCompat): Transaction {
558             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
559                 mImpl.clearFrameRate(surfaceControl.scImpl)
560             }
561             return this
562         }
563 
564         /**
565          * Sets the desired extended range brightness for the layer. This only applies for layers
566          * that are displaying [HardwareBuffer] instances with a DataSpace of
567          * [DataSpace.RANGE_EXTENDED].
568          *
569          * @param surfaceControl The layer whose extended range brightness is being specified
570          * @param currentBufferRatio The current hdr/sdr ratio of the current buffer. For example if
571          *   the buffer was rendered with a target SDR whitepoint of 100 nits and a max display
572          *   brightness of 200 nits, this should be set to 2.0f.
573          *
574          * Default value is 1.0f.
575          *
576          * Transfer functions that encode their own brightness ranges, such as HLG or PQ, should
577          * also set this to 1.0f and instead communicate extended content brightness information via
578          * metadata such as CTA861_3 or SMPTE2086.
579          *
580          * Must be finite && >= 1.0f
581          *
582          * @param desiredRatio The desired hdr/sdr ratio. This can be used to communicate the max
583          *   desired brightness range. This is similar to the "max luminance" value in other HDR
584          *   metadata formats, but represented as a ratio of the target SDR whitepoint to the max
585          *   display brightness. The system may not be able to, or may choose not to, deliver the
586          *   requested range.
587          *
588          * While requesting a large desired ratio will result in the most dynamic range, voluntarily
589          * reducing the requested range can help improve battery life as well as can improve quality
590          * by ensuring greater bit depth is allocated to the luminance range in use.
591          *
592          * Default value is 1.0f and indicates that extended range brightness is not being used, so
593          * the resulting SDR or HDR behavior will be determined entirely by the dataspace being used
594          * (ie, typically SDR however PQ or HLG transfer functions will still result in HDR)
595          *
596          * Must be finite && >= 1.0f
597          *
598          * @return this
599          */
600         @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
setExtendedRangeBrightnessnull601         fun setExtendedRangeBrightness(
602             surfaceControl: SurfaceControlCompat,
603             @FloatRange(from = 1.0, fromInclusive = true) currentBufferRatio: Float,
604             @FloatRange(from = 1.0, fromInclusive = true) desiredRatio: Float
605         ): Transaction {
606             mImpl.setExtendedRangeBrightness(
607                 surfaceControl.scImpl,
608                 currentBufferRatio,
609                 desiredRatio
610             )
611             return this
612         }
613 
614         /**
615          * Set the [DataSpace] for the SurfaceControl. This will control how the buffer set with
616          * [setBuffer] is displayed.
617          *
618          * **NOTE** it is strongly recommended to verify that the display supports the corresponding
619          * [DataSpace] in advance before configuring the data space. For example, if
620          * [DataSpace.DATASPACE_DISPLAY_P3] is desired, consumers should verify that
621          * [Window.isWideGamut] returns true before proceeding.
622          *
623          * @param surfaceControl The SurfaceControl to update
624          * @param dataSpace The [DataSpace] to set it to. Must be one of named
625          *   [android.hardware.DataSpace] types.
626          * @return this
627          * @see [android.view.SurfaceControl.Transaction.setDataSpace]
628          */
setDataSpacenull629         fun setDataSpace(
630             surfaceControl: SurfaceControlCompat,
631             @DataSpace.NamedDataSpace dataSpace: Int
632         ): Transaction {
633             mImpl.setDataSpace(surfaceControl.scImpl, dataSpace)
634             return this
635         }
636 
637         /**
638          * Commit the transaction, clearing it's state, and making it usable as a new transaction.
639          * This will not release any resources and [SurfaceControlCompat.Transaction.close] must be
640          * called to release the transaction.
641          */
commitnull642         fun commit() {
643             mBufferTransforms.clear()
644             mImpl.commit()
645         }
646 
647         /** Release the native transaction object, without committing it. */
closenull648         override fun close() {
649             mImpl.close()
650         }
651 
652         /**
653          * Consume the passed in transaction, and request the View hierarchy to apply it atomically
654          * with the next draw. This transaction will be merged with the buffer transaction from the
655          * ViewRoot and they will show up on-screen atomically synced. This will not cause a draw to
656          * be scheduled, and if there are no other changes to the View hierarchy you may need to
657          * call View.invalidate()
658          *
659          * @param attachedSurfaceControl [AttachedSurfaceControl] associated with the ViewRoot that
660          *   will apply the provided transaction on the next draw pass
661          */
662         @RequiresApi(Build.VERSION_CODES.TIRAMISU)
commitTransactionOnDrawnull663         fun commitTransactionOnDraw(attachedSurfaceControl: AttachedSurfaceControl) {
664             mImpl.commitTransactionOnDraw(attachedSurfaceControl)
665         }
666 
667         internal companion object {
668             @RequiresApi(Build.VERSION_CODES.Q)
createImplnull669             fun createImpl(): SurfaceControlImpl.Transaction {
670                 val usePlatformSurfaceControl =
671                     !FrontBufferUtils.UseCompatSurfaceControl &&
672                         Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
673                 return if (usePlatformSurfaceControl) {
674                     SurfaceControlVerificationHelper.createTransactionV33()
675                 } else {
676                     SurfaceControlVerificationHelper.createTransactionV29()
677                 }
678             }
679         }
680     }
681 }
682 
683 /** Helper class to avoid class verification failures */
684 internal class SurfaceControlVerificationHelper private constructor() {
685 
686     companion object {
687         @RequiresApi(Build.VERSION_CODES.TIRAMISU)
createBuilderV33null688         fun createBuilderV33(): SurfaceControlImpl.Builder = SurfaceControlV33.Builder()
689 
690         @RequiresApi(Build.VERSION_CODES.Q)
691         fun createBuilderV29(): SurfaceControlImpl.Builder = SurfaceControlV29.Builder()
692 
693         @RequiresApi(Build.VERSION_CODES.TIRAMISU)
694         fun createTransactionV33(): SurfaceControlImpl.Transaction = SurfaceControlV33.Transaction()
695 
696         @RequiresApi(Build.VERSION_CODES.Q)
697         fun createTransactionV29(): SurfaceControlImpl.Transaction = SurfaceControlV29.Transaction()
698     }
699 }
700