• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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