• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 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 
17 package android.server.wm;
18 
19 import static android.server.wm.UiDeviceUtils.pressHomeButton;
20 import static android.server.wm.UiDeviceUtils.pressUnlockButton;
21 import static android.server.wm.UiDeviceUtils.pressWakeupButton;
22 import static android.view.SurfaceControlViewHost.SurfacePackage;
23 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
24 import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
25 
26 import static org.junit.Assert.assertEquals;
27 import static org.junit.Assert.assertFalse;
28 import static org.junit.Assert.assertTrue;
29 import static org.junit.Assert.fail;
30 
31 import android.app.Activity;
32 import android.app.ActivityManager;
33 import android.app.Instrumentation;
34 import android.content.Context;
35 import android.content.pm.ConfigurationInfo;
36 import android.content.pm.FeatureInfo;
37 import android.graphics.PixelFormat;
38 import android.platform.test.annotations.Presubmit;
39 import android.platform.test.annotations.RequiresDevice;
40 import android.view.Gravity;
41 import android.view.SurfaceControlViewHost;
42 import android.view.SurfaceHolder;
43 import android.view.SurfaceView;
44 import android.view.View;
45 import android.view.ViewGroup;
46 import android.view.ViewTreeObserver;
47 import android.view.WindowManager;
48 import android.widget.Button;
49 import android.widget.FrameLayout;
50 
51 import androidx.test.InstrumentationRegistry;
52 import androidx.test.filters.FlakyTest;
53 import androidx.test.rule.ActivityTestRule;
54 
55 import com.android.compatibility.common.util.CtsTouchUtils;
56 import com.android.compatibility.common.util.WidgetTestUtils;
57 
58 import org.junit.Before;
59 import org.junit.Test;
60 
61 import java.util.concurrent.CountDownLatch;
62 import java.util.concurrent.TimeUnit;
63 import java.util.concurrent.atomic.AtomicReference;
64 
65 /**
66  * Ensure end-to-end functionality of SurfaceControlViewHost.
67  *
68  * Build/Install/Run:
69  *     atest CtsWindowManagerDeviceTestCases:SurfaceControlViewHostTests
70  */
71 @Presubmit
72 public class SurfaceControlViewHostTests implements SurfaceHolder.Callback {
73     private final ActivityTestRule<Activity> mActivityRule = new ActivityTestRule<>(Activity.class);
74 
75     private Instrumentation mInstrumentation;
76     private Activity mActivity;
77     private SurfaceView mSurfaceView;
78 
79     private SurfaceControlViewHost mVr;
80     private View mEmbeddedView;
81     private WindowManager.LayoutParams mEmbeddedLayoutParams;
82 
83     private volatile boolean mClicked = false;
84 
85     /*
86      * Configurable state to control how the surfaceCreated callback
87      * will initialize the embedded view hierarchy.
88      */
89     int mEmbeddedViewWidth = 100;
90     int mEmbeddedViewHeight = 100;
91 
92     private static final int DEFAULT_SURFACE_VIEW_WIDTH = 100;
93     private static final int DEFAULT_SURFACE_VIEW_HEIGHT = 100;
94 
95     @Before
setUp()96     public void setUp() {
97         pressWakeupButton();
98         pressUnlockButton();
99         pressHomeButton();
100 
101         mClicked = false;
102         mEmbeddedLayoutParams = null;
103 
104         mInstrumentation = InstrumentationRegistry.getInstrumentation();
105         mActivity = mActivityRule.launchActivity(null);
106         mInstrumentation.waitForIdleSync();
107     }
108 
addSurfaceView(int width, int height)109     private void addSurfaceView(int width, int height) throws Throwable {
110         mActivityRule.runOnUiThread(() -> {
111             final FrameLayout content = new FrameLayout(mActivity);
112             mSurfaceView = new SurfaceView(mActivity);
113             mSurfaceView.setZOrderOnTop(true);
114             content.addView(mSurfaceView, new FrameLayout.LayoutParams(
115                 width, height, Gravity.LEFT | Gravity.TOP));
116             mActivity.setContentView(content, new ViewGroup.LayoutParams(width, height));
117             mSurfaceView.getHolder().addCallback(this);
118         });
119     }
120 
addViewToSurfaceView(SurfaceView sv, View v, int width, int height)121     private void addViewToSurfaceView(SurfaceView sv, View v, int width, int height) {
122         mVr = new SurfaceControlViewHost(mActivity, mActivity.getDisplay(), sv.getHostToken());
123 
124 
125         if (mEmbeddedLayoutParams == null) {
126             mVr.setView(v, width, height);
127         } else {
128             mVr.setView(v, mEmbeddedLayoutParams);
129         }
130 
131         sv.setChildSurfacePackage(mVr.getSurfacePackage());
132 
133         assertEquals(v, mVr.getView());
134     }
135 
requestSurfaceViewFocus()136     private void requestSurfaceViewFocus() throws Throwable {
137         mActivityRule.runOnUiThread(() -> {
138             mSurfaceView.setFocusableInTouchMode(true);
139             mSurfaceView.requestFocusFromTouch();
140         });
141     }
142 
assertWindowFocused(final View view, boolean hasWindowFocus)143     private void assertWindowFocused(final View view, boolean hasWindowFocus) {
144         final CountDownLatch latch = new CountDownLatch(1);
145         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule,
146                 view, () -> {
147                     if (view.hasWindowFocus() == hasWindowFocus) {
148                         latch.countDown();
149                         return;
150                     }
151                     view.getViewTreeObserver().addOnWindowFocusChangeListener(
152                             new ViewTreeObserver.OnWindowFocusChangeListener() {
153                                 @Override
154                                 public void onWindowFocusChanged(boolean newFocusState) {
155                                     if (hasWindowFocus == newFocusState) {
156                                         view.getViewTreeObserver()
157                                                 .removeOnWindowFocusChangeListener(this);
158                                         latch.countDown();
159                                     }
160                                 }
161                             });
162                 }
163         );
164 
165         try {
166             if (!latch.await(3, TimeUnit.SECONDS)) {
167                 fail();
168             }
169         } catch (InterruptedException e) {
170             fail();
171         }
172     }
173 
waitUntilEmbeddedViewDrawn()174     private void waitUntilEmbeddedViewDrawn() throws Throwable {
175         // We use frameCommitCallback because we need to ensure HWUI
176         // has actually queued the frame.
177         final CountDownLatch latch = new CountDownLatch(1);
178         mActivityRule.runOnUiThread(() -> {
179             mEmbeddedView.getViewTreeObserver().registerFrameCommitCallback(
180                 latch::countDown);
181             mEmbeddedView.invalidate();
182         });
183         assertTrue(latch.await(1, TimeUnit.SECONDS));
184 
185     }
186 
187     @Override
surfaceCreated(SurfaceHolder holder)188     public void surfaceCreated(SurfaceHolder holder) {
189         addViewToSurfaceView(mSurfaceView, mEmbeddedView,
190                 mEmbeddedViewWidth, mEmbeddedViewHeight);
191     }
192 
193     @Override
surfaceDestroyed(SurfaceHolder holder)194     public void surfaceDestroyed(SurfaceHolder holder) {
195     }
196 
197     @Override
surfaceChanged(SurfaceHolder holder, int format, int width, int height)198     public void surfaceChanged(SurfaceHolder holder, int format, int width,
199             int height) {
200     }
201 
202     @Test
testEmbeddedViewReceivesInput()203     public void testEmbeddedViewReceivesInput() throws Throwable {
204         mEmbeddedView = new Button(mActivity);
205         mEmbeddedView.setOnClickListener((View v) -> {
206             mClicked = true;
207         });
208 
209         addSurfaceView(DEFAULT_SURFACE_VIEW_WIDTH, DEFAULT_SURFACE_VIEW_HEIGHT);
210         mInstrumentation.waitForIdleSync();
211         waitUntilEmbeddedViewDrawn();
212 
213         CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mActivityRule, mSurfaceView);
214         assertTrue(mClicked);
215     }
216 
getGlEsVersion(Context context)217     private static int getGlEsVersion(Context context) {
218         ActivityManager activityManager =
219                 (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
220         ConfigurationInfo configInfo = activityManager.getDeviceConfigurationInfo();
221         if (configInfo.reqGlEsVersion != ConfigurationInfo.GL_ES_VERSION_UNDEFINED) {
222             return getMajorVersion(configInfo.reqGlEsVersion);
223         } else {
224             return 1; // Lack of property means OpenGL ES version 1
225         }
226     }
227 
228     /** @see FeatureInfo#getGlEsVersion() */
getMajorVersion(int glEsVersion)229     private static int getMajorVersion(int glEsVersion) {
230         return ((glEsVersion & 0xffff0000) >> 16);
231     }
232 
233     @Test
234     @RequiresDevice
235     @FlakyTest(bugId = 152103238)
testEmbeddedViewIsHardwareAccelerated()236     public void testEmbeddedViewIsHardwareAccelerated() throws Throwable {
237         // Hardware accel may not be supported on devices without GLES 2.0
238         if (getGlEsVersion(mActivity) < 2) {
239             return;
240         }
241         mEmbeddedView = new Button(mActivity);
242         mEmbeddedView.setOnClickListener((View v) -> {
243             mClicked = true;
244         });
245 
246         addSurfaceView(DEFAULT_SURFACE_VIEW_WIDTH, DEFAULT_SURFACE_VIEW_HEIGHT);
247         mInstrumentation.waitForIdleSync();
248 
249         // If we don't support hardware acceleration on the main activity the embedded
250         // view also won't be.
251         if (!mSurfaceView.isHardwareAccelerated()) {
252             return;
253         }
254 
255         assertTrue(mEmbeddedView.isHardwareAccelerated());
256     }
257 
258     @Test
testEmbeddedViewResizes()259     public void testEmbeddedViewResizes() throws Throwable {
260         mEmbeddedView = new Button(mActivity);
261         mEmbeddedView.setOnClickListener((View v) -> {
262             mClicked = true;
263         });
264 
265         final int bigEdgeLength = mEmbeddedViewWidth * 3;
266 
267         // We make the SurfaceView more than twice as big as the embedded view
268         // so that a touch in the middle of the SurfaceView won't land
269         // on the embedded view.
270         addSurfaceView(bigEdgeLength, bigEdgeLength);
271         mInstrumentation.waitForIdleSync();
272 
273         CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mActivityRule, mSurfaceView);
274         assertFalse(mClicked);
275 
276         mActivityRule.runOnUiThread(() -> {
277                 mVr.relayout(bigEdgeLength, bigEdgeLength);
278         });
279         mInstrumentation.waitForIdleSync();
280         waitUntilEmbeddedViewDrawn();
281 
282         // But after the click should hit.
283         CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mActivityRule, mSurfaceView);
284         assertTrue(mClicked);
285     }
286 
287     @Test
testEmbeddedViewReleases()288     public void testEmbeddedViewReleases() throws Throwable {
289         mEmbeddedView = new Button(mActivity);
290         mEmbeddedView.setOnClickListener((View v) -> {
291             mClicked = true;
292         });
293 
294         addSurfaceView(DEFAULT_SURFACE_VIEW_WIDTH, DEFAULT_SURFACE_VIEW_HEIGHT);
295         mInstrumentation.waitForIdleSync();
296 
297         CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mActivityRule, mSurfaceView);
298         assertTrue(mClicked);
299 
300         mActivityRule.runOnUiThread(() -> {
301             mVr.release();
302         });
303         mInstrumentation.waitForIdleSync();
304 
305         mClicked = false;
306         CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mActivityRule, mSurfaceView);
307         assertFalse(mClicked);
308     }
309 
310     @Test
testDisableInputTouch()311     public void testDisableInputTouch() throws Throwable {
312         mEmbeddedView = new Button(mActivity);
313         mEmbeddedView.setOnClickListener((View v) -> {
314             mClicked = true;
315         });
316 
317         mEmbeddedLayoutParams = new WindowManager.LayoutParams(mEmbeddedViewWidth,
318             mEmbeddedViewHeight, WindowManager.LayoutParams.TYPE_APPLICATION, 0,
319             PixelFormat.OPAQUE);
320 
321         addSurfaceView(DEFAULT_SURFACE_VIEW_WIDTH, DEFAULT_SURFACE_VIEW_HEIGHT);
322         mInstrumentation.waitForIdleSync();
323 
324         mActivityRule.runOnUiThread(() -> {
325                 mEmbeddedLayoutParams.flags |= FLAG_NOT_TOUCHABLE;
326                 mVr.relayout(mEmbeddedLayoutParams);
327         });
328         mInstrumentation.waitForIdleSync();
329 
330         CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mActivityRule, mSurfaceView);
331         assertFalse(mClicked);
332 
333         mActivityRule.runOnUiThread(() -> {
334                 mEmbeddedLayoutParams.flags &= ~FLAG_NOT_TOUCHABLE;
335                 mVr.relayout(mEmbeddedLayoutParams);
336         });
337         mInstrumentation.waitForIdleSync();
338 
339         CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mActivityRule, mSurfaceView);
340         assertTrue(mClicked);
341     }
342 
343     @Test
testFocusable()344     public void testFocusable() throws Throwable {
345         mEmbeddedView = new Button(mActivity);
346         addSurfaceView(DEFAULT_SURFACE_VIEW_WIDTH, DEFAULT_SURFACE_VIEW_HEIGHT);
347         mInstrumentation.waitForIdleSync();
348         waitUntilEmbeddedViewDrawn();
349 
350         // When surface view is focused, it should transfer focus to the embedded view.
351         requestSurfaceViewFocus();
352         assertWindowFocused(mEmbeddedView, true);
353         // assert host does not have focus
354         assertWindowFocused(mSurfaceView, false);
355 
356         // When surface view is no longer focused, it should transfer focus back to the host window.
357         mActivityRule.runOnUiThread(() -> mSurfaceView.setFocusable(false));
358         assertWindowFocused(mEmbeddedView, false);
359         // assert host has focus
360         assertWindowFocused(mSurfaceView, true);
361     }
362 
363     @Test
testNotFocusable()364     public void testNotFocusable() throws Throwable {
365         mEmbeddedView = new Button(mActivity);
366         addSurfaceView(DEFAULT_SURFACE_VIEW_WIDTH, DEFAULT_SURFACE_VIEW_HEIGHT);
367         mEmbeddedLayoutParams = new WindowManager.LayoutParams(mEmbeddedViewWidth,
368                 mEmbeddedViewHeight, WindowManager.LayoutParams.TYPE_APPLICATION, 0,
369                 PixelFormat.OPAQUE);
370         mActivityRule.runOnUiThread(() -> {
371             mEmbeddedLayoutParams.flags |= FLAG_NOT_FOCUSABLE;
372             mVr.relayout(mEmbeddedLayoutParams);
373         });
374         mInstrumentation.waitForIdleSync();
375         waitUntilEmbeddedViewDrawn();
376 
377         // When surface view is focused, nothing should happen since the embedded view is not
378         // focusable.
379         requestSurfaceViewFocus();
380         assertWindowFocused(mEmbeddedView, false);
381         // assert host has focus
382         assertWindowFocused(mSurfaceView, true);
383     }
384 
385     private static class SurfaceCreatedCallback implements SurfaceHolder.Callback {
386         private final CountDownLatch mSurfaceCreated;
SurfaceCreatedCallback(CountDownLatch latch)387         SurfaceCreatedCallback(CountDownLatch latch) {
388             mSurfaceCreated = latch;
389         }
390         @Override
surfaceCreated(SurfaceHolder holder)391         public void surfaceCreated(SurfaceHolder holder) {
392             mSurfaceCreated.countDown();
393         }
394 
395         @Override
surfaceDestroyed(SurfaceHolder holder)396         public void surfaceDestroyed(SurfaceHolder holder) {}
397 
398         @Override
surfaceChanged(SurfaceHolder holder, int format, int width, int height)399         public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}
400     }
401 
402     @Test
testCanCopySurfacePackage()403     public void testCanCopySurfacePackage() throws Throwable {
404         // Create a surface view and wait for its surface to be created.
405         CountDownLatch surfaceCreated = new CountDownLatch(1);
406         mActivityRule.runOnUiThread(() -> {
407             final FrameLayout content = new FrameLayout(mActivity);
408             mSurfaceView = new SurfaceView(mActivity);
409             mSurfaceView.setZOrderOnTop(true);
410             content.addView(mSurfaceView, new FrameLayout.LayoutParams(
411                     DEFAULT_SURFACE_VIEW_WIDTH, DEFAULT_SURFACE_VIEW_HEIGHT, Gravity.LEFT | Gravity.TOP));
412             mActivity.setContentView(content, new ViewGroup.LayoutParams(DEFAULT_SURFACE_VIEW_WIDTH, DEFAULT_SURFACE_VIEW_HEIGHT));
413             mSurfaceView.getHolder().addCallback(new SurfaceCreatedCallback(surfaceCreated));
414 
415             // Create an embedded view.
416             mVr = new SurfaceControlViewHost(mActivity, mActivity.getDisplay(),
417                     mSurfaceView.getHostToken());
418             mEmbeddedView = new Button(mActivity);
419             mEmbeddedView.setOnClickListener((View v) -> mClicked = true);
420             mVr.setView(mEmbeddedView, mEmbeddedViewWidth, mEmbeddedViewHeight);
421 
422         });
423         surfaceCreated.await();
424 
425         // Make a copy of the SurfacePackage and release the original package.
426         SurfacePackage surfacePackage = mVr.getSurfacePackage();
427         SurfacePackage copy = new SurfacePackage(surfacePackage);
428         surfacePackage.release();
429         mSurfaceView.setChildSurfacePackage(copy);
430 
431         mInstrumentation.waitForIdleSync();
432         waitUntilEmbeddedViewDrawn();
433 
434         // Check if SurfacePackage copy remains valid even though the original package has
435         // been released.
436         CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mActivityRule, mSurfaceView);
437         assertTrue(mClicked);
438     }
439 
440     @Test
testTransferSurfacePackage()441     public void testTransferSurfacePackage() throws Throwable {
442         // Create a surface view and wait for its surface to be created.
443         CountDownLatch surfaceCreated = new CountDownLatch(1);
444         CountDownLatch surface2Created = new CountDownLatch(1);
445         CountDownLatch viewDetached = new CountDownLatch(1);
446         AtomicReference<SurfacePackage> surfacePackageRef = new AtomicReference<>(null);
447         AtomicReference<SurfacePackage> surfacePackageCopyRef = new AtomicReference<>(null);
448         AtomicReference<SurfaceView> secondSurfaceRef = new AtomicReference<>(null);
449 
450         mActivityRule.runOnUiThread(() -> {
451             final FrameLayout content = new FrameLayout(mActivity);
452             mSurfaceView = new SurfaceView(mActivity);
453             mSurfaceView.setZOrderOnTop(true);
454             content.addView(mSurfaceView, new FrameLayout.LayoutParams(DEFAULT_SURFACE_VIEW_WIDTH,
455                     DEFAULT_SURFACE_VIEW_HEIGHT, Gravity.LEFT | Gravity.TOP));
456             mActivity.setContentView(content, new ViewGroup.LayoutParams(DEFAULT_SURFACE_VIEW_WIDTH,
457                     DEFAULT_SURFACE_VIEW_HEIGHT));
458             mSurfaceView.getHolder().addCallback(new SurfaceCreatedCallback(surfaceCreated));
459 
460             // Create an embedded view.
461             mVr = new SurfaceControlViewHost(mActivity, mActivity.getDisplay(),
462                     mSurfaceView.getHostToken());
463             mEmbeddedView = new Button(mActivity);
464             mEmbeddedView.setOnClickListener((View v) -> mClicked = true);
465             mVr.setView(mEmbeddedView, mEmbeddedViewWidth, mEmbeddedViewHeight);
466 
467             SurfacePackage surfacePackage = mVr.getSurfacePackage();
468             surfacePackageRef.set(surfacePackage);
469             surfacePackageCopyRef.set(new SurfacePackage(surfacePackage));
470 
471             // Assign the surface package to the first surface
472             mSurfaceView.setChildSurfacePackage(surfacePackage);
473 
474 
475             // Create the second surface view to which we'll assign the surface package copy
476             SurfaceView secondSurface = new SurfaceView(mActivity);
477             secondSurfaceRef.set(secondSurface);
478 
479             mSurfaceView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
480                 @Override
481                 public void onViewAttachedToWindow(View v) {
482                 }
483 
484                 @Override
485                 public void onViewDetachedFromWindow(View v) {
486                     viewDetached.countDown();
487                 }
488             });
489 
490             secondSurface.getHolder().addCallback(new SurfaceCreatedCallback(surface2Created));
491 
492         });
493         surfaceCreated.await();
494 
495         // Add the second surface view and assign it the surface package copy
496         mActivityRule.runOnUiThread(() -> {
497             ViewGroup content = (ViewGroup) mSurfaceView.getParent();
498             content.addView(secondSurfaceRef.get(),
499                     new FrameLayout.LayoutParams(DEFAULT_SURFACE_VIEW_WIDTH,
500                             DEFAULT_SURFACE_VIEW_HEIGHT, Gravity.TOP | Gravity.LEFT));
501             secondSurfaceRef.get().setZOrderOnTop(true);
502             surfacePackageRef.get().release();
503             secondSurfaceRef.get().setChildSurfacePackage(surfacePackageCopyRef.get());
504 
505             content.removeView(mSurfaceView);
506         });
507 
508         // Wait for the first surface to be removed
509         surface2Created.await();
510         viewDetached.await();
511 
512         mInstrumentation.waitForIdleSync();
513         waitUntilEmbeddedViewDrawn();
514 
515         // Check if SurfacePackage copy remains valid even though the original package has
516         // been released and the original surface view removed.
517         CtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mActivityRule,
518                 secondSurfaceRef.get());
519         assertTrue(mClicked);
520     }
521 
522 }
523