1 /* 2 * Copyright (C) 2016 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 package android.uirendering.cts.testclasses; 17 18 import static org.junit.Assert.assertFalse; 19 import static org.junit.Assert.assertTrue; 20 21 import android.animation.ObjectAnimator; 22 import android.graphics.Bitmap; 23 import android.graphics.Bitmap.Config; 24 import android.graphics.Canvas; 25 import android.graphics.Color; 26 import android.graphics.HardwareBufferRenderer; 27 import android.graphics.Rect; 28 import android.graphics.RenderNode; 29 import android.hardware.HardwareBuffer; 30 import android.os.Handler; 31 import android.os.Looper; 32 import android.uirendering.cts.R; 33 import android.uirendering.cts.bitmapverifiers.ColorVerifier; 34 import android.uirendering.cts.testinfrastructure.ActivityTestBase; 35 import android.uirendering.cts.testinfrastructure.CanvasClient; 36 import android.uirendering.cts.testinfrastructure.DrawActivity; 37 import android.uirendering.cts.testinfrastructure.Tracer; 38 import android.uirendering.cts.testinfrastructure.ViewInitializer; 39 import android.uirendering.cts.util.BitmapAsserter; 40 import android.view.AttachedSurfaceControl; 41 import android.view.Gravity; 42 import android.view.PixelCopy; 43 import android.view.SurfaceControl; 44 import android.view.SurfaceHolder; 45 import android.view.SurfaceView; 46 import android.view.View; 47 import android.view.animation.LinearInterpolator; 48 import android.widget.FrameLayout; 49 50 import androidx.test.filters.FlakyTest; 51 import androidx.test.filters.LargeTest; 52 import androidx.test.runner.AndroidJUnit4; 53 54 import com.android.compatibility.common.util.SynchronousPixelCopy; 55 import com.android.compatibility.common.util.WidgetTestUtils; 56 57 import org.junit.Assert; 58 import org.junit.Rule; 59 import org.junit.Test; 60 import org.junit.runner.RunWith; 61 62 import java.util.concurrent.CountDownLatch; 63 import java.util.concurrent.Executors; 64 import java.util.concurrent.TimeUnit; 65 66 @LargeTest 67 @RunWith(AndroidJUnit4.class) 68 public class SurfaceViewTests extends ActivityTestBase { 69 70 @Rule 71 public final Tracer name = new Tracer(); 72 73 static final CanvasCallback sGreenCanvasCallback = 74 new CanvasCallback((canvas, width, height) -> canvas.drawColor(Color.GREEN)); 75 static final CanvasCallback sWhiteCanvasCallback = 76 new CanvasCallback((canvas, width, height) -> canvas.drawColor(Color.WHITE)); 77 static final CanvasCallback sRedCanvasCallback = 78 new CanvasCallback((canvas, width, height) -> canvas.drawColor(Color.RED)); 79 80 private static class CanvasCallback implements SurfaceHolder.Callback { 81 final CanvasClient mCanvasClient; 82 private CountDownLatch mFirstDrawLatch; 83 CanvasCallback(CanvasClient canvasClient)84 public CanvasCallback(CanvasClient canvasClient) { 85 mCanvasClient = canvasClient; 86 } 87 88 @Override surfaceCreated(SurfaceHolder holder)89 public void surfaceCreated(SurfaceHolder holder) { 90 } 91 92 @Override surfaceChanged(SurfaceHolder holder, int format, int width, int height)93 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { 94 Canvas canvas = holder.lockCanvas(); 95 mCanvasClient.draw(canvas, width, height); 96 holder.unlockCanvasAndPost(canvas); 97 98 if (mFirstDrawLatch != null) { 99 mFirstDrawLatch.countDown(); 100 } 101 } 102 103 @Override surfaceDestroyed(SurfaceHolder holder)104 public void surfaceDestroyed(SurfaceHolder holder) { 105 } 106 setFence(CountDownLatch fence)107 public void setFence(CountDownLatch fence) { 108 mFirstDrawLatch = fence; 109 } 110 } 111 createInfiniteAnimator(Object target, String prop, float start, float end)112 static ObjectAnimator createInfiniteAnimator(Object target, String prop, 113 float start, float end) { 114 ObjectAnimator a = ObjectAnimator.ofFloat(target, prop, start, end); 115 a.setRepeatMode(ObjectAnimator.REVERSE); 116 a.setRepeatCount(ObjectAnimator.INFINITE); 117 a.setDuration(200); 118 a.setInterpolator(new LinearInterpolator()); 119 a.start(); 120 return a; 121 } 122 private final Screenshotter mScreenshotter = testPositionInfo -> { 123 Bitmap source = getInstrumentation().getUiAutomation().takeScreenshot(); 124 return Bitmap.createBitmap(source, 125 testPositionInfo.screenOffset.x, testPositionInfo.screenOffset.y, 126 TEST_WIDTH, TEST_HEIGHT); 127 }; 128 129 // waitForRedraw checks that HWUI finished drawing but SurfaceFlinger may be backpressured, so 130 // synchronizing by applying no-op transactions with UI draws instead. waitForScreenshottable()131 private void waitForScreenshottable() throws InterruptedException { 132 AttachedSurfaceControl rootSurfaceControl = 133 getActivity().getWindow().getRootSurfaceControl(); 134 135 CountDownLatch latch = new CountDownLatch(1); 136 SurfaceControl stub = new SurfaceControl.Builder().setName("test").build(); 137 rootSurfaceControl.applyTransactionOnDraw( 138 rootSurfaceControl.buildReparentTransaction(stub)); 139 rootSurfaceControl.applyTransactionOnDraw( 140 new SurfaceControl.Transaction().reparent(stub, null) 141 .addTransactionCommittedListener(Runnable::run, latch::countDown)); 142 getActivity().waitForRedraw(); 143 assertTrue(latch.await(5, TimeUnit.SECONDS)); 144 } 145 146 @FlakyTest(bugId = 244426304) 147 @Test testMovingWhiteSurfaceView()148 public void testMovingWhiteSurfaceView() { 149 // A moving SurfaceViews with white content against a white background should be invisible 150 ViewInitializer initializer = new ViewInitializer() { 151 ObjectAnimator mAnimator; 152 @Override 153 public void initializeView(View view) { 154 FrameLayout root = (FrameLayout) view.findViewById(R.id.frame_layout); 155 mAnimator = createInfiniteAnimator(root, "translationY", 0, 50); 156 157 SurfaceView surfaceViewA = new SurfaceView(view.getContext()); 158 surfaceViewA.getHolder().addCallback(sWhiteCanvasCallback); 159 root.addView(surfaceViewA, new FrameLayout.LayoutParams( 160 90, 40, Gravity.START | Gravity.TOP)); 161 } 162 @Override 163 public void teardownView() { 164 mAnimator.cancel(); 165 } 166 }; 167 createTest() 168 .addLayout(R.layout.frame_layout, initializer, true) 169 .withScreenshotter(mScreenshotter) 170 .runWithAnimationVerifier(new ColorVerifier(Color.WHITE, 0 /* zero tolerance */)); 171 } 172 173 private static class SurfaceViewHelper implements ViewInitializer, Screenshotter, SurfaceHolder.Callback { 174 private final CanvasClient mCanvasClient; 175 private final CountDownLatch mFence = new CountDownLatch(1); 176 private SurfaceView mSurfaceView = null; 177 private boolean mHasSurface = false; 178 SurfaceViewHelper(CanvasClient canvasClient)179 public SurfaceViewHelper(CanvasClient canvasClient) { 180 mCanvasClient = canvasClient; 181 } 182 183 @Override takeScreenshot(TestPositionInfo testPositionInfo)184 public Bitmap takeScreenshot(TestPositionInfo testPositionInfo) { 185 SynchronousPixelCopy copy = new SynchronousPixelCopy(); 186 Bitmap dest = Bitmap.createBitmap( 187 TEST_WIDTH, TEST_HEIGHT, Config.ARGB_8888); 188 Rect srcRect = new Rect(0, 0, TEST_WIDTH, TEST_HEIGHT); 189 int copyResult = copy.request(mSurfaceView, srcRect, dest); 190 Assert.assertEquals(PixelCopy.SUCCESS, copyResult); 191 return dest; 192 } 193 194 @Override initializeView(View view)195 public void initializeView(View view) { 196 FrameLayout root = (FrameLayout) view.findViewById(R.id.frame_layout); 197 mSurfaceView = new SurfaceView(view.getContext()); 198 mSurfaceView.getHolder().addCallback(this); 199 onSurfaceViewCreated(mSurfaceView); 200 root.addView(mSurfaceView, new FrameLayout.LayoutParams( 201 FrameLayout.LayoutParams.MATCH_PARENT, 202 FrameLayout.LayoutParams.MATCH_PARENT)); 203 } 204 getSurfaceView()205 public SurfaceView getSurfaceView() { 206 return mSurfaceView; 207 } 208 209 hasSurface()210 public boolean hasSurface() { 211 return mHasSurface; 212 } 213 214 onSurfaceViewCreated(SurfaceView surfaceView)215 public void onSurfaceViewCreated(SurfaceView surfaceView) { 216 217 } 218 219 @Override surfaceCreated(SurfaceHolder holder)220 public void surfaceCreated(SurfaceHolder holder) { 221 mHasSurface = true; 222 } 223 224 @Override surfaceChanged(SurfaceHolder holder, int format, int width, int height)225 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { 226 // TODO: Remove the post() which is a temporary workaround for b/32484713 227 mSurfaceView.post(() -> { 228 Canvas canvas = holder.lockHardwareCanvas(); 229 mCanvasClient.draw(canvas, width, height); 230 holder.unlockCanvasAndPost(canvas); 231 mFence.countDown(); 232 }); 233 } 234 235 @Override surfaceDestroyed(SurfaceHolder holder)236 public void surfaceDestroyed(SurfaceHolder holder) { 237 mHasSurface = false; 238 } 239 getFence()240 public CountDownLatch getFence() { 241 return mFence; 242 } 243 } 244 245 @Test testSurfaceHolderHardwareCanvas()246 public void testSurfaceHolderHardwareCanvas() { 247 SurfaceViewHelper helper = new SurfaceViewHelper((canvas, width, height) -> { 248 Assert.assertNotNull(canvas); 249 Assert.assertTrue(canvas.isHardwareAccelerated()); 250 canvas.drawColor(Color.GREEN); 251 }); 252 createTest() 253 .addLayout(R.layout.frame_layout, helper, true, helper.getFence()) 254 .withScreenshotter(helper) 255 .runWithVerifier(new ColorVerifier(Color.GREEN, 0 /* zero tolerance */)); 256 } 257 258 @Test testSurfaceViewHolePunchWithLayer()259 public void testSurfaceViewHolePunchWithLayer() { 260 SurfaceViewHelper helper = new SurfaceViewHelper((canvas, width, height) -> { 261 Assert.assertNotNull(canvas); 262 Assert.assertTrue(canvas.isHardwareAccelerated()); 263 canvas.drawColor(Color.GREEN); 264 } 265 ) { 266 @Override 267 public void onSurfaceViewCreated(SurfaceView surfaceView) { 268 surfaceView.setLayerType(View.LAYER_TYPE_HARDWARE, null); 269 } 270 }; 271 createTest() 272 .addLayout(R.layout.frame_layout, helper, true, helper.getFence()) 273 .withScreenshotter(helper) 274 .runWithVerifier(new ColorVerifier(Color.GREEN, 0 /* zero tolerance */)); 275 276 } 277 278 @Test surfaceViewMediaLayer()279 public void surfaceViewMediaLayer() { 280 // Add a shared latch which will fire after both callbacks are complete. 281 CountDownLatch latch = new CountDownLatch(2); 282 sGreenCanvasCallback.setFence(latch); 283 sRedCanvasCallback.setFence(latch); 284 285 ViewInitializer initializer = new ViewInitializer() { 286 @Override 287 public void initializeView(View view) { 288 FrameLayout root = (FrameLayout) view.findViewById(R.id.frame_layout); 289 SurfaceView surfaceViewA = new SurfaceView(view.getContext()); 290 surfaceViewA.setZOrderMediaOverlay(true); 291 surfaceViewA.getHolder().addCallback(sRedCanvasCallback); 292 293 root.addView(surfaceViewA, new FrameLayout.LayoutParams( 294 FrameLayout.LayoutParams.MATCH_PARENT, 295 FrameLayout.LayoutParams.MATCH_PARENT)); 296 297 SurfaceView surfaceViewB = new SurfaceView(view.getContext()); 298 surfaceViewB.getHolder().addCallback(sGreenCanvasCallback); 299 300 root.addView(surfaceViewB, new FrameLayout.LayoutParams( 301 FrameLayout.LayoutParams.MATCH_PARENT, 302 FrameLayout.LayoutParams.MATCH_PARENT)); 303 } 304 }; 305 306 createTest() 307 .addLayout(R.layout.frame_layout, initializer, true, latch) 308 .withScreenshotter(mScreenshotter) 309 // The red layer is the media overlay, so it must be on top. 310 .runWithVerifier(new ColorVerifier(Color.RED, 0 /* zero tolerance */)); 311 } 312 313 @Test surfaceViewBlendZAbove()314 public void surfaceViewBlendZAbove() { 315 SurfaceViewHelper helper = new SurfaceViewHelper((canvas, width, height) -> { 316 Assert.assertNotNull(canvas); 317 Assert.assertTrue(canvas.isHardwareAccelerated()); 318 canvas.drawColor(Color.BLACK); 319 } 320 ) { 321 @Override 322 public void onSurfaceViewCreated(SurfaceView surfaceView) { 323 surfaceView.setAlpha(0.25f); 324 surfaceView.setZOrderOnTop(true); 325 } 326 }; 327 createTest() 328 .addLayout(R.layout.frame_layout, helper, true, helper.getFence()) 329 .withScreenshotter(mScreenshotter) 330 .runWithVerifier(new ColorVerifier( 331 Color.rgb(191, 191, 191), 1 /* blending tolerance */)); 332 } 333 334 @Test surfaceViewBlendZBelow()335 public void surfaceViewBlendZBelow() { 336 SurfaceViewHelper helper = new SurfaceViewHelper((canvas, width, height) -> { 337 Assert.assertNotNull(canvas); 338 Assert.assertTrue(canvas.isHardwareAccelerated()); 339 canvas.drawColor(Color.BLACK); 340 } 341 ) { 342 @Override 343 public void onSurfaceViewCreated(SurfaceView surfaceView) { 344 surfaceView.setAlpha(0.25f); 345 } 346 }; 347 createTest() 348 .addLayout(R.layout.frame_layout, helper, true, helper.getFence()) 349 .withScreenshotter(mScreenshotter) 350 .runWithVerifier(new ColorVerifier( 351 Color.rgb(191, 191, 191), 1 /* blending tolerance */)); 352 } 353 354 @Test surfaceViewSurfaceLifecycleFollowsVisibilityByDefault()355 public void surfaceViewSurfaceLifecycleFollowsVisibilityByDefault() { 356 SurfaceViewHelper helper = new SurfaceViewHelper((canvas, width, height) -> { 357 Assert.assertNotNull(canvas); 358 canvas.drawColor(Color.BLACK); 359 }); 360 361 DrawActivity activity = getActivity(); 362 try { 363 activity.enqueueRenderSpecAndWait(R.layout.frame_layout, null, helper, false, false); 364 assertTrue(helper.hasSurface()); 365 activity.runOnUiThread(() -> helper.getSurfaceView().setVisibility(View.INVISIBLE)); 366 activity.waitForRedraw(); 367 assertFalse(helper.hasSurface()); 368 } finally { 369 activity.reset(); 370 } 371 } 372 373 @Test surfaceViewSurfaceLifecycleFollowsVisibility()374 public void surfaceViewSurfaceLifecycleFollowsVisibility() { 375 SurfaceViewHelper helper = new SurfaceViewHelper((canvas, width, height) -> { 376 Assert.assertNotNull(canvas); 377 canvas.drawColor(Color.BLACK); 378 }); 379 380 DrawActivity activity = getActivity(); 381 try { 382 activity.enqueueRenderSpecAndWait(R.layout.frame_layout, null, helper, false, false); 383 assertTrue(helper.hasSurface()); 384 activity.runOnUiThread(() -> { 385 helper.getSurfaceView() 386 .setSurfaceLifecycle(SurfaceView.SURFACE_LIFECYCLE_FOLLOWS_VISIBILITY); 387 helper.getSurfaceView().setVisibility(View.INVISIBLE); 388 }); 389 activity.waitForRedraw(); 390 assertFalse(helper.hasSurface()); 391 } finally { 392 activity.reset(); 393 } 394 } 395 396 @Test surfaceViewSurfaceLifecycleFollowsAttachment()397 public void surfaceViewSurfaceLifecycleFollowsAttachment() { 398 SurfaceViewHelper helper = new SurfaceViewHelper((canvas, width, height) -> { 399 Assert.assertNotNull(canvas); 400 canvas.drawColor(Color.BLACK); 401 }); 402 403 DrawActivity activity = getActivity(); 404 try { 405 activity.enqueueRenderSpecAndWait(R.layout.frame_layout, null, helper, false, false); 406 assertTrue(helper.hasSurface()); 407 activity.runOnUiThread(() -> { 408 helper.getSurfaceView() 409 .setSurfaceLifecycle(SurfaceView.SURFACE_LIFECYCLE_FOLLOWS_ATTACHMENT); 410 helper.getSurfaceView().setVisibility(View.INVISIBLE); 411 }); 412 activity.waitForRedraw(); 413 assertTrue(helper.hasSurface()); 414 } finally { 415 activity.reset(); 416 } 417 } 418 419 @Test surfaceViewSurfaceLifecycleFollowsAttachmentWithOverlaps()420 public void surfaceViewSurfaceLifecycleFollowsAttachmentWithOverlaps() { 421 // Add a shared latch which will fire after both callbacks are complete. 422 CountDownLatch latch = new CountDownLatch(2); 423 sGreenCanvasCallback.setFence(latch); 424 sRedCanvasCallback.setFence(latch); 425 426 ViewInitializer initializer = new ViewInitializer() { 427 @Override 428 public void initializeView(View view) { 429 FrameLayout root = (FrameLayout) view.findViewById(R.id.frame_layout); 430 SurfaceView surfaceViewA = new SurfaceView(view.getContext()); 431 surfaceViewA.setVisibility(View.VISIBLE); 432 surfaceViewA.setSurfaceLifecycle(SurfaceView.SURFACE_LIFECYCLE_FOLLOWS_VISIBILITY); 433 surfaceViewA.getHolder().addCallback(sRedCanvasCallback); 434 435 root.addView(surfaceViewA, new FrameLayout.LayoutParams( 436 FrameLayout.LayoutParams.MATCH_PARENT, 437 FrameLayout.LayoutParams.MATCH_PARENT)); 438 439 SurfaceView surfaceViewB = new SurfaceView(view.getContext()); 440 surfaceViewB.setVisibility(View.INVISIBLE); 441 surfaceViewB.setSurfaceLifecycle(SurfaceView.SURFACE_LIFECYCLE_FOLLOWS_ATTACHMENT); 442 surfaceViewB.getHolder().addCallback(sGreenCanvasCallback); 443 444 root.addView(surfaceViewB, new FrameLayout.LayoutParams( 445 FrameLayout.LayoutParams.MATCH_PARENT, 446 FrameLayout.LayoutParams.MATCH_PARENT)); 447 } 448 }; 449 450 createTest() 451 .addLayout(R.layout.frame_layout, initializer, true, latch) 452 .withScreenshotter(mScreenshotter) 453 .runWithVerifier(new ColorVerifier(Color.RED, 0 /* zero tolerance */)); 454 } 455 456 @Test surfaceViewSurfaceLifecycleChangesFromFollowsAttachmentToFollowsVisibility()457 public void surfaceViewSurfaceLifecycleChangesFromFollowsAttachmentToFollowsVisibility() { 458 SurfaceViewHelper helper = new SurfaceViewHelper((canvas, width, height) -> { 459 Assert.assertNotNull(canvas); 460 canvas.drawColor(Color.BLACK); 461 }); 462 463 DrawActivity activity = getActivity(); 464 try { 465 activity.enqueueRenderSpecAndWait(R.layout.frame_layout, null, helper, false, false); 466 assertTrue(helper.hasSurface()); 467 activity.runOnUiThread(() -> { 468 helper.getSurfaceView() 469 .setSurfaceLifecycle(SurfaceView.SURFACE_LIFECYCLE_FOLLOWS_ATTACHMENT); 470 helper.getSurfaceView().setVisibility(View.INVISIBLE); 471 }); 472 activity.waitForRedraw(); 473 assertTrue(helper.hasSurface()); 474 activity.runOnUiThread(() -> { 475 helper.getSurfaceView() 476 .setSurfaceLifecycle(SurfaceView.SURFACE_LIFECYCLE_FOLLOWS_VISIBILITY); 477 }); 478 activity.waitForRedraw(); 479 assertFalse(helper.hasSurface()); 480 } finally { 481 activity.reset(); 482 } 483 } 484 485 @Test surfaceViewSurfaceLifecycleChangesFromFollowsVisibilityToFollowsAttachment()486 public void surfaceViewSurfaceLifecycleChangesFromFollowsVisibilityToFollowsAttachment() { 487 SurfaceViewHelper helper = new SurfaceViewHelper((canvas, width, height) -> { 488 Assert.assertNotNull(canvas); 489 canvas.drawColor(Color.BLACK); 490 }); 491 492 DrawActivity activity = getActivity(); 493 try { 494 activity.enqueueRenderSpecAndWait(R.layout.frame_layout, null, helper, false, false); 495 assertTrue(helper.hasSurface()); 496 activity.runOnUiThread(() -> { 497 helper.getSurfaceView() 498 .setSurfaceLifecycle(SurfaceView.SURFACE_LIFECYCLE_FOLLOWS_VISIBILITY); 499 helper.getSurfaceView().setVisibility(View.INVISIBLE); 500 }); 501 activity.waitForRedraw(); 502 assertFalse(helper.hasSurface()); 503 activity.runOnUiThread(() -> { 504 helper.getSurfaceView() 505 .setSurfaceLifecycle(SurfaceView.SURFACE_LIFECYCLE_FOLLOWS_ATTACHMENT); 506 }); 507 activity.waitForRedraw(); 508 assertTrue(helper.hasSurface()); 509 } finally { 510 activity.reset(); 511 } 512 } 513 514 @Test surfaceViewAppliesTransactionsToFrame()515 public void surfaceViewAppliesTransactionsToFrame() 516 throws InterruptedException { 517 SurfaceControl blueLayer = new SurfaceControl.Builder() 518 .setName("SurfaceViewTests") 519 .setHidden(false) 520 .build(); 521 SurfaceViewHelper helper = new SurfaceViewHelper((canvas, width, height) -> { 522 Assert.assertNotNull(canvas); 523 canvas.drawColor(Color.RED); 524 }); 525 526 DrawActivity activity = getActivity(); 527 try { 528 TestPositionInfo testInfo = activity 529 .enqueueRenderSpecAndWait(R.layout.frame_layout, null, helper, true, false); 530 assertTrue(helper.hasSurface()); 531 helper.getFence().await(3, TimeUnit.SECONDS); 532 CountDownLatch latch = new CountDownLatch(1); 533 CountDownLatch transactionCommitted = new CountDownLatch(1); 534 activity.runOnUiThread(() -> { 535 SurfaceControl.Transaction transaction = new SurfaceControl.Transaction() 536 .reparent(blueLayer, helper.getSurfaceView().getSurfaceControl()) 537 .setLayer(blueLayer, 1) 538 .addTransactionCommittedListener(Runnable::run, 539 transactionCommitted::countDown); 540 541 int width = helper.getSurfaceView().getWidth(); 542 int height = helper.getSurfaceView().getHeight(); 543 long usage = HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE 544 | HardwareBuffer.USAGE_GPU_COLOR_OUTPUT 545 | HardwareBuffer.USAGE_COMPOSER_OVERLAY; 546 HardwareBuffer buffer = HardwareBuffer.create( 547 width, height, HardwareBuffer.RGBA_8888, 1, usage); 548 HardwareBufferRenderer renderer = new HardwareBufferRenderer(buffer); 549 RenderNode node = new RenderNode("content"); 550 node.setPosition(0, 0, width, height); 551 Canvas canvas = node.beginRecording(); 552 canvas.drawColor(Color.BLUE); 553 node.endRecording(); 554 renderer.setContentRoot(node); 555 Handler handler = new Handler(Looper.getMainLooper()); 556 renderer.obtainRenderRequest().draw(Executors.newSingleThreadExecutor(), result -> { 557 handler.post(() -> { 558 transaction.setBuffer(blueLayer, buffer, result.getFence()); 559 helper.getSurfaceView().applyTransactionToFrame(transaction); 560 latch.countDown(); 561 }); 562 }); 563 }); 564 565 assertTrue(latch.await(5, TimeUnit.SECONDS)); 566 // Wait for an additional second to ensure that the transaction reparenting the blue 567 // layer is not applied. 568 assertFalse(transactionCommitted.await(1, TimeUnit.SECONDS)); 569 Bitmap screenshot = mScreenshotter.takeScreenshot(testInfo); 570 BitmapAsserter asserter = 571 new BitmapAsserter(this.getClass().getSimpleName(), name.getMethodName()); 572 asserter.assertBitmapIsVerified( 573 screenshot, new ColorVerifier(Color.RED, 2), getName(), ""); 574 activity.runOnUiThread(() -> { 575 SurfaceHolder holder = helper.getSurfaceView().getHolder(); 576 Canvas canvas = holder.lockHardwareCanvas(); 577 canvas.drawColor(Color.GREEN); 578 holder.unlockCanvasAndPost(canvas); 579 }); 580 assertTrue(transactionCommitted.await(1, TimeUnit.SECONDS)); 581 screenshot = mScreenshotter.takeScreenshot(testInfo); 582 // Now that a new frame was drawn, the blue layer should be overlaid now. 583 asserter.assertBitmapIsVerified( 584 screenshot, new ColorVerifier(Color.BLUE, 2), getName(), ""); 585 } finally { 586 activity.reset(); 587 } 588 } 589 590 // Regression test for b/269113414 591 @Test surfaceViewOffscreenDoesNotPeekThrough()592 public void surfaceViewOffscreenDoesNotPeekThrough() throws InterruptedException { 593 594 // Add a shared latch which will fire after both callbacks are complete. 595 CountDownLatch latch = new CountDownLatch(2); 596 sGreenCanvasCallback.setFence(latch); 597 sRedCanvasCallback.setFence(latch); 598 599 DrawActivity activity = getActivity(); 600 601 SurfaceView surfaceViewRed = new SurfaceView(activity); 602 surfaceViewRed.getHolder().addCallback(sRedCanvasCallback); 603 SurfaceView surfaceViewGreen = new SurfaceView(activity); 604 surfaceViewGreen.setZOrderMediaOverlay(true); 605 surfaceViewGreen.getHolder().addCallback(sGreenCanvasCallback); 606 607 int width = activity.getWindow().getDecorView().getWidth(); 608 int height = activity.getWindow().getDecorView().getHeight(); 609 610 ViewInitializer initializer = (View view) -> { 611 FrameLayout root = view.findViewById(R.id.frame_layout); 612 root.addView(surfaceViewRed, new FrameLayout.LayoutParams( 613 FrameLayout.LayoutParams.MATCH_PARENT, 614 FrameLayout.LayoutParams.MATCH_PARENT)); 615 616 root.addView(surfaceViewGreen, new FrameLayout.LayoutParams( 617 FrameLayout.LayoutParams.MATCH_PARENT, 618 FrameLayout.LayoutParams.MATCH_PARENT)); 619 }; 620 621 try { 622 TestPositionInfo testInfo = activity.enqueueRenderSpecAndWait( 623 R.layout.frame_layout, null, initializer, true, false); 624 assertTrue(latch.await(5, TimeUnit.SECONDS)); 625 626 BitmapAsserter asserter = 627 new BitmapAsserter(this.getClass().getSimpleName(), name.getMethodName()); 628 629 // Layout the SurfaceView way offscreen which would cause it to get quick rejected. 630 WidgetTestUtils.runOnMainAndDrawSync(surfaceViewGreen, () -> { 631 surfaceViewGreen.layout( 632 width * 2, 633 height * 2, 634 width * 2 + TEST_WIDTH, 635 height * 2 + TEST_HEIGHT); 636 }); 637 waitForScreenshottable(); 638 Bitmap screenshot = mScreenshotter.takeScreenshot(testInfo); 639 asserter.assertBitmapIsVerified( 640 screenshot, 641 new ColorVerifier(Color.RED, 0), getName(), 642 "Verifying red SurfaceControl"); 643 } finally { 644 activity.reset(); 645 } 646 } 647 648 649 } 650