1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.wm; 18 19 import static android.os.Build.HW_TIMEOUT_MULTIPLIER; 20 import static android.window.SurfaceSyncGroup.TRANSACTION_READY_TIMEOUT; 21 22 import static org.junit.Assert.assertEquals; 23 import static org.junit.Assert.assertNotNull; 24 import static org.junit.Assert.assertTrue; 25 26 import android.app.Instrumentation; 27 import android.graphics.Bitmap; 28 import android.graphics.Color; 29 import android.graphics.PixelFormat; 30 import android.graphics.Rect; 31 import android.os.Handler; 32 import android.os.HandlerThread; 33 import android.platform.test.annotations.Presubmit; 34 import android.server.wm.scvh.SurfaceSyncGroupActivity; 35 import android.view.SurfaceControl; 36 import android.view.View; 37 import android.view.ViewTreeObserver; 38 import android.view.WindowManager; 39 import android.view.cts.surfacevalidator.BitmapPixelChecker; 40 import android.window.SurfaceSyncGroup; 41 42 import androidx.test.InstrumentationRegistry; 43 import androidx.test.rule.ActivityTestRule; 44 45 import org.junit.Before; 46 import org.junit.Rule; 47 import org.junit.Test; 48 49 import java.util.concurrent.CountDownLatch; 50 import java.util.concurrent.TimeUnit; 51 52 @Presubmit 53 public class SurfaceSyncGroupTests { 54 private static final String TAG = "SurfaceSyncGroupTests"; 55 56 private static final long TIMEOUT_S = HW_TIMEOUT_MULTIPLIER * 5L; 57 58 @Rule 59 public ActivityTestRule<SurfaceSyncGroupActivity> mActivityRule = new ActivityTestRule<>( 60 SurfaceSyncGroupActivity.class); 61 62 private SurfaceSyncGroupActivity mActivity; 63 64 Instrumentation mInstrumentation; 65 66 private final HandlerThread mHandlerThread = new HandlerThread("applyTransaction"); 67 private Handler mHandler; 68 69 @Before setup()70 public void setup() { 71 mActivity = mActivityRule.getActivity(); 72 mInstrumentation = InstrumentationRegistry.getInstrumentation(); 73 mHandlerThread.start(); 74 mHandler = mHandlerThread.getThreadHandler(); 75 } 76 77 @Test testOverlappingSyncsEnsureOrder_WhenTimeout()78 public void testOverlappingSyncsEnsureOrder_WhenTimeout() throws InterruptedException { 79 WindowManager.LayoutParams params = new WindowManager.LayoutParams(); 80 params.format = PixelFormat.TRANSLUCENT; 81 82 CountDownLatch secondDrawCompleteLatch = new CountDownLatch(1); 83 CountDownLatch bothSyncGroupsComplete = new CountDownLatch(2); 84 final SurfaceSyncGroup firstSsg = new SurfaceSyncGroup(TAG + "-first"); 85 final SurfaceSyncGroup secondSsg = new SurfaceSyncGroup(TAG + "-second"); 86 final SurfaceSyncGroup infiniteSsg = new SurfaceSyncGroup(TAG + "-infinite"); 87 88 SurfaceControl.Transaction t = new SurfaceControl.Transaction(); 89 t.addTransactionCommittedListener(Runnable::run, bothSyncGroupsComplete::countDown); 90 firstSsg.addTransaction(t); 91 92 View backgroundView = mActivity.getBackgroundView(); 93 firstSsg.add(backgroundView.getRootSurfaceControl(), 94 () -> mActivity.runOnUiThread(() -> backgroundView.setBackgroundColor(Color.RED))); 95 96 addSecondSyncGroup(secondSsg, secondDrawCompleteLatch, bothSyncGroupsComplete); 97 98 assertTrue("Failed to draw two frames", 99 secondDrawCompleteLatch.await(TIMEOUT_S, TimeUnit.SECONDS)); 100 101 mHandler.postDelayed(() -> { 102 // Don't add a markSyncReady for the first sync group until after it's added to another 103 // SSG to ensure the timeout is longer than the second frame's timeout. The infinite SSG 104 // will never complete to ensure it reaches the timeout, but only after the second SSG 105 // had a chance to reach its timeout. 106 infiniteSsg.add(firstSsg, null /* runnable */); 107 firstSsg.markSyncReady(); 108 }, 200); 109 110 assertTrue("Failed to wait for both SurfaceSyncGroups to apply", 111 bothSyncGroupsComplete.await(TIMEOUT_S, TimeUnit.SECONDS)); 112 113 validateScreenshot(); 114 } 115 116 @Test testOverlappingSyncsEnsureOrder_WhileHoldingTransaction()117 public void testOverlappingSyncsEnsureOrder_WhileHoldingTransaction() 118 throws InterruptedException { 119 WindowManager.LayoutParams params = new WindowManager.LayoutParams(); 120 params.format = PixelFormat.TRANSLUCENT; 121 122 CountDownLatch secondDrawCompleteLatch = new CountDownLatch(1); 123 CountDownLatch bothSyncGroupsComplete = new CountDownLatch(2); 124 125 final SurfaceSyncGroup firstSsg = new SurfaceSyncGroup(TAG + "-first", 126 transaction -> mHandler.postDelayed(() -> { 127 try { 128 assertTrue("Failed to draw two frames", 129 secondDrawCompleteLatch.await(TIMEOUT_S, TimeUnit.SECONDS)); 130 } catch (InterruptedException e) { 131 throw new RuntimeException(e); 132 } 133 transaction.apply(); 134 }, TRANSACTION_READY_TIMEOUT + 200)); 135 final SurfaceSyncGroup secondSsg = new SurfaceSyncGroup(TAG + "-second"); 136 137 SurfaceControl.Transaction t = new SurfaceControl.Transaction(); 138 t.addTransactionCommittedListener(Runnable::run, bothSyncGroupsComplete::countDown); 139 firstSsg.addTransaction(t); 140 141 View backgroundView = mActivity.getBackgroundView(); 142 firstSsg.add(backgroundView.getRootSurfaceControl(), 143 () -> mActivity.runOnUiThread(() -> backgroundView.setBackgroundColor(Color.RED))); 144 firstSsg.markSyncReady(); 145 146 addSecondSyncGroup(secondSsg, secondDrawCompleteLatch, bothSyncGroupsComplete); 147 148 assertTrue("Failed to wait for both SurfaceSyncGroups to apply", 149 bothSyncGroupsComplete.await(TIMEOUT_S, TimeUnit.SECONDS)); 150 151 validateScreenshot(); 152 } 153 addSecondSyncGroup(SurfaceSyncGroup surfaceSyncGroup, CountDownLatch waitForSecondDraw, CountDownLatch bothSyncGroupsComplete)154 private void addSecondSyncGroup(SurfaceSyncGroup surfaceSyncGroup, 155 CountDownLatch waitForSecondDraw, CountDownLatch bothSyncGroupsComplete) { 156 View backgroundView = mActivity.getBackgroundView(); 157 ViewTreeObserver viewTreeObserver = backgroundView.getViewTreeObserver(); 158 viewTreeObserver.registerFrameCommitCallback(() -> mHandler.post(() -> { 159 surfaceSyncGroup.add(backgroundView.getRootSurfaceControl(), 160 () -> mActivity.runOnUiThread( 161 () -> backgroundView.setBackgroundColor(Color.BLUE))); 162 163 SurfaceControl.Transaction t = new SurfaceControl.Transaction(); 164 t.addTransactionCommittedListener(Runnable::run, bothSyncGroupsComplete::countDown); 165 surfaceSyncGroup.addTransaction(t); 166 surfaceSyncGroup.markSyncReady(); 167 viewTreeObserver.registerFrameCommitCallback(waitForSecondDraw::countDown); 168 })); 169 } 170 validateScreenshot()171 private void validateScreenshot() { 172 Bitmap screenshot = mInstrumentation.getUiAutomation().takeScreenshot( 173 mActivity.getWindow()); 174 assertNotNull("Failed to generate a screenshot", screenshot); 175 Bitmap swBitmap = screenshot.copy(Bitmap.Config.ARGB_8888, false); 176 screenshot.recycle(); 177 178 BitmapPixelChecker pixelChecker = new BitmapPixelChecker(Color.BLUE); 179 int halfWidth = swBitmap.getWidth() / 2; 180 int halfHeight = swBitmap.getHeight() / 2; 181 // We don't need to check all the pixels since we only care that at least some of them are 182 // blue. If the buffers were submitted out of order, all the pixels will be red. 183 Rect bounds = new Rect(halfWidth, halfHeight, halfWidth + 10, halfHeight + 10); 184 int numMatchingPixels = pixelChecker.getNumMatchingPixels(swBitmap, bounds); 185 assertEquals("Expected 100 received " + numMatchingPixels + " matching pixels", 100, 186 numMatchingPixels); 187 188 swBitmap.recycle(); 189 } 190 } 191