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