1 /*
<lambda>null2  * Copyright 2022 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.annotation.SuppressLint
20 import android.graphics.Bitmap
21 import android.graphics.Color
22 import android.graphics.ColorSpace
23 import android.graphics.Rect
24 import android.graphics.Region
25 import android.hardware.DataSpace
26 import android.opengl.EGL14
27 import android.os.Build
28 import android.os.SystemClock
29 import android.view.Display
30 import android.view.SurfaceHolder
31 import android.view.SurfaceView
32 import androidx.annotation.RequiresApi
33 import androidx.graphics.opengl.egl.EGLConfigAttributes
34 import androidx.graphics.opengl.egl.EGLManager
35 import androidx.graphics.opengl.egl.EGLSpec
36 import androidx.graphics.opengl.egl.EGLVersion
37 import androidx.graphics.opengl.egl.supportsNativeAndroidFence
38 import androidx.graphics.surface.SurfaceControlUtils.Companion.getSolidBuffer
39 import androidx.hardware.SyncFenceCompat
40 import androidx.lifecycle.Lifecycle
41 import androidx.test.core.app.ActivityScenario
42 import androidx.test.ext.junit.runners.AndroidJUnit4
43 import androidx.test.filters.SdkSuppress
44 import androidx.test.filters.SmallTest
45 import java.util.concurrent.CountDownLatch
46 import java.util.concurrent.Executor
47 import java.util.concurrent.Executors
48 import java.util.concurrent.TimeUnit
49 import java.util.function.Consumer
50 import org.junit.Assert.assertEquals
51 import org.junit.Assert.assertFalse
52 import org.junit.Assert.assertNotNull
53 import org.junit.Assert.assertThrows
54 import org.junit.Assert.assertTrue
55 import org.junit.Assert.fail
56 import org.junit.Before
57 import org.junit.Test
58 import org.junit.runner.RunWith
59 
60 @RunWith(AndroidJUnit4::class)
61 @SmallTest
62 @SdkSuppress(minSdkVersion = 29)
63 class SurfaceControlCompatTest {
64     var executor: Executor? = null
65 
66     @Before
67     fun setup() {
68         executor = Executors.newSingleThreadExecutor(Executors.defaultThreadFactory())
69     }
70 
71     private abstract class SurfaceHolderCallback : SurfaceHolder.Callback {
72         override fun surfaceChanged(p0: SurfaceHolder, p1: Int, p2: Int, p3: Int) {}
73 
74         override fun surfaceDestroyed(p0: SurfaceHolder) {}
75     }
76 
77     @Test
78     fun testSurfaceControlCompatBuilder_parent() {
79         val callbackLatch = CountDownLatch(1)
80         val scenario = ActivityScenario.launch(SurfaceControlWrapperTestActivity::class.java)
81 
82         try {
83             scenario.onActivity {
84                 val callback =
85                     object : SurfaceHolderCallback() {
86                         override fun surfaceCreated(sh: SurfaceHolder) {
87                             SurfaceControlCompat.Builder()
88                                 .setParent(it.mSurfaceView)
89                                 .setName("SurfaceControlCompatTest")
90                                 .build()
91 
92                             callbackLatch.countDown()
93                         }
94                     }
95 
96                 it.addSurface(it.getSurfaceView(), callback)
97             }
98 
99             assertTrue(callbackLatch.await(3000, TimeUnit.MILLISECONDS))
100         } catch (e: java.lang.IllegalArgumentException) {
101             fail()
102         } finally {
103             // ensure activity is destroyed after any failures
104             scenario.moveToState(Lifecycle.State.DESTROYED)
105         }
106     }
107 
108     @Test
109     fun testSurfaceControlCompatBuilder_parentSurfaceControl() {
110         val callbackLatch = CountDownLatch(1)
111         val scenario = ActivityScenario.launch(SurfaceControlWrapperTestActivity::class.java)
112 
113         try {
114             scenario.onActivity {
115                 val callback =
116                     object : SurfaceHolderCallback() {
117                         override fun surfaceCreated(sh: SurfaceHolder) {
118                             val parentSc =
119                                 SurfaceControlCompat.Builder()
120                                     .setParent(it.mSurfaceView)
121                                     .setName("ParentSurfaceControl")
122                                     .build()
123 
124                             SurfaceControlCompat.Builder()
125                                 .setParent(parentSc)
126                                 .setName("ChildSurfaceControl")
127                                 .build()
128 
129                             callbackLatch.countDown()
130                         }
131                     }
132 
133                 it.addSurface(it.getSurfaceView(), callback)
134             }
135             assertTrue(callbackLatch.await(3000, TimeUnit.MILLISECONDS))
136         } catch (e: java.lang.IllegalArgumentException) {
137             fail()
138         } finally {
139             // ensure activity is destroyed after any failures
140             scenario.moveToState(Lifecycle.State.DESTROYED)
141         }
142     }
143 
144     @Test
145     fun testSurfaceTransactionCreate() {
146         try {
147             SurfaceControlCompat.Transaction()
148         } catch (e: java.lang.IllegalArgumentException) {
149             fail()
150         }
151     }
152 
153     class TransactionOnCommitListener : SurfaceControlCompat.TransactionCommittedListener {
154         var mCallbackTime = -1L
155         var mLatch = CountDownLatch(1)
156 
157         override fun onTransactionCommitted() {
158             mCallbackTime = SystemClock.elapsedRealtime()
159             mLatch.countDown()
160         }
161     }
162 
163     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
164     @Test
165     fun testSurfaceTransactionOnCommitCallback() {
166         val listener = TransactionOnCommitListener()
167 
168         val scenario = ActivityScenario.launch(SurfaceControlWrapperTestActivity::class.java)
169 
170         try {
171             scenario.onActivity {
172                 SurfaceControlCompat.Transaction()
173                     .addTransactionCommittedListener(executor!!, listener)
174                     .commit()
175             }
176 
177             listener.mLatch.await(3, TimeUnit.SECONDS)
178             assertEquals(0, listener.mLatch.count)
179             assertTrue(listener.mCallbackTime > 0)
180         } finally {
181             // ensure activity is destroyed after any failures
182             scenario.moveToState(Lifecycle.State.DESTROYED)
183         }
184     }
185 
186     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
187     @Test
188     fun testSurfaceTransactionOnCommitCallback_multiple() {
189         val listener = TransactionOnCommitListener()
190         val listener2 = TransactionOnCommitListener()
191 
192         val scenario = ActivityScenario.launch(SurfaceControlWrapperTestActivity::class.java)
193 
194         try {
195             scenario.onActivity {
196                 SurfaceControlCompat.Transaction()
197                     .addTransactionCommittedListener(executor!!, listener)
198                     .addTransactionCommittedListener(executor!!, listener2)
199                     .commit()
200             }
201 
202             listener.mLatch.await(3, TimeUnit.SECONDS)
203             listener2.mLatch.await(3, TimeUnit.SECONDS)
204 
205             assertEquals(0, listener.mLatch.count)
206             assertEquals(0, listener2.mLatch.count)
207 
208             assertTrue(listener.mCallbackTime > 0)
209             assertTrue(listener2.mCallbackTime > 0)
210         } finally {
211             // ensure activity is destroyed after any failures
212             scenario.moveToState(Lifecycle.State.DESTROYED)
213         }
214     }
215 
216     @Test
217     fun testSurfaceControlIsValid_valid() {
218         val callbackLatch = CountDownLatch(1)
219         val scenario = ActivityScenario.launch(SurfaceControlWrapperTestActivity::class.java)
220         try {
221             scenario.onActivity {
222                 val callback =
223                     object : SurfaceHolderCallback() {
224                         override fun surfaceCreated(sh: SurfaceHolder) {
225                             val scCompat =
226                                 SurfaceControlCompat.Builder()
227                                     .setParent(it.getSurfaceView())
228                                     .setName("SurfaceControlCompatTest")
229                                     .build()
230 
231                             assertTrue(scCompat.isValid())
232                             callbackLatch.countDown()
233                         }
234                     }
235 
236                 it.addSurface(it.mSurfaceView, callback)
237             }
238 
239             assertTrue(callbackLatch.await(3000, TimeUnit.MILLISECONDS))
240         } catch (e: java.lang.IllegalArgumentException) {
241             fail()
242         } finally {
243             // ensure activity is destroyed after any failures
244             scenario.moveToState(Lifecycle.State.DESTROYED)
245         }
246     }
247 
248     @Test
249     fun testSurfaceControlIsValid_validNotValid() {
250         val callbackLatch = CountDownLatch(1)
251         val scenario = ActivityScenario.launch(SurfaceControlWrapperTestActivity::class.java)
252         try {
253             scenario.onActivity {
254                 val callback =
255                     object : SurfaceHolderCallback() {
256                         override fun surfaceCreated(sh: SurfaceHolder) {
257                             val scCompat =
258                                 SurfaceControlCompat.Builder()
259                                     .setParent(it.getSurfaceView())
260                                     .setName("SurfaceControlCompatTest")
261                                     .build()
262 
263                             assertTrue(scCompat.isValid())
264                             scCompat.release()
265                             assertFalse(scCompat.isValid())
266 
267                             callbackLatch.countDown()
268                         }
269                     }
270 
271                 it.addSurface(it.mSurfaceView, callback)
272             }
273             assertTrue(callbackLatch.await(3000, TimeUnit.MILLISECONDS))
274         } catch (e: java.lang.IllegalArgumentException) {
275             fail()
276         } finally {
277             // ensure activity is destroyed after any failures
278             scenario.moveToState(Lifecycle.State.DESTROYED)
279         }
280     }
281 
282     @Test
283     fun testSurfaceControlIsValid_multipleReleases() {
284         val callbackLatch = CountDownLatch(1)
285         val scenario = ActivityScenario.launch(SurfaceControlWrapperTestActivity::class.java)
286         try {
287             scenario.onActivity {
288                 val callback =
289                     object : SurfaceHolderCallback() {
290                         override fun surfaceCreated(sh: SurfaceHolder) {
291                             val scCompat =
292                                 SurfaceControlCompat.Builder()
293                                     .setParent(it.getSurfaceView())
294                                     .setName("SurfaceControlCompatTest")
295                                     .build()
296 
297                             assertTrue(scCompat.isValid())
298                             scCompat.release()
299                             scCompat.release()
300                             assertFalse(scCompat.isValid())
301 
302                             callbackLatch.countDown()
303                         }
304                     }
305 
306                 it.addSurface(it.mSurfaceView, callback)
307             }
308 
309             assertTrue(callbackLatch.await(3000, TimeUnit.MILLISECONDS))
310         } catch (e: java.lang.IllegalArgumentException) {
311             fail()
312         } finally {
313             // ensure activity is destroyed after any failures
314             scenario.moveToState(Lifecycle.State.DESTROYED)
315         }
316     }
317 
318     @Test
319     fun testTransactionReparent_null() {
320         verifySurfaceControlTest({ surfaceView ->
321             val scCompat =
322                 SurfaceControlCompat.Builder()
323                     .setParent(surfaceView)
324                     .setName("SurfaceControlCompatTest")
325                     .build()
326 
327             // Buffer colorspace is RGBA, so Color.BLUE will be visually Red
328             val buffer =
329                 SurfaceControlUtils.getSolidBuffer(
330                     SurfaceControlWrapperTestActivity.DEFAULT_WIDTH,
331                     SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT,
332                     Color.BLUE
333                 )
334             assertNotNull(buffer)
335 
336             SurfaceControlCompat.Transaction()
337                 .setBuffer(scCompat, buffer)
338                 .setVisibility(scCompat, true)
339                 .reparent(scCompat, null)
340         }) { bitmap, rect ->
341             Color.BLACK == bitmap.getPixel(rect.left, rect.top)
342         }
343     }
344 
345     @Test
346     fun testSetBufferInvokesPreviousReleaseCallback() {
347         val buffer1 =
348             SurfaceControlUtils.getSolidBuffer(
349                 SurfaceControlWrapperTestActivity.DEFAULT_WIDTH,
350                 SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT,
351                 Color.BLUE
352             )
353         val buffer2 =
354             SurfaceControlUtils.getSolidBuffer(
355                 SurfaceControlWrapperTestActivity.DEFAULT_WIDTH,
356                 SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT,
357                 Color.RED
358             )
359         var releaseCallbackInvoked = false
360         val setBuffer1Latch = CountDownLatch(1)
361 
362         try {
363             verifySurfaceControlTest({ surfaceView ->
364                 val scCompat =
365                     SurfaceControlCompat.Builder()
366                         .setParent(surfaceView)
367                         .setName("SurfaceControlCompatTest")
368                         .build()
369                 SurfaceControlCompat.Transaction()
370                     .setBuffer(scCompat, buffer1) {
371                         releaseCallbackInvoked = true
372                         setBuffer1Latch.countDown()
373                     }
374                     .commit()
375 
376                 var visibility = false
377                 repeat(3) {
378                     SurfaceControlCompat.Transaction().setVisibility(scCompat, visibility).commit()
379                     visibility = !visibility
380                 }
381 
382                 assertFalse(setBuffer1Latch.await(1000, TimeUnit.MILLISECONDS))
383                 assertFalse(releaseCallbackInvoked)
384 
385                 SurfaceControlCompat.Transaction().setBuffer(scCompat, buffer2) {
386                     // NO-OP
387                 }
388             }) { _, _ ->
389                 setBuffer1Latch.await(3000, TimeUnit.MILLISECONDS) && releaseCallbackInvoked
390             }
391         } finally {
392             buffer1.close()
393             buffer2.close()
394         }
395     }
396 
397     @Test
398     fun testTransactionReparent_childOfSibling() {
399         verifySurfaceControlTest({ surfaceView ->
400             val scCompat =
401                 SurfaceControlCompat.Builder()
402                     .setParent(surfaceView)
403                     .setName("SurfaceControlCompatTest")
404                     .build()
405             val scCompat2 =
406                 SurfaceControlCompat.Builder()
407                     .setParent(surfaceView)
408                     .setName("SurfaceControlCompatTest")
409                     .build()
410 
411             // Buffer colorspace is RGBA, so Color.BLUE will be visually Red
412             val buffer =
413                 SurfaceControlUtils.getSolidBuffer(
414                     SurfaceControlWrapperTestActivity.DEFAULT_WIDTH,
415                     SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT,
416                     Color.BLUE
417                 )
418             assertNotNull(buffer)
419 
420             val buffer2 =
421                 SurfaceControlUtils.getSolidBuffer(
422                     SurfaceControlWrapperTestActivity.DEFAULT_WIDTH,
423                     SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT,
424                     Color.GREEN
425                 )
426             assertNotNull(buffer2)
427 
428             SurfaceControlCompat.Transaction()
429                 .setBuffer(scCompat, buffer)
430                 .setBuffer(scCompat2, buffer2)
431                 .setVisibility(scCompat, true)
432                 .setVisibility(scCompat2, true)
433                 .apply { reparent(scCompat, scCompat2) }
434         }) { bitmap, rect ->
435             Color.RED == bitmap.getPixel(rect.left, rect.top)
436         }
437     }
438 
439     @Test
440     fun testTransactionSetBuffer_nullFence() {
441         verifySurfaceControlTest({ surfaceView ->
442             val scCompat =
443                 SurfaceControlCompat.Builder()
444                     .setParent(surfaceView)
445                     .setName("SurfaceControlCompatTest")
446                     .build()
447 
448             // Buffer colorspace is RGBA, so Color.BLUE will be visually Red
449             val buffer =
450                 SurfaceControlUtils.getSolidBuffer(
451                     SurfaceControlWrapperTestActivity.DEFAULT_WIDTH,
452                     SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT,
453                     Color.BLUE
454                 )
455             assertNotNull(buffer)
456 
457             SurfaceControlCompat.Transaction()
458                 .setBuffer(scCompat, buffer, null)
459                 .setVisibility(scCompat, true)
460         }) { bitmap, rect ->
461             Color.RED == bitmap.getPixel(rect.left, rect.top)
462         }
463     }
464 
465     @Test
466     fun testTransactionSetBuffer_simpleFence() {
467         var eglManager: EGLManager? = null
468         verifySurfaceControlTest({ surfaceView ->
469             val manager =
470                 EGLManager().apply {
471                     initialize()
472                     val config = loadConfig(EGLConfigAttributes.RGBA_8888)
473                     if (config == null) {
474                         fail("Config 8888 should be supported")
475                     }
476                     createContext(config!!)
477                 }
478             eglManager = manager
479 
480             val scCompat =
481                 SurfaceControlCompat.Builder()
482                     .setParent(surfaceView)
483                     .setName("SurfaceControlCompatTest")
484                     .build()
485 
486             // Buffer colorspace is RGBA, so Color.BLUE will be visually Red
487             val buffer =
488                 SurfaceControlUtils.getSolidBuffer(
489                     SurfaceControlWrapperTestActivity.DEFAULT_WIDTH,
490                     SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT,
491                     Color.BLUE
492                 )
493             assertNotNull(buffer)
494 
495             val fence =
496                 if (manager.supportsNativeAndroidFence()) {
497                     SyncFenceCompat.createNativeSyncFence()
498                 } else {
499                     null
500                 }
501             SurfaceControlCompat.Transaction()
502                 .setBuffer(scCompat, buffer, fence)
503                 .setVisibility(scCompat, true)
504         }) { bitmap, rect ->
505             Color.RED == bitmap.getPixel(rect.left, rect.top)
506         }
507         eglManager?.release()
508     }
509 
510     @Test
511     fun testTransactionSetBuffer_nullCallback() {
512         verifySurfaceControlTest({ surfaceView ->
513             val scCompat =
514                 SurfaceControlCompat.Builder()
515                     .setParent(surfaceView)
516                     .setName("SurfaceControlCompatTest")
517                     .build()
518 
519             // Buffer colorspace is RGBA, so Color.BLUE will be visually Red
520             val buffer =
521                 SurfaceControlUtils.getSolidBuffer(
522                     SurfaceControlWrapperTestActivity.DEFAULT_WIDTH,
523                     SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT,
524                     Color.BLUE
525                 )
526             assertNotNull(buffer)
527 
528             SurfaceControlCompat.Transaction()
529                 .setBuffer(scCompat, buffer)
530                 .setVisibility(scCompat, true)
531         }) { bitmap, rect ->
532             Color.RED == bitmap.getPixel(rect.left, rect.top)
533         }
534     }
535 
536     @Test
537     fun testTransactionSetBuffer_singleReleaseCallback() {
538         val releaseLatch = CountDownLatch(1)
539         verifySurfaceControlTest({ surfaceView ->
540             val scCompat =
541                 SurfaceControlCompat.Builder()
542                     .setParent(surfaceView)
543                     .setName("SurfaceControlCompatTest")
544                     .build()
545 
546             val buffer =
547                 SurfaceControlUtils.getSolidBuffer(
548                     SurfaceControlWrapperTestActivity.DEFAULT_WIDTH,
549                     SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT,
550                     Color.GREEN
551                 )
552             assertNotNull(buffer)
553 
554             // Buffer colorspace is RGBA, so Color.BLUE will be visually Red
555             val buffer2 =
556                 SurfaceControlUtils.getSolidBuffer(
557                     SurfaceControlWrapperTestActivity.DEFAULT_WIDTH,
558                     SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT,
559                     Color.BLUE
560                 )
561             assertNotNull(buffer2)
562 
563             SurfaceControlCompat.Transaction()
564                 .setBuffer(scCompat, buffer) { releaseFence ->
565                     releaseFence.awaitForever()
566                     releaseLatch.countDown()
567                 }
568                 .setVisibility(scCompat, true)
569                 .commit()
570 
571             SurfaceControlCompat.Transaction()
572                 .setBuffer(scCompat, buffer2)
573                 .setVisibility(scCompat, true)
574         }) { bitmap, rect ->
575             assertTrue(releaseLatch.await(3000, TimeUnit.MILLISECONDS))
576             Color.RED == bitmap.getPixel(rect.left, rect.top)
577         }
578     }
579 
580     @Test
581     fun testTransactionSetBuffer_multipleReleaseCallbacksAndOverwriteWithSingleSC() {
582         val releaseLatch = CountDownLatch(1)
583         val releaseLatch2 = CountDownLatch(1)
584 
585         verifySurfaceControlTest({ surfaceView ->
586             val scCompat =
587                 SurfaceControlCompat.Builder()
588                     .setParent(surfaceView)
589                     .setName("SurfaceControlCompatTest")
590                     .build()
591 
592             val buffer =
593                 SurfaceControlUtils.getSolidBuffer(
594                     SurfaceControlWrapperTestActivity.DEFAULT_WIDTH,
595                     SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT,
596                     Color.GREEN
597                 )
598             assertNotNull(buffer)
599 
600             val buffer2 =
601                 SurfaceControlUtils.getSolidBuffer(
602                     SurfaceControlWrapperTestActivity.DEFAULT_WIDTH,
603                     SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT,
604                     Color.GREEN
605                 )
606             assertNotNull(buffer2)
607 
608             val buffer3 =
609                 SurfaceControlUtils.getSolidBuffer(
610                     SurfaceControlWrapperTestActivity.DEFAULT_WIDTH,
611                     SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT,
612                     Color.BLUE
613                 )
614             assertNotNull(buffer3)
615 
616             SurfaceControlCompat.Transaction()
617                 .setBuffer(scCompat, buffer) { releaseFence ->
618                     releaseFence.awaitForever()
619                     releaseLatch.countDown()
620                 }
621                 .setBuffer(scCompat, buffer2) { releaseFence ->
622                     releaseFence.awaitForever()
623                     assertTrue(releaseLatch.await(3000, TimeUnit.MILLISECONDS))
624                     releaseLatch2.countDown()
625                 }
626                 .setVisibility(scCompat, true)
627                 .commit()
628 
629             SurfaceControlCompat.Transaction()
630                 .setBuffer(scCompat, buffer3)
631                 .setVisibility(scCompat, true)
632         }) { bitmap, rect ->
633             assertTrue(releaseLatch2.await(3000, TimeUnit.MILLISECONDS))
634             Color.RED == bitmap.getPixel(rect.left, rect.top)
635         }
636     }
637 
638     @Test
639     fun testTransactionSetBuffer_multipleNullCallbacksWithOneNonNull() {
640         val releaseLatch = CountDownLatch(1)
641         verifySurfaceControlTest({ surfaceView ->
642             val scCompat =
643                 SurfaceControlCompat.Builder()
644                     .setParent(surfaceView)
645                     .setName("SurfaceControlCompatTest")
646                     .build()
647 
648             val buffer =
649                 SurfaceControlUtils.getSolidBuffer(
650                     SurfaceControlWrapperTestActivity.DEFAULT_WIDTH,
651                     SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT,
652                     Color.GREEN
653                 )
654             assertNotNull(buffer)
655 
656             val buffer2 =
657                 SurfaceControlUtils.getSolidBuffer(
658                     SurfaceControlWrapperTestActivity.DEFAULT_WIDTH,
659                     SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT,
660                     Color.GREEN
661                 )
662             assertNotNull(buffer2)
663 
664             val buffer3 =
665                 SurfaceControlUtils.getSolidBuffer(
666                     SurfaceControlWrapperTestActivity.DEFAULT_WIDTH,
667                     SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT,
668                     Color.BLUE
669                 )
670             assertNotNull(buffer3)
671 
672             SurfaceControlCompat.Transaction()
673                 .setBuffer(scCompat, buffer)
674                 .setBuffer(scCompat, buffer3) { releaseFence ->
675                     releaseFence.awaitForever()
676                     releaseLatch.countDown()
677                 }
678                 .setBuffer(scCompat, buffer2)
679                 .setVisibility(scCompat, true)
680                 .commit()
681 
682             SurfaceControlCompat.Transaction()
683                 .setBuffer(scCompat, buffer3)
684                 .setVisibility(scCompat, true)
685         }) { bitmap, rect ->
686             assertTrue(releaseLatch.await(3000, TimeUnit.MILLISECONDS))
687             Color.RED == bitmap.getPixel(rect.left, rect.top)
688         }
689     }
690 
691     @Test
692     fun testTransactionSetBuffer_ReleaseCallbacksAndOverwriteWithMultipleSC() {
693         val releaseLatch = CountDownLatch(1)
694         val releaseLatch2 = CountDownLatch(1)
695         verifySurfaceControlTest({ surfaceView ->
696             val scCompat =
697                 SurfaceControlCompat.Builder()
698                     .setParent(surfaceView)
699                     .setName("SurfaceControlCompatTest")
700                     .build()
701             val scCompat2 =
702                 SurfaceControlCompat.Builder()
703                     .setParent(surfaceView)
704                     .setName("SurfaceControlCompatTest")
705                     .build()
706 
707             val buffer =
708                 SurfaceControlUtils.getSolidBuffer(
709                     SurfaceControlWrapperTestActivity.DEFAULT_WIDTH,
710                     SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT,
711                     Color.GREEN
712                 )
713             assertNotNull(buffer)
714 
715             val buffer2 =
716                 SurfaceControlUtils.getSolidBuffer(
717                     SurfaceControlWrapperTestActivity.DEFAULT_WIDTH,
718                     SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT,
719                     Color.GREEN
720                 )
721             assertNotNull(buffer2)
722 
723             val buffer3 =
724                 SurfaceControlUtils.getSolidBuffer(
725                     SurfaceControlWrapperTestActivity.DEFAULT_WIDTH,
726                     SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT,
727                     Color.BLUE
728                 )
729             assertNotNull(buffer3)
730             val buffer4 =
731                 SurfaceControlUtils.getSolidBuffer(
732                     SurfaceControlWrapperTestActivity.DEFAULT_WIDTH,
733                     SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT,
734                     Color.BLUE
735                 )
736             assertNotNull(buffer3)
737 
738             SurfaceControlCompat.Transaction()
739                 .setBuffer(scCompat, buffer) { releaseFence ->
740                     releaseFence.awaitForever()
741                     releaseLatch.countDown()
742                 }
743                 .setBuffer(scCompat2, buffer2) { releaseFence ->
744                     releaseFence.awaitForever()
745                     releaseLatch2.countDown()
746                 }
747                 .setVisibility(scCompat, true)
748                 .setVisibility(scCompat2, true)
749                 .commit()
750 
751             SurfaceControlCompat.Transaction()
752                 .setBuffer(scCompat, buffer3)
753                 .setBuffer(scCompat2, buffer4)
754                 .setVisibility(scCompat, true)
755                 .setVisibility(scCompat2, true)
756         }) { bitmap, rect ->
757             assertTrue(releaseLatch.await(3000, TimeUnit.MILLISECONDS))
758             assertTrue(releaseLatch2.await(3000, TimeUnit.MILLISECONDS))
759             Color.RED == bitmap.getPixel(rect.left, rect.top)
760         }
761     }
762 
763     @Test
764     fun testTransactionSetVisibility_show() {
765         verifySurfaceControlTest({ surfaceView ->
766             val scCompat =
767                 SurfaceControlCompat.Builder()
768                     .setParent(surfaceView)
769                     .setName("SurfaceControlCompatTest")
770                     .build()
771 
772             // Buffer colorspace is RGBA, so Color.BLUE will be visually Red
773             val buffer =
774                 SurfaceControlUtils.getSolidBuffer(
775                     SurfaceControlWrapperTestActivity.DEFAULT_WIDTH,
776                     SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT,
777                     Color.BLUE
778                 )
779             assertNotNull(buffer)
780 
781             SurfaceControlCompat.Transaction()
782                 .setBuffer(scCompat, buffer)
783                 .setVisibility(scCompat, true)
784         }) { bitmap, rect ->
785             Color.RED == bitmap.getPixel(rect.left, rect.top)
786         }
787     }
788 
789     @Test
790     fun testTransactionSetVisibility_hide() {
791         verifySurfaceControlTest({ surfaceView ->
792             val scCompat =
793                 SurfaceControlCompat.Builder()
794                     .setParent(surfaceView)
795                     .setName("SurfaceControlCompatTest")
796                     .build()
797 
798             // Buffer colorspace is RGBA, so Color.BLUE will be visually Red
799             val buffer =
800                 SurfaceControlUtils.getSolidBuffer(
801                     SurfaceControlWrapperTestActivity.DEFAULT_WIDTH,
802                     SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT,
803                     Color.BLUE
804                 )
805             assertNotNull(buffer)
806 
807             SurfaceControlCompat.Transaction()
808                 .setBuffer(scCompat, buffer)
809                 .setVisibility(scCompat, false)
810         }) { bitmap, rect ->
811             Color.BLACK == bitmap.getPixel(rect.left, rect.top)
812         }
813     }
814 
815     @Test
816     fun testTransactionSetLayer_zero() {
817         verifySurfaceControlTest({ surfaceView ->
818             val scCompat1 =
819                 SurfaceControlCompat.Builder()
820                     .setParent(surfaceView)
821                     .setName("SurfaceControlCompatTest")
822                     .build()
823             val scCompat2 =
824                 SurfaceControlCompat.Builder()
825                     .setParent(surfaceView)
826                     .setName("SurfaceControlCompatTest")
827                     .build()
828 
829             // Buffer colorspace is RGBA, so Color.BLUE will be visually Red
830             SurfaceControlCompat.Transaction()
831                 .setLayer(scCompat1, 1)
832                 .setBuffer(
833                     scCompat1,
834                     SurfaceControlUtils.getSolidBuffer(
835                         SurfaceControlWrapperTestActivity.DEFAULT_WIDTH,
836                         SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT,
837                         Color.BLUE
838                     )
839                 )
840                 .setVisibility(scCompat1, true)
841                 .setLayer(scCompat2, 0)
842                 .setBuffer(
843                     scCompat2,
844                     SurfaceControlUtils.getSolidBuffer(
845                         SurfaceControlWrapperTestActivity.DEFAULT_WIDTH,
846                         SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT,
847                         Color.GREEN
848                     )
849                 )
850                 .setVisibility(scCompat2, true)
851         }) { bitmap, rect ->
852             Color.RED == bitmap.getPixel(rect.left, rect.top)
853         }
854     }
855 
856     @Test
857     fun testTransactionSetLayer_positive() {
858         verifySurfaceControlTest({ surfaceView ->
859             val scCompat1 =
860                 SurfaceControlCompat.Builder()
861                     .setParent(surfaceView)
862                     .setName("SurfaceControlCompatTest")
863                     .build()
864             val scCompat2 =
865                 SurfaceControlCompat.Builder()
866                     .setParent(surfaceView)
867                     .setName("SurfaceControlCompatTest")
868                     .build()
869 
870             // Buffer colorspace is RGBA, so Color.BLUE will be visually Red
871             SurfaceControlCompat.Transaction()
872                 .setLayer(scCompat1, 1)
873                 .setBuffer(
874                     scCompat1,
875                     SurfaceControlUtils.getSolidBuffer(
876                         SurfaceControlWrapperTestActivity.DEFAULT_WIDTH,
877                         SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT,
878                         Color.GREEN
879                     )
880                 )
881                 .setVisibility(scCompat1, true)
882                 .setLayer(scCompat2, 24)
883                 .setBuffer(
884                     scCompat2,
885                     SurfaceControlUtils.getSolidBuffer(
886                         SurfaceControlWrapperTestActivity.DEFAULT_WIDTH,
887                         SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT,
888                         Color.BLUE
889                     )
890                 )
891                 .setVisibility(scCompat2, true)
892         }) { bitmap, rect ->
893             Color.RED == bitmap.getPixel(rect.left, rect.top)
894         }
895     }
896 
897     @Test
898     fun testTransactionSetLayer_negative() {
899         verifySurfaceControlTest({ surfaceView ->
900             val scCompat1 =
901                 SurfaceControlCompat.Builder()
902                     .setParent(surfaceView)
903                     .setName("SurfaceControlCompatTest")
904                     .build()
905             val scCompat2 =
906                 SurfaceControlCompat.Builder()
907                     .setParent(surfaceView)
908                     .setName("SurfaceControlCompatTest")
909                     .build()
910 
911             // Buffer colorspace is RGBA, so Color.BLUE will be visually Red
912             SurfaceControlCompat.Transaction()
913                 .setLayer(scCompat1, 1)
914                 .setBuffer(
915                     scCompat1,
916                     SurfaceControlUtils.getSolidBuffer(
917                         SurfaceControlWrapperTestActivity.DEFAULT_WIDTH,
918                         SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT,
919                         Color.BLUE
920                     )
921                 )
922                 .setVisibility(scCompat1, true)
923                 .setLayer(scCompat2, -7)
924                 .setBuffer(
925                     scCompat2,
926                     SurfaceControlUtils.getSolidBuffer(
927                         SurfaceControlWrapperTestActivity.DEFAULT_WIDTH,
928                         SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT,
929                         Color.GREEN
930                     )
931                 )
932                 .setVisibility(scCompat2, true)
933         }) { bitmap, rect ->
934             Color.RED == bitmap.getPixel(rect.left, rect.top)
935         }
936     }
937 
938     @Test
939     fun testTransactionSetDamageRegion_all() {
940         verifySurfaceControlTest({ surfaceView ->
941             val scCompat =
942                 SurfaceControlCompat.Builder()
943                     .setParent(surfaceView)
944                     .setName("SurfaceControlCompatTest")
945                     .build()
946 
947             // Buffer colorspace is RGBA, so Color.BLUE will be visually Red
948             SurfaceControlCompat.Transaction()
949                 .setDamageRegion(
950                     scCompat,
951                     Region(
952                         0,
953                         0,
954                         SurfaceControlWrapperTestActivity.DEFAULT_WIDTH,
955                         SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT
956                     )
957                 )
958                 .setBuffer(
959                     scCompat,
960                     SurfaceControlUtils.getSolidBuffer(
961                         SurfaceControlWrapperTestActivity.DEFAULT_WIDTH,
962                         SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT,
963                         Color.BLUE
964                     )
965                 )
966                 .setVisibility(scCompat, true)
967         }) { bitmap, rect ->
968             Color.RED == bitmap.getPixel(rect.left, rect.top)
969         }
970     }
971 
972     @Test
973     fun testTransactionSetDamageRegion_null() {
974         verifySurfaceControlTest({ surfaceView ->
975             val scCompat =
976                 SurfaceControlCompat.Builder()
977                     .setParent(surfaceView)
978                     .setName("SurfaceControlCompatTest")
979                     .build()
980 
981             // Buffer colorspace is RGBA, so Color.BLUE will be visually Red
982             SurfaceControlCompat.Transaction()
983                 .setDamageRegion(scCompat, null)
984                 .setBuffer(
985                     scCompat,
986                     SurfaceControlUtils.getSolidBuffer(
987                         SurfaceControlWrapperTestActivity.DEFAULT_WIDTH,
988                         SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT,
989                         Color.BLUE
990                     )
991                 )
992                 .setVisibility(scCompat, true)
993         }) { bitmap, rect ->
994             Color.RED == bitmap.getPixel(rect.left, rect.top)
995         }
996     }
997 
998     @Test
999     fun testTransactionSetBufferTransparency_opaque() {
1000         verifySurfaceControlTest({ surfaceView ->
1001             val scCompat =
1002                 SurfaceControlCompat.Builder()
1003                     .setParent(surfaceView)
1004                     .setName("SurfaceControlCompatTest")
1005                     .build()
1006 
1007             // Buffer colorspace is RGBA, so Color.BLUE will be visually Red
1008             val buffer =
1009                 SurfaceControlUtils.getSolidBuffer(
1010                     SurfaceControlWrapperTestActivity.DEFAULT_WIDTH,
1011                     SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT,
1012                     Color.BLUE
1013                 )
1014 
1015             SurfaceControlCompat.Transaction()
1016                 .setBuffer(scCompat, buffer)
1017                 .setVisibility(scCompat, true)
1018                 .setOpaque(scCompat, true)
1019         }) { bitmap, rect ->
1020             Color.RED == bitmap.getPixel(rect.left, rect.top)
1021         }
1022     }
1023 
1024     @Test
1025     fun testTransactionSetNullBuffer() {
1026         verifySurfaceControlTest({ surfaceView ->
1027             val scCompat =
1028                 SurfaceControlCompat.Builder()
1029                     .setParent(surfaceView)
1030                     .setName("SurfaceControlCompatTest")
1031                     .build()
1032 
1033             // Buffer colorspace is RGBA, so Color.BLUE will be visually Red
1034             val buffer =
1035                 SurfaceControlUtils.getSolidBuffer(
1036                     SurfaceControlWrapperTestActivity.DEFAULT_WIDTH,
1037                     SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT,
1038                     Color.BLUE
1039                 )
1040             SurfaceControlCompat.Transaction()
1041                 .setBuffer(scCompat, buffer)
1042                 .setOpaque(scCompat, false)
1043                 .commit()
1044 
1045             SurfaceControlCompat.Transaction().setBuffer(scCompat, null)
1046         }) { bitmap, rect ->
1047             Color.BLACK == bitmap.getPixel(rect.left, rect.top)
1048         }
1049     }
1050 
1051     @Test
1052     fun testTransactionSetAlpha_0_0() {
1053         verifySurfaceControlTest({ surfaceView ->
1054             val scCompat =
1055                 SurfaceControlCompat.Builder()
1056                     .setParent(surfaceView)
1057                     .setName("SurfaceControlCompatTest")
1058                     .build()
1059 
1060             // Buffer colorspace is RGBA, so Color.BLUE will be visually Red
1061             val buffer =
1062                 SurfaceControlUtils.getSolidBuffer(
1063                     SurfaceControlWrapperTestActivity.DEFAULT_WIDTH,
1064                     SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT,
1065                     Color.BLUE
1066                 )
1067             SurfaceControlCompat.Transaction()
1068                 .setBuffer(scCompat, buffer)
1069                 .setOpaque(scCompat, false)
1070                 .setAlpha(scCompat, 0.0f)
1071         }) { bitmap, rect ->
1072             Color.BLACK == bitmap.getPixel(rect.left, rect.top)
1073         }
1074     }
1075 
1076     @Test
1077     fun testTransactionSetAlpha_0_5() {
1078         verifySurfaceControlTest({ surfaceView ->
1079             val scCompat =
1080                 SurfaceControlCompat.Builder()
1081                     .setParent(surfaceView)
1082                     .setName("SurfaceControlCompatTest")
1083                     .build()
1084 
1085             // Buffer colorspace is RGBA, so Color.BLUE will be visually Red
1086             val buffer =
1087                 SurfaceControlUtils.getSolidBuffer(
1088                     SurfaceControlWrapperTestActivity.DEFAULT_WIDTH,
1089                     SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT,
1090                     Color.BLUE
1091                 )
1092             SurfaceControlCompat.Transaction()
1093                 .setBuffer(scCompat, buffer)
1094                 .setVisibility(scCompat, true)
1095                 .setAlpha(scCompat, 0.5f)
1096         }) { bitmap, rect ->
1097             val left = rect.left
1098             val top = rect.top
1099             val fConnector: ColorSpace.Connector =
1100                 ColorSpace.connect(ColorSpace.get(ColorSpace.Named.SRGB), bitmap.colorSpace!!)
1101 
1102             val red = fConnector.transform(1.0f, 0.0f, 0.0f)
1103             val black = fConnector.transform(0.0f, 0.0f, 0.0f)
1104             val expectedResult =
1105                 Color.valueOf(red[0], red[1], red[2], 0.5f)
1106                     .compositeOver(Color.valueOf(black[0], black[1], black[2], 1.0f))
1107 
1108             (Math.abs(expectedResult.red() - bitmap.getColor(left, top).red()) < 2.5e-3f) &&
1109                 (Math.abs(expectedResult.green() - bitmap.getColor(left, top).green()) < 2.5e-3f) &&
1110                 (Math.abs(expectedResult.blue() - bitmap.getColor(left, top).blue()) < 2.5e-3f)
1111         }
1112     }
1113 
1114     @Test
1115     fun testTransactionSetAlpha_1_0() {
1116         verifySurfaceControlTest({ surfaceView ->
1117             val scCompat =
1118                 SurfaceControlCompat.Builder()
1119                     .setParent(surfaceView)
1120                     .setName("SurfaceControlCompatTest")
1121                     .build()
1122 
1123             // Buffer colorspace is RGBA, so Color.BLUE will be visually Red
1124             val buffer =
1125                 SurfaceControlUtils.getSolidBuffer(
1126                     SurfaceControlWrapperTestActivity.DEFAULT_WIDTH,
1127                     SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT,
1128                     Color.BLUE
1129                 )
1130             SurfaceControlCompat.Transaction()
1131                 .setBuffer(scCompat, buffer)
1132                 .setVisibility(scCompat, true)
1133                 .setAlpha(scCompat, 1.0f)
1134         }) { bitmap, rect ->
1135             Color.RED == bitmap.getPixel(rect.left, rect.top)
1136         }
1137     }
1138 
1139     @Test
1140     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
1141     fun testTransactionSetCrop_null() {
1142         verifySurfaceControlTest({ surfaceView ->
1143             val scCompat =
1144                 SurfaceControlCompat.Builder()
1145                     .setParent(surfaceView)
1146                     .setName("SurfaceControlCompatTest")
1147                     .build()
1148 
1149             // Buffer colorspace is RGBA, so Color.BLUE will be visually Red
1150             val buffer =
1151                 SurfaceControlUtils.getSolidBuffer(
1152                     SurfaceControlWrapperTestActivity.DEFAULT_WIDTH,
1153                     SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT,
1154                     Color.BLUE
1155                 )
1156 
1157             SurfaceControlCompat.Transaction()
1158                 .setBuffer(scCompat, buffer)
1159                 .setVisibility(scCompat, true)
1160                 .setCrop(scCompat, null)
1161         }) { bitmap, rect ->
1162             SurfaceControlUtils.checkNullCrop(bitmap, intArrayOf(rect.left, rect.top))
1163         }
1164     }
1165 
1166     @Test
1167     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
1168     fun testTransactionSetCrop_standardCrop() {
1169         verifySurfaceControlTest({ surfaceView ->
1170             val scCompat =
1171                 SurfaceControlCompat.Builder()
1172                     .setParent(surfaceView)
1173                     .setName("SurfaceControlCompatTest")
1174                     .build()
1175 
1176             // Buffer colorspace is RGBA, so Color.BLUE will be visually Red
1177             val buffer =
1178                 SurfaceControlUtils.getSolidBuffer(
1179                     SurfaceControlWrapperTestActivity.DEFAULT_WIDTH,
1180                     SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT,
1181                     Color.BLUE
1182                 )
1183 
1184             SurfaceControlCompat.Transaction()
1185                 .setBuffer(scCompat, buffer)
1186                 .setVisibility(scCompat, true)
1187                 .setCrop(scCompat, Rect(20, 30, 90, 60))
1188         }) { bitmap, rect ->
1189             SurfaceControlUtils.checkStandardCrop(bitmap, intArrayOf(rect.left, rect.top))
1190         }
1191     }
1192 
1193     @Test
1194     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
1195     fun testTransactionSetCrop_standardThenNullCrop() {
1196         val destroyLatch = CountDownLatch(1)
1197         val listener = TransactionOnCommitListener()
1198         var scCompat: SurfaceControlCompat? = null
1199         var surfaceView: SurfaceView? = null
1200         val scenario =
1201             ActivityScenario.launch(SurfaceControlWrapperTestActivity::class.java).onActivity {
1202                 it.setDestroyCallback { destroyLatch.countDown() }
1203                 surfaceView = it.mSurfaceView
1204                 val callback =
1205                     object : SurfaceHolderCallback() {
1206                         override fun surfaceCreated(sh: SurfaceHolder) {
1207                             scCompat =
1208                                 SurfaceControlCompat.Builder()
1209                                     .setParent(it.getSurfaceView())
1210                                     .setName("SurfaceControlCompatTest")
1211                                     .build()
1212 
1213                             // Buffer colorspace is RGBA, so Color.BLUE will be visually Red
1214                             val buffer =
1215                                 SurfaceControlUtils.getSolidBuffer(
1216                                     SurfaceControlWrapperTestActivity.DEFAULT_WIDTH,
1217                                     SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT,
1218                                     Color.BLUE
1219                                 )
1220 
1221                             SurfaceControlCompat.Transaction()
1222                                 .addTransactionCommittedListener(executor!!, listener)
1223                                 .setBuffer(scCompat!!, buffer)
1224                                 .setVisibility(scCompat!!, true)
1225                                 .setCrop(scCompat!!, Rect(20, 30, 90, 60))
1226                                 .commit()
1227                         }
1228                     }
1229 
1230                 it.addSurface(it.mSurfaceView, callback)
1231             }
1232 
1233         assertTrue(listener.mLatch.await(3000, TimeUnit.MILLISECONDS))
1234 
1235         try {
1236             SurfaceControlUtils.validateOutput { bitmap ->
1237                 val coord = intArrayOf(0, 0)
1238                 surfaceView!!.getLocationOnScreen(coord)
1239                 SurfaceControlUtils.checkStandardCrop(bitmap, coord)
1240             }
1241 
1242             // Buffer colorspace is RGBA, so Color.BLUE will be visually Red
1243             val buffer =
1244                 SurfaceControlUtils.getSolidBuffer(
1245                     SurfaceControlWrapperTestActivity.DEFAULT_WIDTH,
1246                     SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT,
1247                     Color.BLUE
1248                 )
1249 
1250             val cropLatchListener = TransactionOnCommitListener()
1251             SurfaceControlCompat.Transaction()
1252                 .addTransactionCommittedListener(executor!!, cropLatchListener)
1253                 .setBuffer(scCompat!!, buffer)
1254                 .setVisibility(scCompat!!, true)
1255                 .setCrop(scCompat!!, Rect(0, 0, 0, 0))
1256                 .commit()
1257 
1258             assertTrue(cropLatchListener.mLatch.await(3000, TimeUnit.MILLISECONDS))
1259 
1260             SurfaceControlUtils.validateOutput { bitmap ->
1261                 val coord = intArrayOf(0, 0)
1262                 surfaceView!!.getLocationOnScreen(coord)
1263                 SurfaceControlUtils.checkNullCrop(bitmap, coord)
1264             }
1265         } finally {
1266             scenario.moveToState(Lifecycle.State.DESTROYED)
1267             assertTrue(destroyLatch.await(3000, TimeUnit.MILLISECONDS))
1268         }
1269     }
1270 
1271     @Test
1272     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
1273     fun testTransactionSetPosition() {
1274         verifySurfaceControlTest({ surfaceView ->
1275             val scCompat =
1276                 SurfaceControlCompat.Builder()
1277                     .setParent(surfaceView)
1278                     .setName("SurfaceControlCompatTest")
1279                     .build()
1280 
1281             // Buffer colorspace is RGBA, so Color.BLUE will be visually Red
1282             val buffer =
1283                 SurfaceControlUtils.getSolidBuffer(
1284                     SurfaceControlWrapperTestActivity.DEFAULT_WIDTH,
1285                     SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT,
1286                     Color.BLUE
1287                 )
1288 
1289             SurfaceControlCompat.Transaction()
1290                 .setBuffer(scCompat, buffer)
1291                 .setVisibility(scCompat, true)
1292                 .setPosition(scCompat, 30f, 30f)
1293         }) { bitmap, rect ->
1294             val left = rect.left
1295             val top = rect.top
1296             Color.BLACK ==
1297                 bitmap.getPixel(
1298                     left + SurfaceControlWrapperTestActivity.DEFAULT_WIDTH / 2,
1299                     top + 29
1300                 ) &&
1301                 Color.BLACK ==
1302                     bitmap.getPixel(
1303                         left + 29,
1304                         top + SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT / 2
1305                     ) &&
1306                 Color.RED == bitmap.getPixel(left + 30, top + 30)
1307         }
1308     }
1309 
1310     @Test
1311     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
1312     fun testTransactionSetScale() {
1313         verifySurfaceControlTest({ surfaceView ->
1314             val scCompat =
1315                 SurfaceControlCompat.Builder()
1316                     .setParent(surfaceView)
1317                     .setName("SurfaceControlCompatTest")
1318                     .build()
1319 
1320             // Buffer colorspace is RGBA, so Color.BLUE will be visually Red
1321             val buffer =
1322                 SurfaceControlUtils.getSolidBuffer(
1323                     SurfaceControlWrapperTestActivity.DEFAULT_WIDTH,
1324                     SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT,
1325                     Color.BLUE
1326                 )
1327 
1328             SurfaceControlCompat.Transaction()
1329                 .setBuffer(scCompat, buffer)
1330                 .setVisibility(scCompat, true)
1331                 .setScale(scCompat, 0.5f, 0.5f)
1332         }) { bitmap, rect ->
1333             val left = rect.left
1334             val top = rect.top
1335             Color.RED == bitmap.getPixel(left, top) &&
1336                 Color.RED ==
1337                     bitmap.getPixel(
1338                         left + SurfaceControlWrapperTestActivity.DEFAULT_WIDTH / 2 - 1,
1339                         top + SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT / 2 - 1
1340                     ) &&
1341                 // Scale reduced by 50%, so should be black here
1342                 Color.BLACK ==
1343                     bitmap.getPixel(
1344                         left + SurfaceControlWrapperTestActivity.DEFAULT_WIDTH / 2,
1345                         top + SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT / 2
1346                     )
1347         }
1348     }
1349 
1350     @Test
1351     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q)
1352     fun testTransactionSetBufferTransform_identity() {
1353         verifySurfaceControlTest({ surfaceView ->
1354             val scCompat =
1355                 SurfaceControlCompat.Builder()
1356                     .setParent(surfaceView)
1357                     .setName("SurfaceControlCompatTest")
1358                     .build()
1359 
1360             // Buffer colorspace is RGBA, so Color.BLUE will be visually Red
1361             val buffer =
1362                 SurfaceControlUtils.getQuadrantBuffer(
1363                     SurfaceControlWrapperTestActivity.DEFAULT_WIDTH,
1364                     SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT,
1365                     Color.BLUE,
1366                     Color.BLACK,
1367                     Color.BLACK,
1368                     Color.BLACK
1369                 )
1370 
1371             SurfaceControlCompat.Transaction()
1372                 .setBuffer(scCompat, buffer)
1373                 .setVisibility(scCompat, true)
1374                 .setBufferTransform(scCompat, SurfaceControlCompat.BUFFER_TRANSFORM_IDENTITY)
1375         }) { bitmap, rect ->
1376             val left = rect.left
1377             val top = rect.top
1378             // Check outer bounds of square to ensure its scaled correctly
1379             Color.RED == bitmap.getPixel(left, top) &&
1380                 Color.RED ==
1381                     bitmap.getPixel(
1382                         left + SurfaceControlWrapperTestActivity.DEFAULT_WIDTH / 2 - 1,
1383                         top + SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT / 2 - 1
1384                     ) &&
1385                 Color.BLACK ==
1386                     bitmap.getPixel(
1387                         left + SurfaceControlWrapperTestActivity.DEFAULT_WIDTH / 2,
1388                         top + SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT / 2
1389                     )
1390         }
1391     }
1392 
1393     @Test
1394     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q)
1395     fun testTransactionSetBufferTransform_singleTransform() {
1396         verifySurfaceControlTest({ surfaceView ->
1397             val scCompat =
1398                 SurfaceControlCompat.Builder()
1399                     .setParent(surfaceView)
1400                     .setName("SurfaceControlCompatTest")
1401                     .build()
1402 
1403             // Buffer colorspace is RGBA, so Color.BLUE will be visually Red
1404             val buffer =
1405                 SurfaceControlUtils.getQuadrantBuffer(
1406                     SurfaceControlWrapperTestActivity.DEFAULT_WIDTH,
1407                     SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT,
1408                     Color.BLUE,
1409                     Color.BLACK,
1410                     Color.BLACK,
1411                     Color.BLACK
1412                 )
1413 
1414             SurfaceControlCompat.Transaction()
1415                 .setBuffer(scCompat, buffer)
1416                 .setVisibility(scCompat, true)
1417                 .setBufferTransform(
1418                     scCompat,
1419                     SurfaceControlCompat.BUFFER_TRANSFORM_MIRROR_HORIZONTAL
1420                 )
1421         }) { bitmap, rect ->
1422             // Ensure it actually rotated by checking its outer bounds are black
1423             Color.BLACK ==
1424                 bitmap.getPixel(
1425                     rect.left + SurfaceControlWrapperTestActivity.DEFAULT_WIDTH / 2 - 1,
1426                     rect.top + SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT / 4 - 1
1427                 ) &&
1428                 Color.BLACK ==
1429                     bitmap.getPixel(
1430                         rect.left + SurfaceControlWrapperTestActivity.DEFAULT_WIDTH * 3 / 4,
1431                         rect.top + SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT / 2
1432                     ) &&
1433                 Color.RED ==
1434                     bitmap.getPixel(
1435                         rect.left + SurfaceControlWrapperTestActivity.DEFAULT_WIDTH / 2 + 1,
1436                         rect.top + SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT / 2 - 1
1437                     )
1438         }
1439     }
1440 
1441     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU)
1442     @Test
1443     fun testSurfaceTransactionCommitOnDraw() {
1444         verifySurfaceControlTest({ surfaceView ->
1445             val scCompat =
1446                 SurfaceControlCompat.Builder()
1447                     .setParent(surfaceView)
1448                     .setName("SurfaceControlCompatTest")
1449                     .build()
1450 
1451             // Buffer colorspace is RGBA, so Color.BLUE will be visually Red
1452             val buffer =
1453                 SurfaceControlUtils.getSolidBuffer(
1454                     SurfaceControlWrapperTestActivity.DEFAULT_WIDTH,
1455                     SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT,
1456                     Color.BLUE
1457                 )
1458 
1459             SurfaceControlCompat.Transaction()
1460                 .setBuffer(scCompat, buffer)
1461                 .setVisibility(scCompat, true)
1462                 .setAlpha(scCompat, 1.0f)
1463                 .apply { commitTransactionOnDraw(surfaceView.rootSurfaceControl!!) }
1464         }) { bitmap, rect ->
1465             Color.RED == bitmap.getPixel(rect.left, rect.top)
1466         }
1467     }
1468 
1469     @SuppressLint("NewApi")
1470     @SdkSuppress(maxSdkVersion = Build.VERSION_CODES.TIRAMISU)
1471     @Test
1472     fun testSetExtendedRangeBrightnessThrowsOnUnsupportedPlatforms() {
1473         ActivityScenario.launch(SurfaceControlWrapperTestActivity::class.java)
1474             .moveToState(Lifecycle.State.CREATED)
1475             .onActivity {
1476                 val callback =
1477                     object : SurfaceHolderCallback() {
1478                         override fun surfaceCreated(sh: SurfaceHolder) {
1479 
1480                             assertThrows(UnsupportedOperationException::class.java) {
1481                                 val surfaceControl =
1482                                     SurfaceControlCompat.Builder()
1483                                         .setName("testSurfaceControl")
1484                                         .setParent(it.mSurfaceView)
1485                                         .build()
1486                                 SurfaceControlCompat.Transaction()
1487                                     .setExtendedRangeBrightness(surfaceControl, 1.0f, 2.0f)
1488                                     .commit()
1489                             }
1490                         }
1491                     }
1492 
1493                 it.addSurface(it.mSurfaceView, callback)
1494             }
1495     }
1496 
1497     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q)
1498     @Test
1499     fun testSetFrameRate120WithDefaultCompatibilityAndAlwaysChangeStrategy() {
1500         testFrameRate(
1501             120f,
1502             SurfaceControlCompat.FRAME_RATE_COMPATIBILITY_DEFAULT,
1503             SurfaceControlCompat.CHANGE_FRAME_RATE_ALWAYS
1504         )
1505     }
1506 
1507     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q)
1508     @Test
1509     fun testSetFrameRateNegativeWithDefaultCompatibilityAndAlwaysChangeStrategy() {
1510         testFrameRate(
1511             -50f,
1512             SurfaceControlCompat.FRAME_RATE_COMPATIBILITY_DEFAULT,
1513             SurfaceControlCompat.CHANGE_FRAME_RATE_ALWAYS
1514         )
1515     }
1516 
1517     @SuppressLint("NewApi")
1518     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q)
1519     @Test
1520     fun testSetFrameRateZeroWithDefaultCompatibilityAndAlwaysChangeStrategy() {
1521         testFrameRate(
1522             0f,
1523             SurfaceControlCompat.FRAME_RATE_COMPATIBILITY_DEFAULT,
1524             SurfaceControlCompat.CHANGE_FRAME_RATE_ALWAYS
1525         )
1526     }
1527 
1528     @SuppressLint("NewApi")
1529     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q)
1530     @Test
1531     fun testSetFrameRateInvalidCompatibility() {
1532         testFrameRate(120f, 42, SurfaceControlCompat.CHANGE_FRAME_RATE_ALWAYS)
1533     }
1534 
1535     @SuppressLint("NewApi")
1536     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q)
1537     @Test
1538     fun testSetFrameRateInvalidStrategy() {
1539         testFrameRate(120f, SurfaceControlCompat.FRAME_RATE_COMPATIBILITY_DEFAULT, 108)
1540     }
1541 
1542     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.Q)
1543     @Test
1544     fun testClearFrameRate() {
1545         ActivityScenario.launch(SurfaceControlWrapperTestActivity::class.java).onActivity {
1546             val callback =
1547                 object : SurfaceHolderCallback() {
1548                     override fun surfaceCreated(sh: SurfaceHolder) {
1549 
1550                         val surfaceControl =
1551                             SurfaceControlCompat.Builder()
1552                                 .setName("testSurfaceControl")
1553                                 .setParent(it.mSurfaceView)
1554                                 .build()
1555                         SurfaceControlCompat.Transaction().clearFrameRate(surfaceControl).commit()
1556                     }
1557                 }
1558 
1559             it.addSurface(it.mSurfaceView, callback)
1560         }
1561     }
1562 
1563     private fun testFrameRate(frameRate: Float, compatibility: Int, strategy: Int) {
1564         ActivityScenario.launch(SurfaceControlWrapperTestActivity::class.java).onActivity {
1565             val callback =
1566                 object : SurfaceHolderCallback() {
1567                     override fun surfaceCreated(sh: SurfaceHolder) {
1568 
1569                         val surfaceControl =
1570                             SurfaceControlCompat.Builder()
1571                                 .setName("testSurfaceControl")
1572                                 .setParent(it.mSurfaceView)
1573                                 .build()
1574                         SurfaceControlCompat.Transaction()
1575                             .setFrameRate(surfaceControl, frameRate, compatibility, strategy)
1576                             .commit()
1577                     }
1578                 }
1579 
1580             it.addSurface(it.mSurfaceView, callback)
1581         }
1582     }
1583 
1584     @SuppressLint("NewApi")
1585     @SdkSuppress(maxSdkVersion = Build.VERSION_CODES.S_V2)
1586     @Test
1587     fun testSetDataSpaceThrowsOnUnsupportedPlatforms() {
1588         ActivityScenario.launch(SurfaceControlWrapperTestActivity::class.java)
1589             .moveToState(Lifecycle.State.CREATED)
1590             .onActivity {
1591                 val callback =
1592                     object : SurfaceHolderCallback() {
1593                         override fun surfaceCreated(sh: SurfaceHolder) {
1594 
1595                             assertThrows(UnsupportedOperationException::class.java) {
1596                                 val surfaceControl =
1597                                     SurfaceControlCompat.Builder()
1598                                         .setName("testSurfaceControl")
1599                                         .setParent(it.mSurfaceView)
1600                                         .build()
1601 
1602                                 val extendedDataspace =
1603                                     DataSpace.pack(
1604                                         DataSpace.STANDARD_BT709,
1605                                         DataSpace.TRANSFER_SRGB,
1606                                         DataSpace.RANGE_EXTENDED
1607                                     )
1608                                 SurfaceControlCompat.Transaction()
1609                                     .setDataSpace(surfaceControl, extendedDataspace)
1610                                     .commit()
1611                             }
1612                         }
1613                     }
1614 
1615                 it.addSurface(it.mSurfaceView, callback)
1616             }
1617     }
1618 
1619     @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
1620     @Test
1621     fun testSetExtendedRangeBrightness() {
1622         val destroyLatch = CountDownLatch(1)
1623         val scenario =
1624             ActivityScenario.launch(SurfaceControlWrapperTestActivity::class.java).onActivity {
1625                 it.setDestroyCallback { destroyLatch.countDown() }
1626                 val display = it.display
1627                 assertNotNull(display)
1628                 if (display!!.isHdrSdrRatioAvailable) {
1629                     assertEquals(1.0f, display.hdrSdrRatio, .0001f)
1630                 }
1631 
1632                 it.window.attributes.screenBrightness = 0.01f
1633                 val hdrReady = CountDownLatch(1)
1634                 val listenerErrors = arrayOfNulls<Exception>(1)
1635                 if (display.isHdrSdrRatioAvailable) {
1636                     display.registerHdrSdrRatioChangedListener(
1637                         executor!!,
1638                         object : Consumer<Display?> {
1639                             var mIsRegistered = true
1640 
1641                             override fun accept(updatedDisplay: Display?) {
1642                                 try {
1643                                     assertEquals(display.displayId, updatedDisplay!!.displayId)
1644                                     assertTrue(mIsRegistered)
1645                                     if (display.hdrSdrRatio > 2f) {
1646                                         hdrReady.countDown()
1647                                         display.unregisterHdrSdrRatioChangedListener(this)
1648                                         mIsRegistered = false
1649                                     }
1650                                 } catch (e: Exception) {
1651                                     synchronized(it) {
1652                                         listenerErrors[0] = e
1653                                         hdrReady.countDown()
1654                                     }
1655                                 }
1656                             }
1657                         }
1658                     )
1659                 } else {
1660                     assertThrows(IllegalStateException::class.java) {
1661                         display.registerHdrSdrRatioChangedListener(
1662                             executor!!,
1663                             Consumer { _: Display? -> }
1664                         )
1665                     }
1666                 }
1667                 val extendedDataspace =
1668                     DataSpace.pack(
1669                         DataSpace.STANDARD_BT709,
1670                         DataSpace.TRANSFER_SRGB,
1671                         DataSpace.RANGE_EXTENDED
1672                     )
1673                 val buffer =
1674                     getSolidBuffer(
1675                         SurfaceControlWrapperTestActivity.DEFAULT_WIDTH,
1676                         SurfaceControlWrapperTestActivity.DEFAULT_HEIGHT,
1677                         Color.RED
1678                     )
1679                 val callback =
1680                     object : SurfaceHolderCallback() {
1681                         override fun surfaceCreated(sh: SurfaceHolder) {
1682                             val scCompat =
1683                                 SurfaceControlCompat.Builder()
1684                                     .setParent(it.getSurfaceView())
1685                                     .setName("SurfaceControlCompatTest")
1686                                     .build()
1687 
1688                             SurfaceControlCompat.Transaction()
1689                                 .setBuffer(scCompat, buffer)
1690                                 .setDataSpace(scCompat, extendedDataspace)
1691                                 .setExtendedRangeBrightness(scCompat, 1.0f, 3.0f)
1692                                 .setVisibility(scCompat, true)
1693                                 .commit()
1694                         }
1695                     }
1696 
1697                 it.addSurface(it.mSurfaceView, callback)
1698             }
1699 
1700         try {
1701             scenario.onActivity {
1702                 SurfaceControlUtils.validateOutput(it.window) { bitmap ->
1703                     val coord = intArrayOf(0, 0)
1704                     it.mSurfaceView.getLocationInWindow(coord)
1705                     val topLeft = bitmap.getPixel(coord[0] + 2, coord[1] + 2)
1706                     val topRight =
1707                         bitmap.getPixel(coord[0] + it.mSurfaceView.width - 2, coord[1] + 2)
1708                     val bottomLeft =
1709                         bitmap.getPixel(coord[0] + 2, coord[1] + it.mSurfaceView.height - 2)
1710                     val bottomRight =
1711                         bitmap.getPixel(
1712                             coord[0] + it.mSurfaceView.width - 2,
1713                             coord[1] + it.mSurfaceView.height - 2
1714                         )
1715 
1716                     Color.RED == topLeft &&
1717                         topLeft == topRight &&
1718                         bottomLeft == topRight &&
1719                         bottomLeft == bottomRight
1720                 }
1721             }
1722         } finally {
1723             scenario.moveToState(Lifecycle.State.DESTROYED)
1724             assertTrue(destroyLatch.await(3000, TimeUnit.MILLISECONDS))
1725         }
1726     }
1727 
1728     @RequiresApi(Build.VERSION_CODES.Q)
1729     private fun verifySurfaceControlTest(
1730         createTransaction: (SurfaceView) -> SurfaceControlCompat.Transaction,
1731         verifyOutput: (Bitmap, Rect) -> Boolean = { _, _ -> true }
1732     ) {
1733         SurfaceControlUtils.surfaceControlTestHelper(
1734             { surfaceView, latch ->
1735                 val transaction = createTransaction(surfaceView)
1736                 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
1737                     transaction.addTransactionCommittedListener(
1738                         executor!!,
1739                         object : SurfaceControlCompat.TransactionCommittedListener {
1740                             override fun onTransactionCommitted() {
1741                                 latch.countDown()
1742                             }
1743                         }
1744                     )
1745                 } else {
1746                     latch.countDown()
1747                 }
1748                 transaction.commit()
1749             },
1750             verifyOutput
1751         )
1752     }
1753 
1754     fun Color.compositeOver(background: Color): Color {
1755         val fg = this.convert(background.colorSpace)
1756 
1757         val bgA = background.alpha()
1758         val fgA = fg.alpha()
1759         val a = fgA + (bgA * (1f - fgA))
1760 
1761         val r = compositeComponent(fg.red(), background.red(), fgA, bgA, a)
1762         val g = compositeComponent(fg.green(), background.green(), fgA, bgA, a)
1763         val b = compositeComponent(fg.blue(), background.blue(), fgA, bgA, a)
1764 
1765         return Color.valueOf(r, g, b, a, background.colorSpace)
1766     }
1767 
1768     @Suppress("NOTHING_TO_INLINE")
1769     private inline fun compositeComponent(
1770         fgC: Float,
1771         bgC: Float,
1772         fgA: Float,
1773         bgA: Float,
1774         a: Float
1775     ) = if (a == 0f) 0f else ((fgC * fgA) + ((bgC * bgA) * (1f - fgA))) / a
1776 
1777     // Helper method to create and initialize an EGLManager
1778     fun createAndSetupEGLManager(eglSpec: EGLSpec = EGLSpec.V14): EGLManager {
1779         val egl = EGLManager(eglSpec)
1780         assertEquals(EGLVersion.Unknown, egl.eglVersion)
1781         assertEquals(EGL14.EGL_NO_CONTEXT, egl.eglContext)
1782 
1783         egl.initialize()
1784 
1785         val config = egl.loadConfig(EGLConfigAttributes.RGBA_8888)
1786         if (config == null) {
1787             fail("Config 888 should be supported")
1788         }
1789 
1790         egl.createContext(config!!)
1791         return egl
1792     }
1793 
1794     // Helper method to release EGLManager
1795     fun releaseEGLManager(egl: EGLManager) {
1796         egl.release()
1797         assertEquals(EGLVersion.Unknown, egl.eglVersion)
1798         assertEquals(EGL14.EGL_NO_CONTEXT, egl.eglContext)
1799     }
1800 }
1801