1 /* 2 * Copyright 2019 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.server.wm; 17 18 import static android.server.wm.ActivityManagerTestBase.createFullscreenActivityScenarioRule; 19 import static android.view.cts.surfacevalidator.ASurfaceControlTestActivity.MultiRectChecker; 20 import static android.view.cts.surfacevalidator.ASurfaceControlTestActivity.RectChecker; 21 22 import static org.junit.Assert.assertFalse; 23 import static org.junit.Assert.assertTrue; 24 25 import android.graphics.Canvas; 26 import android.graphics.Color; 27 import android.graphics.Rect; 28 import android.platform.test.annotations.Presubmit; 29 import android.view.Surface; 30 import android.view.SurfaceControl; 31 import android.view.SurfaceHolder; 32 import android.view.cts.surfacevalidator.ASurfaceControlTestActivity; 33 import android.view.cts.surfacevalidator.ASurfaceControlTestActivity.PixelChecker; 34 import android.view.cts.surfacevalidator.PixelColor; 35 36 import androidx.annotation.NonNull; 37 import androidx.test.ext.junit.rules.ActivityScenarioRule; 38 39 import org.junit.Before; 40 import org.junit.Rule; 41 import org.junit.Test; 42 import org.junit.rules.TestName; 43 44 @Presubmit 45 public class SurfaceControlTest { 46 private static final int DEFAULT_SURFACE_SIZE = 100; 47 48 @Rule 49 public ActivityScenarioRule<ASurfaceControlTestActivity> mActivityRule = 50 createFullscreenActivityScenarioRule(ASurfaceControlTestActivity.class); 51 52 @Rule 53 public TestName mName = new TestName(); 54 private ASurfaceControlTestActivity mActivity; 55 56 private abstract class SurfaceHolderCallback implements 57 SurfaceHolder.Callback { 58 addChildren(SurfaceControl parent)59 public abstract void addChildren(SurfaceControl parent); 60 61 @Override surfaceCreated(@onNull SurfaceHolder holder)62 public void surfaceCreated(@NonNull SurfaceHolder holder) { 63 addChildren(mActivity.getSurfaceView().getSurfaceControl()); 64 } 65 66 @Override surfaceChanged(@onNull SurfaceHolder holder, int format, int width, int height)67 public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, 68 int height) {} 69 70 @Override surfaceDestroyed(@onNull SurfaceHolder holder)71 public void surfaceDestroyed(@NonNull SurfaceHolder holder) {} 72 } 73 verifyTest(SurfaceHolder.Callback callback, PixelChecker pixelChecker)74 private void verifyTest(SurfaceHolder.Callback callback, 75 PixelChecker pixelChecker) throws Throwable { 76 mActivity.verifyTest(callback, pixelChecker, 0 /* delayInMs */); 77 } 78 79 @Before setup()80 public void setup() { 81 mActivityRule.getScenario().onActivity(activity -> mActivity = activity); 82 } 83 84 @Test testLifecycle()85 public void testLifecycle() { 86 final SurfaceControl.Builder b = new SurfaceControl.Builder(); 87 final SurfaceControl sc = b.setName("CTS").build(); 88 89 assertTrue("Failed to build SurfaceControl", sc != null); 90 assertTrue(sc.isValid()); 91 sc.release(); 92 assertFalse(sc.isValid()); 93 } 94 95 @Test testSameSurface()96 public void testSameSurface() { 97 final SurfaceControl.Builder b = new SurfaceControl.Builder(); 98 final SurfaceControl sc = b.setName("CTS").build(); 99 SurfaceControl copy = new SurfaceControl(sc, "SurfaceControlTest.testSameSurface"); 100 assertTrue(copy.isSameSurface(sc)); 101 sc.release(); 102 copy.release(); 103 } 104 buildDefaultSurface(SurfaceControl parent)105 private SurfaceControl buildDefaultSurface(SurfaceControl parent) { 106 return new SurfaceControl.Builder() 107 .setBufferSize(DEFAULT_SURFACE_SIZE, DEFAULT_SURFACE_SIZE) 108 .setName("CTS surface") 109 .setParent(parent) 110 .build(); 111 112 } 113 fillWithColor(SurfaceControl sc, int color)114 void fillWithColor(SurfaceControl sc, int color) { 115 Surface s = new Surface(sc); 116 117 Canvas c = s.lockHardwareCanvas(); 118 c.drawColor(color); 119 s.unlockCanvasAndPost(c); 120 } 121 buildDefaultSurface(SurfaceControl parent, int color)122 private SurfaceControl buildDefaultSurface(SurfaceControl parent, int color) { 123 final SurfaceControl sc = buildDefaultSurface(parent); 124 fillWithColor(sc, color); 125 return sc; 126 } 127 buildDefaultRedSurface(SurfaceControl parent)128 private SurfaceControl buildDefaultRedSurface(SurfaceControl parent) { 129 return buildDefaultSurface(parent, Color.RED); 130 } buildSmallRedSurface(SurfaceControl parent)131 private SurfaceControl buildSmallRedSurface(SurfaceControl parent) { 132 SurfaceControl surfaceControl = new SurfaceControl.Builder() 133 .setBufferSize(DEFAULT_SURFACE_SIZE / 2, DEFAULT_SURFACE_SIZE / 2) 134 .setName("CTS surface") 135 .setParent(parent) 136 .build(); 137 fillWithColor(surfaceControl, Color.RED); 138 return surfaceControl; 139 } 140 141 /** 142 * Verify that showing a 100x100 surface filled with RED produces roughly 10,000 red pixels. 143 */ 144 @Test testShow()145 public void testShow() throws Throwable { 146 verifyTest( 147 new SurfaceHolderCallback() { 148 @Override 149 public void addChildren(SurfaceControl parent) { 150 final SurfaceControl sc = buildDefaultRedSurface(parent); 151 152 new SurfaceControl.Transaction().setVisibility(sc, true).apply(); 153 154 sc.release(); 155 } 156 }, 157 new RectChecker(new Rect(0, 0, 100, 100), PixelColor.RED)); 158 } 159 160 /** 161 * The same setup as testShow, however we hide the surface and verify that we don't see Red. 162 */ 163 @Test testHide()164 public void testHide() throws Throwable { 165 verifyTest( 166 new SurfaceHolderCallback () { 167 @Override 168 public void addChildren(SurfaceControl parent) { 169 final SurfaceControl sc = buildDefaultRedSurface(parent); 170 171 new SurfaceControl.Transaction().setVisibility(sc, false).apply(); 172 173 sc.release(); 174 } 175 }, 176 new RectChecker(new Rect(0, 0, 100, 100), PixelColor.BLACK)); 177 } 178 179 /** 180 * Like testHide but we reparent the surface off-screen instead. 181 */ 182 @Test testReparentOff()183 public void testReparentOff() throws Throwable { 184 final SurfaceControl sc = buildDefaultRedSurface(null); 185 verifyTest( 186 new SurfaceHolderCallback () { 187 @Override 188 public void addChildren(SurfaceControl parent) { 189 new SurfaceControl.Transaction().reparent(sc, parent).apply(); 190 new SurfaceControl.Transaction().reparent(sc, null).apply(); 191 } 192 }, 193 new RectChecker(new Rect(0, 0, 100, 100), PixelColor.BLACK)); 194 // Since the SurfaceControl is parented off-screen, if we release our reference 195 // it may completely die. If this occurs while the render thread is still rendering 196 // the RED background we could trigger a crash. For this test defer destroying the 197 // Surface until we have collected our test results. 198 if (sc != null) { 199 sc.release(); 200 } 201 } 202 203 /** 204 * Here we use the same red-surface set up but construct it off-screen and then re-parent it. 205 */ 206 @Test testReparentOn()207 public void testReparentOn() throws Throwable { 208 verifyTest( 209 new SurfaceHolderCallback () { 210 @Override 211 public void addChildren(SurfaceControl parent) { 212 final SurfaceControl sc = buildDefaultRedSurface(null); 213 214 new SurfaceControl.Transaction().setVisibility(sc, true) 215 .reparent(sc, parent) 216 .apply(); 217 218 sc.release(); 219 } 220 }, 221 new RectChecker(new Rect(0, 0, 100, 100), PixelColor.RED)); 222 } 223 224 /** 225 * Test that a surface with Layer "2" appears over a surface with Layer "1". 226 */ 227 @Test testSetLayer()228 public void testSetLayer() throws Throwable { 229 verifyTest( 230 new SurfaceHolderCallback () { 231 @Override 232 public void addChildren(SurfaceControl parent) { 233 final SurfaceControl sc = buildDefaultRedSurface(parent); 234 final SurfaceControl sc2 = buildDefaultSurface(parent, Color.GREEN); 235 236 new SurfaceControl.Transaction().setVisibility(sc, true) 237 .setVisibility(sc2, true) 238 .setLayer(sc, 1) 239 .setLayer(sc2, 2) 240 .apply(); 241 242 sc.release(); 243 } 244 }, 245 new RectChecker(new Rect(0, 0, 100, 100), PixelColor.GREEN)); 246 } 247 248 /** 249 * Try setting the position of a surface with the top-left corner off-screen. 250 */ 251 @Test testSetGeometry_dstBoundsOffScreen()252 public void testSetGeometry_dstBoundsOffScreen() throws Throwable { 253 verifyTest( 254 new SurfaceHolderCallback () { 255 @Override 256 public void addChildren(SurfaceControl parent) { 257 final SurfaceControl sc = buildDefaultRedSurface(parent); 258 new SurfaceControl.Transaction().setVisibility(sc, true) 259 .setGeometry(sc, null, new Rect(-50, -50, 50, 50), Surface.ROTATION_0) 260 .apply(); 261 sc.release(); 262 } 263 }, 264 265 // The rect should be offset by -50 pixels 266 new MultiRectChecker(new Rect(0, 0, 100, 100)) { 267 final PixelColor red = new PixelColor(PixelColor.RED); 268 final PixelColor black = new PixelColor(PixelColor.BLACK); 269 @Override 270 public PixelColor getExpectedColor(int x, int y) { 271 if (x < 50 && y < 50) { 272 return red; 273 } else { 274 return black; 275 } 276 } 277 }); 278 } 279 280 /** 281 * Try setting the position of a surface with the top-left corner on-screen. 282 */ 283 @Test testSetGeometry_dstBoundsOnScreen()284 public void testSetGeometry_dstBoundsOnScreen() throws Throwable { 285 verifyTest( 286 new SurfaceHolderCallback () { 287 @Override 288 public void addChildren(SurfaceControl parent) { 289 final SurfaceControl sc = buildDefaultRedSurface(parent); 290 new SurfaceControl.Transaction().setVisibility(sc, true) 291 .setGeometry(sc, null, new Rect(50, 50, 150, 150), Surface.ROTATION_0) 292 .apply(); 293 294 sc.release(); 295 } 296 }, 297 298 // The rect should be offset by 50 pixels 299 new MultiRectChecker(new Rect(0, 0, 100, 100)) { 300 final PixelColor red = new PixelColor(PixelColor.RED); 301 final PixelColor black = new PixelColor(PixelColor.BLACK); 302 @Override 303 public PixelColor getExpectedColor(int x, int y) { 304 if (x >= 50 && y >= 50) { 305 return red; 306 } else { 307 return black; 308 } 309 } 310 }); 311 } 312 313 /** 314 * Try scaling a surface 315 */ 316 @Test testSetGeometry_dstBoundsScaled()317 public void testSetGeometry_dstBoundsScaled() throws Throwable { 318 verifyTest( 319 new SurfaceHolderCallback () { 320 @Override 321 public void addChildren(SurfaceControl parent) { 322 final SurfaceControl sc = buildSmallRedSurface(parent); 323 new SurfaceControl.Transaction().setVisibility(sc, true) 324 .setGeometry(sc, new Rect(0, 0, DEFAULT_SURFACE_SIZE / 2, DEFAULT_SURFACE_SIZE / 2), 325 new Rect(0, 0, DEFAULT_SURFACE_SIZE , DEFAULT_SURFACE_SIZE), 326 Surface.ROTATION_0) 327 .apply(); 328 sc.release(); 329 } 330 }, 331 332 new RectChecker(new Rect(0, 0, 100, 100), PixelColor.RED)); 333 } 334 } 335