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 package android.view.surfacecontrol.cts;
17 
18 import static android.server.wm.BuildUtils.HW_TIMEOUT_MULTIPLIER;
19 import static android.server.wm.CtsWindowInfoUtils.getWindowCenter;
20 import static android.server.wm.CtsWindowInfoUtils.getWindowBoundsInDisplaySpace;
21 import static android.server.wm.CtsWindowInfoUtils.waitForStableWindowGeometry;
22 import static android.server.wm.CtsWindowInfoUtils.waitForWindowFocus;
23 import static android.server.wm.CtsWindowInfoUtils.waitForWindowInfo;
24 import static android.server.wm.CtsWindowInfoUtils.waitForWindowInfos;
25 import static android.server.wm.CtsWindowInfoUtils.waitForWindowOnTop;
26 import static android.server.wm.CtsWindowInfoUtils.waitForWindowVisible;
27 import static android.server.wm.MockImeHelper.createManagedMockImeSession;
28 import static android.view.SurfaceControlViewHost.SurfacePackage;
29 import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
30 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
31 import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
32 
33 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
34 
35 import static com.android.cts.input.inputeventmatchers.InputEventMatchersKt.withFlags;
36 import static com.android.cts.input.inputeventmatchers.InputEventMatchersKt.withMotionAction;
37 import static com.android.cts.mockime.ImeEventStreamTestUtils.editorMatcher;
38 import static com.android.cts.mockime.ImeEventStreamTestUtils.expectEvent;
39 
40 import static org.hamcrest.CoreMatchers.allOf;
41 import static org.hamcrest.MatcherAssert.assertThat;
42 import static org.junit.Assert.assertEquals;
43 import static org.junit.Assert.assertFalse;
44 import static org.junit.Assert.assertNotEquals;
45 import static org.junit.Assert.assertNotNull;
46 import static org.junit.Assert.assertNull;
47 import static org.junit.Assert.assertTrue;
48 import static org.junit.Assert.fail;
49 import static org.junit.Assume.assumeTrue;
50 
51 import android.app.Activity;
52 import android.app.ActivityManager;
53 import android.app.Instrumentation;
54 import android.app.KeyguardManager;
55 import android.app.UiAutomation;
56 import android.content.ComponentName;
57 import android.content.Context;
58 import android.content.Intent;
59 import android.content.pm.ActivityInfo;
60 import android.content.pm.ConfigurationInfo;
61 import android.content.pm.FeatureInfo;
62 import android.content.res.Configuration;
63 import android.graphics.Color;
64 import android.graphics.PixelFormat;
65 import android.graphics.Point;
66 import android.graphics.Rect;
67 import android.graphics.Region;
68 import android.os.Binder;
69 import android.os.Bundle;
70 import android.os.IBinder;
71 import android.os.RemoteException;
72 import android.os.SystemClock;
73 import android.platform.test.annotations.Presubmit;
74 import android.platform.test.annotations.RequiresDevice;
75 import android.platform.test.annotations.RequiresFlagsEnabled;
76 import android.platform.test.flag.junit.CheckFlagsRule;
77 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
78 import android.server.wm.ActivityManagerTestBase;
79 import android.server.wm.CtsWindowInfoUtils;
80 import android.server.wm.FutureConnection;
81 import android.server.wm.WindowManagerState;
82 import android.server.wm.scvh.Components;
83 import android.server.wm.scvh.ICrossProcessSurfaceControlViewHostTestService;
84 import android.util.ArrayMap;
85 import android.view.Gravity;
86 import android.view.MotionEvent;
87 import android.view.SurfaceControl;
88 import android.view.SurfaceControlViewHost;
89 import android.view.SurfaceHolder;
90 import android.view.SurfaceView;
91 import android.view.View;
92 import android.view.ViewGroup;
93 import android.view.WindowManager;
94 import android.widget.Button;
95 import android.widget.EditText;
96 import android.widget.FrameLayout;
97 import android.widget.PopupWindow;
98 import android.window.WindowInfosListenerForTest.WindowInfo;
99 
100 import androidx.annotation.NonNull;
101 import androidx.annotation.Nullable;
102 import androidx.test.InstrumentationRegistry;
103 import androidx.test.rule.ActivityTestRule;
104 
105 import com.android.compatibility.common.util.CtsTouchUtils;
106 import com.android.compatibility.common.util.PollingCheck;
107 import com.android.cts.input.DebugInputRule;
108 import com.android.cts.input.UinputTouchDevice;
109 import com.android.cts.input.UinputTouchScreen;
110 import com.android.cts.mockime.ImeEventStream;
111 import com.android.cts.mockime.MockImeSession;
112 import com.android.window.flags.Flags;
113 
114 import org.junit.After;
115 import org.junit.Before;
116 import org.junit.Rule;
117 import org.junit.Test;
118 import org.junit.rules.TestName;
119 
120 import java.util.ArrayList;
121 import java.util.List;
122 import java.util.Map;
123 import java.util.concurrent.CountDownLatch;
124 import java.util.concurrent.TimeUnit;
125 import java.util.concurrent.atomic.AtomicReference;
126 import java.util.function.Consumer;
127 import java.util.function.Predicate;
128 import java.util.function.Supplier;
129 
130 /**
131  * Ensure end-to-end functionality of SurfaceControlViewHost.
132  * <p>
133  * Build/Install/Run:
134  * atest CtsWindowManagerDeviceTestCases:SurfaceControlViewHostTests
135  */
136 @Presubmit
137 public class SurfaceControlViewHostTests extends ActivityManagerTestBase implements
138         SurfaceHolder.Callback {
139 
140     public static class TestActivity extends Activity {
141         @Override
onCreate(@ullable Bundle savedInstanceState)142         protected void onCreate(@Nullable Bundle savedInstanceState) {
143             super.onCreate(savedInstanceState);
144             KeyguardManager keyguardManager = getSystemService(KeyguardManager.class);
145             if (keyguardManager.isKeyguardLocked()) {
146                 keyguardManager.requestDismissKeyguard(this, null);
147             }
148         }
149     }
150 
151     @Rule
152     public DebugInputRule mDebugInputRule = new DebugInputRule();
153 
154     private static final String TAG = "SurfaceControlViewHostTests";
155 
156     private static final long WAIT_TIMEOUT_S = 5L * HW_TIMEOUT_MULTIPLIER;
157 
158     private static final ComponentName TEST_ACTIVITY = new ComponentName(
159             getInstrumentation().getContext(), TestActivity.class);
160 
161     private final ActivityTestRule<TestActivity> mActivityRule = new ActivityTestRule<>(
162             TestActivity.class);
163 
164     @Rule
165     public TestName mName = new TestName();
166 
167     @Rule
168     public final CheckFlagsRule mCheckFlagsRule =
169             DeviceFlagsValueProvider.createCheckFlagsRule();
170 
171     private Instrumentation mInstrumentation;
172     private CtsTouchUtils mCtsTouchUtils;
173     private Activity mActivity;
174     private SurfaceView mSurfaceView;
175     private ViewGroup mViewParent;
176 
177     private SurfaceControlViewHost mVr;
178     private View mEmbeddedView;
179     private WindowManager.LayoutParams mEmbeddedLayoutParams;
180 
181     private volatile boolean mClicked = false;
182     private volatile boolean mPopupClicked = false;
183     private volatile PopupWindow mPopupWindow;
184 
185     private SurfaceControlViewHost.SurfacePackage mRemoteSurfacePackage;
186 
187     private final Map<String,
188             FutureConnection<ICrossProcessSurfaceControlViewHostTestService>> mConnections =
189             new ArrayMap<>();
190     private ICrossProcessSurfaceControlViewHostTestService mTestService = null;
191     private static final long TIMEOUT_MS = 3000L;
192 
193     /*
194      * Configurable state to control how the surfaceCreated callback
195      * will initialize the embedded view hierarchy.
196      */
197     int mEmbeddedViewWidth = 100;
198     int mEmbeddedViewHeight = 100;
199 
200     private static final int DEFAULT_SURFACE_VIEW_WIDTH = 100;
201     private static final int DEFAULT_SURFACE_VIEW_HEIGHT = 100;
202     MockImeSession mImeSession;
203 
204     Consumer<MotionEvent> mSurfaceViewMotionConsumer = null;
205 
206     private CountDownLatch mSvCreatedLatch;
207 
208     UinputTouchDevice mTouchScreen;
209 
210     class MotionConsumingSurfaceView extends SurfaceView {
MotionConsumingSurfaceView(Context c)211         MotionConsumingSurfaceView(Context c) {
212             super(c);
213         }
214 
215         @Override
onTouchEvent(MotionEvent ev)216         public boolean onTouchEvent(MotionEvent ev) {
217             if (mSurfaceViewMotionConsumer == null) {
218                 return false;
219             } else {
220                 mSurfaceViewMotionConsumer.accept(ev);
221                 return true;
222             }
223         }
224     }
225 
226     boolean mHostGotEvent = false;
227 
228     @Before
setUp()229     public void setUp() throws Exception {
230         super.setUp();
231         mClicked = false;
232         mEmbeddedLayoutParams = null;
233         mPopupWindow = null;
234         mRemoteSurfacePackage = null;
235 
236         if (supportsInstallableIme()) {
237             mImeSession = createManagedMockImeSession(this);
238         }
239 
240         mInstrumentation = InstrumentationRegistry.getInstrumentation();
241         mCtsTouchUtils = new CtsTouchUtils(mInstrumentation.getTargetContext());
242         mActivity = mActivityRule.launchActivity(null);
243         mTouchScreen = new UinputTouchScreen(mInstrumentation, mActivity.getDisplay());
244         mInstrumentation.waitForIdleSync();
245         // Wait for device animation that shows above the activity to leave.
246         waitForWindowOnTop(mActivity.getWindow());
247 
248         // This is necessary to call waitForWindowInfos
249         mInstrumentation.getUiAutomation().adoptShellPermissionIdentity(
250                 android.Manifest.permission.ACCESS_SURFACE_FLINGER);
251 
252         mSvCreatedLatch = new CountDownLatch(1);
253     }
254 
255     @After
tearDown()256     public void tearDown() throws Throwable {
257         for (FutureConnection<ICrossProcessSurfaceControlViewHostTestService> connection :
258                 mConnections.values()) {
259             mInstrumentation.getContext().unbindService(connection);
260         }
261         mConnections.clear();
262         mInstrumentation.getUiAutomation().dropShellPermissionIdentity();
263         mTouchScreen.close();
264     }
265 
getViewLocationOnScreen(@onNull View view)266     private static int[] getViewLocationOnScreen(@NonNull View view) {
267         final int[] xy = new int[2];
268         view.getLocationOnScreen(xy);
269         return xy;
270     }
271 
getViewCenterOnScreen(@onNull View view)272     private static Point getViewCenterOnScreen(@NonNull View view) {
273         final int[] xy = getViewLocationOnScreen(view);
274         final int viewWidth = view.getWidth();
275         final int viewHeight = view.getHeight();
276 
277         return new Point(xy[0] + viewWidth / 2, xy[1] + viewHeight / 2);
278     }
279 
globalTapOnViewCenter(@onNull View view)280     private void globalTapOnViewCenter(@NonNull View view) {
281         final Point location = getViewCenterOnScreen(view);
282         UinputTouchDevice.Pointer pointer = mTouchScreen.touchDown(location.x, location.y);
283         pointer.lift();
284     }
285 
globalTapOnWindowCenter(@onNull Supplier<IBinder> windowTokenSupplier)286     private void globalTapOnWindowCenter(@NonNull Supplier<IBinder> windowTokenSupplier)
287             throws InterruptedException {
288         final Point center = getWindowCenter(windowTokenSupplier);
289         UinputTouchDevice.Pointer pointer = mTouchScreen.touchDown(center.x, center.y);
290         pointer.lift();
291     }
292 
globalTapOnWindowCorner(@onNull Supplier<IBinder> windowTokenSupplier)293     private void globalTapOnWindowCorner(@NonNull Supplier<IBinder> windowTokenSupplier)
294             throws InterruptedException {
295         Rect bounds = getWindowBoundsInDisplaySpace(windowTokenSupplier);
296         if (bounds == null) {
297             fail("Could not get bounds for window!");
298         }
299 
300         UinputTouchDevice.Pointer pointer = mTouchScreen.touchDown(bounds.left, bounds.top);
301         pointer.lift();
302     }
303 
addSurfaceView(int width, int height)304     private void addSurfaceView(int width, int height) throws Throwable {
305         addSurfaceView(width, height, true);
306     }
307 
addSurfaceView(int width, int height, boolean onTop)308     private void addSurfaceView(int width, int height, boolean onTop) throws Throwable {
309         addSurfaceView(width, height, onTop, 0 /* leftMargin */, 0 /* topMargin */);
310     }
311 
addSurfaceView(int width, int height, boolean onTop, int leftMargin, int topMargin)312     private void addSurfaceView(int width, int height, boolean onTop, int leftMargin, int topMargin)
313             throws Throwable {
314         mActivityRule.runOnUiThread(() -> {
315             final FrameLayout content = new FrameLayout(mActivity);
316             mSurfaceView = new MotionConsumingSurfaceView(mActivity);
317             mSurfaceView.setBackgroundColor(Color.BLACK);
318             mSurfaceView.setZOrderOnTop(onTop);
319             final FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
320                     width, height, Gravity.LEFT | Gravity.TOP);
321             lp.leftMargin = leftMargin;
322             lp.topMargin = topMargin;
323             content.addView(mSurfaceView, lp);
324             mViewParent = content;
325             mActivity.setContentView(content,
326                     new ViewGroup.LayoutParams(width + leftMargin, height + topMargin));
327             mSurfaceView.getHolder().addCallback(this);
328         });
329     }
330 
addViewToSurfaceView(SurfaceView sv, View v, int width, int height)331     private void addViewToSurfaceView(SurfaceView sv, View v, int width, int height) {
332         mVr = new SurfaceControlViewHost(mActivity, mActivity.getDisplay(), sv.getHostToken());
333 
334         if (mEmbeddedLayoutParams == null) {
335             mVr.setView(v, width, height);
336         } else {
337             mVr.setView(v, mEmbeddedLayoutParams);
338         }
339 
340         sv.setChildSurfacePackage(mVr.getSurfacePackage());
341 
342         assertEquals(v, mVr.getView());
343     }
344 
requestSurfaceViewFocus()345     private void requestSurfaceViewFocus() throws Throwable {
346         mActivityRule.runOnUiThread(() -> {
347             mSurfaceView.setFocusableInTouchMode(true);
348             mSurfaceView.requestFocusFromTouch();
349         });
350     }
351 
assertWindowFocused(final View view, boolean hasWindowFocus)352     private void assertWindowFocused(final View view, boolean hasWindowFocus) {
353         if (!waitForWindowFocus(view, hasWindowFocus)) {
354             fail();
355         }
356     }
357 
waitUntilViewDrawn(View view)358     private void waitUntilViewDrawn(View view) throws Throwable {
359         // We use frameCommitCallback because we need to ensure HWUI
360         // has actually queued the frame.
361         final CountDownLatch latch = new CountDownLatch(1);
362         mActivityRule.runOnUiThread(() -> {
363             view.getViewTreeObserver().registerFrameCommitCallback(
364                     latch::countDown);
365             view.invalidate();
366         });
367         assertTrue(latch.await(HW_TIMEOUT_MULTIPLIER, TimeUnit.SECONDS));
368     }
369 
waitUntilEmbeddedViewDrawn()370     private void waitUntilEmbeddedViewDrawn() throws Throwable {
371         waitUntilViewDrawn(mEmbeddedView);
372     }
373 
getTouchableRegionFromDump()374     private String getTouchableRegionFromDump() {
375         final String output = runCommandAndPrintOutput("dumpsys window windows");
376         boolean foundWindow = false;
377         for (String line : output.split("\\n")) {
378             if (line.contains("SurfaceControlViewHostTests$TestActivity")) {
379                 foundWindow = true;
380             }
381             if (foundWindow && line.contains("touchable region")) {
382                 return line;
383             }
384         }
385         return null;
386     }
387 
waitForTouchableRegionChanged(String originalTouchableRegion)388     private boolean waitForTouchableRegionChanged(String originalTouchableRegion) {
389         int retries = 0;
390         while (retries < 50) {
391             if (getTouchableRegionFromDump() != originalTouchableRegion) {
392                 return true;
393             }
394             try {
395                 Thread.sleep(100);
396             } catch (Exception e) {
397             }
398         }
399         return false;
400     }
401 
waitForViewFocus(final View view, boolean hasViewFocus)402     public static boolean waitForViewFocus(final View view, boolean hasViewFocus) {
403         final CountDownLatch latch = new CountDownLatch(1);
404 
405         view.getHandler().post(() -> {
406             if (view.hasFocus() == hasViewFocus) {
407                 latch.countDown();
408                 return;
409             }
410             view.setOnFocusChangeListener((v, hasFocus) -> {
411                 if (hasViewFocus == hasFocus) {
412                     view.setOnFocusChangeListener(null);
413                     latch.countDown();
414                 }
415             });
416         });
417 
418         try {
419             if (!latch.await(3, TimeUnit.SECONDS)) {
420                 return false;
421             }
422         } catch (InterruptedException e) {
423             return false;
424         }
425         return true;
426     }
427 
428     @Override
surfaceCreated(SurfaceHolder holder)429     public void surfaceCreated(SurfaceHolder holder) {
430         if (mTestService == null) {
431             if (mEmbeddedView != null) {
432                 addViewToSurfaceView(mSurfaceView, mEmbeddedView,
433                         mEmbeddedViewWidth, mEmbeddedViewHeight);
434             }
435         } else if (mRemoteSurfacePackage == null) {
436             try {
437                 mRemoteSurfacePackage = mTestService.getSurfacePackage(mSurfaceView.getHostToken());
438             } catch (Exception e) {
439             }
440             mSurfaceView.setChildSurfacePackage(mRemoteSurfacePackage);
441         } else {
442             mSurfaceView.setChildSurfacePackage(mRemoteSurfacePackage);
443         }
444         mSvCreatedLatch.countDown();
445     }
446 
447     @Override
surfaceDestroyed(SurfaceHolder holder)448     public void surfaceDestroyed(SurfaceHolder holder) {
449     }
450 
451     @Override
surfaceChanged(SurfaceHolder holder, int format, int width, int height)452     public void surfaceChanged(SurfaceHolder holder, int format, int width,
453             int height) {
454     }
455 
456     @Test
testEmbeddedViewReceivesInput()457     public void testEmbeddedViewReceivesInput() throws Throwable {
458         mEmbeddedView = new Button(mActivity);
459         mEmbeddedView.setOnClickListener((View v) -> {
460             mClicked = true;
461         });
462 
463         addSurfaceView(DEFAULT_SURFACE_VIEW_WIDTH, DEFAULT_SURFACE_VIEW_HEIGHT);
464         mInstrumentation.waitForIdleSync();
465         waitUntilEmbeddedViewDrawn();
466 
467         mCtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mActivityRule, mSurfaceView);
468         assertTrue(mClicked);
469     }
470 
471     @Test
testEmbeddedViewReceivesRawInputCoordinatesInDisplaySpace()472     public void testEmbeddedViewReceivesRawInputCoordinatesInDisplaySpace() throws Throwable {
473         final UiAutomation uiAutomation = mInstrumentation.getUiAutomation();
474         final int viewX = DEFAULT_SURFACE_VIEW_WIDTH / 2;
475         final int viewY = DEFAULT_SURFACE_VIEW_HEIGHT / 2;
476 
477         // Verify the input coordinates received by the embedded view in three different locations.
478         for (int i = 0; i < 3; i++) {
479             final List<MotionEvent> events = new ArrayList<>();
480             mEmbeddedView = new View(mActivity);
481             mEmbeddedView.setOnTouchListener((v, e) -> events.add(MotionEvent.obtain(e)));
482 
483             // Add a margin to the SurfaceView to offset the embedded view's location on the screen.
484             final int leftMargin = i * 20;
485             final int topMargin = i * 10;
486             addSurfaceView(DEFAULT_SURFACE_VIEW_WIDTH, DEFAULT_SURFACE_VIEW_HEIGHT, true /*onTop*/,
487                     leftMargin, topMargin);
488             mInstrumentation.waitForIdleSync();
489             waitUntilEmbeddedViewDrawn();
490             waitForStableWindowGeometry(WAIT_TIMEOUT_S, TimeUnit.SECONDS);
491 
492             final int[] surfaceLocation = new int[2];
493             mSurfaceView.getLocationOnScreen(surfaceLocation);
494 
495             final int displayX = surfaceLocation[0] + viewX;
496             final int displayY = surfaceLocation[1] + viewY;
497             final long downTime = SystemClock.uptimeMillis();
498 
499             UinputTouchDevice.Pointer pointer = mTouchScreen.touchDown(displayX, displayY);
500             pointer.lift();
501 
502             PollingCheck.waitFor(() -> (events.size() == 2));
503             final float epsilon = 0.001f;
504             events.forEach(e -> {
505                 assertEquals("Expected to get the x coordinate in View space.",
506                         viewX, e.getX(), epsilon);
507                 assertEquals("Expected to get the y coordinate in View space.",
508                         viewY, e.getY(), epsilon);
509                 assertEquals("Expected to get raw x coordinate in Display space.",
510                         displayX, e.getRawX(), epsilon);
511                 assertEquals("Expected to get raw y coordinate in Display space.",
512                         displayY, e.getRawY(), epsilon);
513             });
514         }
515     }
516 
getGlEsVersion(Context context)517     private static int getGlEsVersion(Context context) {
518         ActivityManager activityManager =
519                 (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
520         ConfigurationInfo configInfo = activityManager.getDeviceConfigurationInfo();
521         if (configInfo.reqGlEsVersion != ConfigurationInfo.GL_ES_VERSION_UNDEFINED) {
522             return getMajorVersion(configInfo.reqGlEsVersion);
523         } else {
524             return 1; // Lack of property means OpenGL ES version 1
525         }
526     }
527 
528     /**
529      * @see FeatureInfo#getGlEsVersion()
530      */
getMajorVersion(int glEsVersion)531     private static int getMajorVersion(int glEsVersion) {
532         return ((glEsVersion & 0xffff0000) >> 16);
533     }
534 
535     @Test
536     @RequiresDevice
testEmbeddedViewIsHardwareAccelerated()537     public void testEmbeddedViewIsHardwareAccelerated() throws Throwable {
538         // Hardware accel may not be supported on devices without GLES 2.0
539         if (getGlEsVersion(mActivity) < 2) {
540             return;
541         }
542         mEmbeddedView = new Button(mActivity);
543         mEmbeddedView.setOnClickListener((View v) -> {
544             mClicked = true;
545         });
546 
547         addSurfaceView(DEFAULT_SURFACE_VIEW_WIDTH, DEFAULT_SURFACE_VIEW_HEIGHT);
548         mInstrumentation.waitForIdleSync();
549 
550         // If we don't support hardware acceleration on the main activity the embedded
551         // view also won't be.
552         if (!mSurfaceView.isHardwareAccelerated()) {
553             return;
554         }
555 
556         assertTrue(mEmbeddedView.isHardwareAccelerated());
557     }
558 
559     @Test
testEmbeddedViewResizes()560     public void testEmbeddedViewResizes() throws Throwable {
561         mEmbeddedView = new Button(mActivity);
562         mEmbeddedView.setOnClickListener((View v) -> {
563             mClicked = true;
564         });
565 
566         final int bigEdgeLength = mEmbeddedViewWidth * 3;
567 
568         // We make the SurfaceView more than twice as big as the embedded view
569         // so that a touch in the middle of the SurfaceView won't land
570         // on the embedded view.
571         addSurfaceView(bigEdgeLength, bigEdgeLength);
572         mInstrumentation.waitForIdleSync();
573 
574         mCtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mActivityRule, mSurfaceView);
575         assertFalse(mClicked);
576 
577         mActivityRule.runOnUiThread(() -> {
578             mVr.relayout(bigEdgeLength, bigEdgeLength);
579         });
580         mInstrumentation.waitForIdleSync();
581         waitUntilEmbeddedViewDrawn();
582 
583         // But after the click should hit.
584         mCtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mActivityRule, mSurfaceView);
585         assertTrue(mClicked);
586     }
587 
588     @Test
testEmbeddedViewReleases()589     public void testEmbeddedViewReleases() throws Throwable {
590         mEmbeddedView = new Button(mActivity);
591         mEmbeddedView.setOnClickListener((View v) -> {
592             mClicked = true;
593         });
594 
595         addSurfaceView(DEFAULT_SURFACE_VIEW_WIDTH, DEFAULT_SURFACE_VIEW_HEIGHT);
596         mInstrumentation.waitForIdleSync();
597 
598         mCtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mActivityRule, mSurfaceView);
599         assertTrue(mClicked);
600 
601         mActivityRule.runOnUiThread(() -> {
602             mVr.release();
603         });
604         mInstrumentation.waitForIdleSync();
605 
606         mClicked = false;
607         mCtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mActivityRule, mSurfaceView);
608         assertFalse(mClicked);
609     }
610 
611     @Test
testDisableInputTouch()612     public void testDisableInputTouch() throws Throwable {
613         mEmbeddedView = new Button(mActivity);
614         mEmbeddedView.setOnClickListener((View v) -> {
615             mClicked = true;
616         });
617 
618         mEmbeddedLayoutParams = new WindowManager.LayoutParams(mEmbeddedViewWidth,
619                 mEmbeddedViewHeight, WindowManager.LayoutParams.TYPE_APPLICATION, 0,
620                 PixelFormat.OPAQUE);
621 
622         addSurfaceView(DEFAULT_SURFACE_VIEW_WIDTH, DEFAULT_SURFACE_VIEW_HEIGHT);
623         mInstrumentation.waitForIdleSync();
624 
625         mActivityRule.runOnUiThread(() -> {
626             mEmbeddedLayoutParams.flags |= FLAG_NOT_TOUCHABLE;
627             mVr.relayout(mEmbeddedLayoutParams);
628         });
629         mInstrumentation.waitForIdleSync();
630 
631         mCtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mActivityRule, mSurfaceView);
632         assertFalse(mClicked);
633 
634         mActivityRule.runOnUiThread(() -> {
635             mEmbeddedLayoutParams.flags &= ~FLAG_NOT_TOUCHABLE;
636             mVr.relayout(mEmbeddedLayoutParams);
637         });
638         mInstrumentation.waitForIdleSync();
639 
640         mCtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mActivityRule, mSurfaceView);
641         assertTrue(mClicked);
642     }
643 
644     @Test
testFocusable()645     public void testFocusable() throws Throwable {
646         mEmbeddedView = new Button(mActivity);
647         addSurfaceView(DEFAULT_SURFACE_VIEW_WIDTH, DEFAULT_SURFACE_VIEW_HEIGHT);
648         mInstrumentation.waitForIdleSync();
649         waitUntilEmbeddedViewDrawn();
650 
651         // When surface view is focused, it should transfer focus to the embedded view.
652         requestSurfaceViewFocus();
653         assertWindowFocused(mEmbeddedView, true);
654         // assert host does not have focus
655         assertWindowFocused(mSurfaceView, false);
656 
657         // When surface view is no longer focused, it should transfer focus back to the host window.
658         mActivityRule.runOnUiThread(() -> mSurfaceView.setFocusable(false));
659         assertWindowFocused(mEmbeddedView, false);
660         // assert host has focus
661         assertWindowFocused(mSurfaceView, true);
662     }
663 
664     @Test
testFocusWithTouch()665     public void testFocusWithTouch() throws Throwable {
666         mEmbeddedView = new Button(mActivity);
667         addSurfaceView(DEFAULT_SURFACE_VIEW_WIDTH, DEFAULT_SURFACE_VIEW_HEIGHT);
668         mInstrumentation.waitForIdleSync();
669         waitUntilEmbeddedViewDrawn();
670 
671         // Tap where the embedded window is placed to ensure focus is given via touch
672         globalTapOnWindowCenter(mEmbeddedView::getWindowToken);
673 
674         assertWindowFocused(mEmbeddedView, true);
675         // assert host does not have focus
676         assertWindowFocused(mSurfaceView, false);
677 
678         // Tap where the host window is placed to ensure focus is given back to host when touched
679         globalTapOnWindowCenter(mViewParent::getWindowToken);
680         assertWindowFocused(mEmbeddedView, false);
681         // assert host does not have focus
682         assertWindowFocused(mViewParent, true);
683     }
684 
685     @Test
testChildWindowFocusable()686     public void testChildWindowFocusable() throws Throwable {
687         mEmbeddedView = new Button(mActivity);
688         mEmbeddedView.setBackgroundColor(Color.BLUE);
689         View embeddedViewChild = new Button(mActivity);
690         embeddedViewChild.setBackgroundColor(Color.RED);
691         addSurfaceView(DEFAULT_SURFACE_VIEW_WIDTH, DEFAULT_SURFACE_VIEW_HEIGHT);
692         mInstrumentation.waitForIdleSync();
693         waitUntilEmbeddedViewDrawn();
694 
695         mActivityRule.runOnUiThread(() -> {
696             final WindowManager.LayoutParams embeddedViewChildParams =
697                     new WindowManager.LayoutParams(25, 25,
698                             WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.OPAQUE);
699             embeddedViewChildParams.token = mEmbeddedView.getWindowToken();
700             WindowManager wm = mActivity.getSystemService(WindowManager.class);
701             wm.addView(embeddedViewChild, embeddedViewChildParams);
702         });
703 
704         waitUntilViewDrawn(embeddedViewChild);
705 
706         globalTapOnWindowCenter(embeddedViewChild::getWindowToken);
707         // When tapping on the child embedded window, it should gain focus.
708         assertWindowFocused(embeddedViewChild, true);
709         // assert parent embedded window does not have focus.
710         assertWindowFocused(mEmbeddedView, false);
711         // assert host does not have focus
712         assertWindowFocused(mSurfaceView, false);
713 
714         globalTapOnWindowCorner(mEmbeddedView::getWindowToken);
715 
716         // When tapping on the parent embedded window, it should gain focus.
717         assertWindowFocused(mEmbeddedView, true);
718         // assert child embedded window does not have focus.
719         assertWindowFocused(embeddedViewChild, false);
720         // assert host does not have focus
721         assertWindowFocused(mSurfaceView, false);
722     }
723 
724     @Test
testFocusWithTouchCrossProcess()725     public void testFocusWithTouchCrossProcess() throws Throwable {
726         mTestService = getService();
727         assertNotNull(mTestService);
728 
729         addSurfaceView(DEFAULT_SURFACE_VIEW_WIDTH, DEFAULT_SURFACE_VIEW_HEIGHT);
730         mSvCreatedLatch.await(5, TimeUnit.SECONDS);
731 
732         // Tap where the embedded window is placed to ensure focus is given via touch
733         globalTapOnWindowCenter(() -> {
734             try {
735                 return mTestService.getWindowToken();
736             } catch (RemoteException e) {
737                 fail("Could not get token from service, got " + e);
738                 return null;
739             }
740         });
741         assertTrue(mTestService.waitForFocus(true));
742         // assert host does not have focus
743         assertWindowFocused(mSurfaceView, false);
744 
745         // Tap where the host window is placed to ensure focus is given back to host when touched
746         globalTapOnWindowCenter(mViewParent::getWindowToken);
747         assertTrue(mTestService.waitForFocus(false));
748         // assert host does not have focus
749         assertWindowFocused(mViewParent, true);
750     }
751 
752     @Test
testWindowResumes_FocusTransfersToEmbedded()753     public void testWindowResumes_FocusTransfersToEmbedded() throws Throwable {
754         mEmbeddedView = new Button(mActivity);
755         addSurfaceView(DEFAULT_SURFACE_VIEW_WIDTH, DEFAULT_SURFACE_VIEW_HEIGHT);
756         mInstrumentation.waitForIdleSync();
757         waitUntilEmbeddedViewDrawn();
758 
759         // When surface view is focused, it should transfer focus to the embedded view.
760         requestSurfaceViewFocus();
761         assertWindowFocused(mEmbeddedView, true);
762         // assert host does not have focus
763         assertWindowFocused(mSurfaceView, false);
764 
765         WindowManager wm = mActivity.getSystemService(WindowManager.class);
766         View childView = new Button(mActivity);
767         mActivityRule.runOnUiThread(() -> {
768             final WindowManager.LayoutParams childWindowParams =
769                     new WindowManager.LayoutParams(25, 25,
770                             WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.OPAQUE);
771             wm.addView(childView, childWindowParams);
772         });
773         waitUntilViewDrawn(childView);
774         assertWindowFocused(childView, true);
775         // Neither host or embedded should be focus
776         assertWindowFocused(mSurfaceView, false);
777         assertWindowFocused(mEmbeddedView, false);
778 
779         mActivityRule.runOnUiThread(() -> wm.removeView(childView));
780         mInstrumentation.waitForIdleSync();
781 
782         assertWindowFocused(mEmbeddedView, true);
783         assertWindowFocused(mSurfaceView, false);
784     }
785 
786     @Test
testImeVisible()787     public void testImeVisible() throws Throwable {
788         assumeTrue(MSG_NO_MOCK_IME, supportsInstallableIme());
789         EditText editText = new EditText(mActivity);
790 
791         mEmbeddedView = editText;
792         editText.setBackgroundColor(Color.BLUE);
793         editText.setPrivateImeOptions("Hello reader! This is a random string");
794         addSurfaceView(DEFAULT_SURFACE_VIEW_WIDTH, DEFAULT_SURFACE_VIEW_HEIGHT);
795         mInstrumentation.waitForIdleSync();
796         waitUntilEmbeddedViewDrawn();
797 
798         // When surface view is focused, it should transfer focus to the embedded view.
799         requestSurfaceViewFocus();
800         assertWindowFocused(mEmbeddedView, true);
801         // assert host does not have focus
802         assertWindowFocused(mSurfaceView, false);
803 
804         mCtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mActivityRule, mSurfaceView);
805         final ImeEventStream stream = mImeSession.openEventStream();
806         expectEvent(stream, editorMatcher("onStartInputView",
807                 editText.getPrivateImeOptions()), TIMEOUT_MS);
808     }
809 
810     @Test
testNotFocusable()811     public void testNotFocusable() throws Throwable {
812         mEmbeddedView = new Button(mActivity);
813         addSurfaceView(DEFAULT_SURFACE_VIEW_WIDTH, DEFAULT_SURFACE_VIEW_HEIGHT);
814         mEmbeddedLayoutParams = new WindowManager.LayoutParams(mEmbeddedViewWidth,
815                 mEmbeddedViewHeight, WindowManager.LayoutParams.TYPE_APPLICATION, 0,
816                 PixelFormat.OPAQUE);
817         mActivityRule.runOnUiThread(() -> {
818             mEmbeddedLayoutParams.flags |= FLAG_NOT_FOCUSABLE;
819             mVr.relayout(mEmbeddedLayoutParams);
820         });
821         mInstrumentation.waitForIdleSync();
822         waitUntilEmbeddedViewDrawn();
823 
824         // When surface view is focused, nothing should happen since the embedded view is not
825         // focusable.
826         requestSurfaceViewFocus();
827         assertWindowFocused(mEmbeddedView, false);
828         // assert host has focus
829         assertWindowFocused(mSurfaceView, true);
830     }
831 
832     @Test
testFocusBeforeAddingEmbedded()833     public void testFocusBeforeAddingEmbedded() throws Throwable {
834         addSurfaceView(DEFAULT_SURFACE_VIEW_WIDTH, DEFAULT_SURFACE_VIEW_HEIGHT);
835         // Request focus to the SV before adding the embedded.
836         requestSurfaceViewFocus();
837         mSvCreatedLatch.await();
838         assertTrue("Failed to wait for sv to gain focus", waitForViewFocus(mSurfaceView, true));
839 
840         mEmbeddedView = new Button(mActivity);
841         mActivityRule.runOnUiThread(() -> {
842             addViewToSurfaceView(mSurfaceView, mEmbeddedView, mEmbeddedViewWidth,
843                     mEmbeddedViewHeight);
844         });
845         waitForWindowVisible(mEmbeddedView);
846         assertWindowFocused(mEmbeddedView, true);
847         assertWindowFocused(mSurfaceView, false);
848     }
849 
850     private static class SurfaceCreatedCallback implements SurfaceHolder.Callback {
851         private final CountDownLatch mSurfaceCreated;
852 
SurfaceCreatedCallback(CountDownLatch latch)853         SurfaceCreatedCallback(CountDownLatch latch) {
854             mSurfaceCreated = latch;
855         }
856 
857         @Override
surfaceCreated(SurfaceHolder holder)858         public void surfaceCreated(SurfaceHolder holder) {
859             mSurfaceCreated.countDown();
860         }
861 
862         @Override
surfaceDestroyed(SurfaceHolder holder)863         public void surfaceDestroyed(SurfaceHolder holder) {
864         }
865 
866         @Override
surfaceChanged(SurfaceHolder holder, int format, int width, int height)867         public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
868         }
869     }
870 
871     @Test
testCanCopySurfacePackage()872     public void testCanCopySurfacePackage() throws Throwable {
873         // Create a surface view and wait for its surface to be created.
874         CountDownLatch surfaceCreated = new CountDownLatch(1);
875         mActivityRule.runOnUiThread(() -> {
876             final FrameLayout content = new FrameLayout(mActivity);
877             mSurfaceView = new SurfaceView(mActivity);
878             mSurfaceView.setZOrderOnTop(true);
879             content.addView(mSurfaceView, new FrameLayout.LayoutParams(
880                     DEFAULT_SURFACE_VIEW_WIDTH, DEFAULT_SURFACE_VIEW_HEIGHT,
881                     Gravity.LEFT | Gravity.TOP));
882             mActivity.setContentView(content, new ViewGroup.LayoutParams(DEFAULT_SURFACE_VIEW_WIDTH,
883                     DEFAULT_SURFACE_VIEW_HEIGHT));
884             mSurfaceView.getHolder().addCallback(new SurfaceCreatedCallback(surfaceCreated));
885 
886             // Create an embedded view.
887             mVr = new SurfaceControlViewHost(mActivity, mActivity.getDisplay(),
888                     mSurfaceView.getHostToken());
889             mEmbeddedView = new Button(mActivity);
890             mEmbeddedView.setOnClickListener((View v) -> mClicked = true);
891             mVr.setView(mEmbeddedView, mEmbeddedViewWidth, mEmbeddedViewHeight);
892 
893         });
894         assertTrue("Failed to wait for SurfaceView created",
895                 surfaceCreated.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS));
896 
897         // Make a copy of the SurfacePackage and release the original package.
898         SurfacePackage surfacePackage = mVr.getSurfacePackage();
899         SurfacePackage copy = new SurfacePackage(surfacePackage);
900         surfacePackage.release();
901 
902         CountDownLatch surfacePackageReparented = new CountDownLatch(1);
903         mActivityRule.runOnUiThread(() -> {
904             mSurfaceView.setChildSurfacePackage(copy);
905             SurfaceControl.Transaction t = new SurfaceControl.Transaction();
906             t.addTransactionCommittedListener(Runnable::run, surfacePackageReparented::countDown);
907             mSurfaceView.getRootSurfaceControl().applyTransactionOnDraw(t);
908         });
909         assertTrue("Failed to wait for surface package to get reparented",
910                 surfacePackageReparented.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS));
911 
912         mInstrumentation.waitForIdleSync();
913         waitUntilEmbeddedViewDrawn();
914 
915         // Check if SurfacePackage copy remains valid even though the original package has
916         // been released.
917         globalTapOnWindowCenter(mEmbeddedView::getWindowToken);
918         PollingCheck.waitFor(() -> mClicked);
919     }
920 
921     @Test
testTransferSurfacePackage()922     public void testTransferSurfacePackage() throws Throwable {
923         // Create a surface view and wait for its surface to be created.
924         CountDownLatch surfaceCreated = new CountDownLatch(1);
925         CountDownLatch surface2Created = new CountDownLatch(1);
926         CountDownLatch viewDetached = new CountDownLatch(1);
927         AtomicReference<SurfacePackage> surfacePackageRef = new AtomicReference<>(null);
928         AtomicReference<SurfacePackage> surfacePackageCopyRef = new AtomicReference<>(null);
929         AtomicReference<SurfaceView> secondSurfaceRef = new AtomicReference<>(null);
930 
931         mActivityRule.runOnUiThread(() -> {
932             final FrameLayout content = new FrameLayout(mActivity);
933             mSurfaceView = new SurfaceView(mActivity);
934             mSurfaceView.setZOrderOnTop(true);
935             content.addView(mSurfaceView, new FrameLayout.LayoutParams(DEFAULT_SURFACE_VIEW_WIDTH,
936                     DEFAULT_SURFACE_VIEW_HEIGHT, Gravity.LEFT | Gravity.TOP));
937             mActivity.setContentView(content, new ViewGroup.LayoutParams(DEFAULT_SURFACE_VIEW_WIDTH,
938                     DEFAULT_SURFACE_VIEW_HEIGHT));
939             mSurfaceView.getHolder().addCallback(new SurfaceCreatedCallback(surfaceCreated));
940 
941             // Create an embedded view.
942             mVr = new SurfaceControlViewHost(mActivity, mActivity.getDisplay(),
943                     mSurfaceView.getHostToken());
944             mEmbeddedView = new Button(mActivity);
945             mEmbeddedView.setOnClickListener((View v) -> mClicked = true);
946             mVr.setView(mEmbeddedView, mEmbeddedViewWidth, mEmbeddedViewHeight);
947 
948             SurfacePackage surfacePackage = mVr.getSurfacePackage();
949             surfacePackageRef.set(surfacePackage);
950             surfacePackageCopyRef.set(new SurfacePackage(surfacePackage));
951 
952             // Assign the surface package to the first surface
953             mSurfaceView.setChildSurfacePackage(surfacePackage);
954 
955             // Create the second surface view to which we'll assign the surface package copy
956             SurfaceView secondSurface = new SurfaceView(mActivity);
957             secondSurfaceRef.set(secondSurface);
958 
959             mSurfaceView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
960                 @Override
961                 public void onViewAttachedToWindow(View v) {
962                 }
963 
964                 @Override
965                 public void onViewDetachedFromWindow(View v) {
966                     viewDetached.countDown();
967                 }
968             });
969 
970             secondSurface.getHolder().addCallback(new SurfaceCreatedCallback(surface2Created));
971 
972         });
973         surfaceCreated.await();
974 
975         // Add the second surface view and assign it the surface package copy
976         mActivityRule.runOnUiThread(() -> {
977             ViewGroup content = (ViewGroup) mSurfaceView.getParent();
978             content.addView(secondSurfaceRef.get(),
979                     new FrameLayout.LayoutParams(DEFAULT_SURFACE_VIEW_WIDTH,
980                             DEFAULT_SURFACE_VIEW_HEIGHT, Gravity.TOP | Gravity.LEFT));
981             secondSurfaceRef.get().setZOrderOnTop(true);
982             surfacePackageRef.get().release();
983             secondSurfaceRef.get().setChildSurfacePackage(surfacePackageCopyRef.get());
984 
985             content.removeView(mSurfaceView);
986         });
987 
988         // Wait for the first surface to be removed
989         surface2Created.await();
990         viewDetached.await();
991 
992         mInstrumentation.waitForIdleSync();
993         waitUntilEmbeddedViewDrawn();
994 
995         // Check if SurfacePackage copy remains valid even though the original package has
996         // been released and the original surface view removed.
997         mCtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mActivityRule,
998                 secondSurfaceRef.get());
999         assertTrue(mClicked);
1000     }
1001 
1002     @Test
testCanReplaceSurfacePackage()1003     public void testCanReplaceSurfacePackage() throws Throwable {
1004         // Create a surface view and wait for its surface to be created.
1005         CountDownLatch surfaceCreated = new CountDownLatch(1);
1006         mActivityRule.runOnUiThread(() -> {
1007             final FrameLayout content = new FrameLayout(mActivity);
1008             mSurfaceView = new SurfaceView(mActivity);
1009             mSurfaceView.setZOrderOnTop(true);
1010             content.addView(mSurfaceView, new FrameLayout.LayoutParams(
1011                     DEFAULT_SURFACE_VIEW_WIDTH, DEFAULT_SURFACE_VIEW_HEIGHT,
1012                     Gravity.LEFT | Gravity.TOP));
1013             mActivity.setContentView(content, new ViewGroup.LayoutParams(
1014                     DEFAULT_SURFACE_VIEW_WIDTH, DEFAULT_SURFACE_VIEW_HEIGHT));
1015             mSurfaceView.getHolder().addCallback(new SurfaceCreatedCallback(surfaceCreated));
1016 
1017             // Create an embedded view without click handling.
1018             mVr = new SurfaceControlViewHost(mActivity, mActivity.getDisplay(),
1019                     mSurfaceView.getHostToken());
1020             mEmbeddedView = new Button(mActivity);
1021             mVr.setView(mEmbeddedView, mEmbeddedViewWidth, mEmbeddedViewHeight);
1022             mSurfaceView.setChildSurfacePackage(mVr.getSurfacePackage());
1023         });
1024         surfaceCreated.await();
1025         mInstrumentation.waitForIdleSync();
1026         waitUntilEmbeddedViewDrawn();
1027 
1028         CountDownLatch hostReady = new CountDownLatch(1);
1029         // Create a second surface view and wait for its surface to be created.
1030         mActivityRule.runOnUiThread(() -> {
1031             // Create an embedded view.
1032             mVr = new SurfaceControlViewHost(mActivity, mActivity.getDisplay(),
1033                     mSurfaceView.getHostToken());
1034             mEmbeddedView = new Button(mActivity);
1035             mEmbeddedView.setOnClickListener((View v) -> mClicked = true);
1036             mVr.setView(mEmbeddedView, mEmbeddedViewWidth, mEmbeddedViewHeight);
1037             hostReady.countDown();
1038             mSurfaceView.setChildSurfacePackage(mVr.getSurfacePackage());
1039         });
1040         hostReady.await();
1041         mInstrumentation.waitForIdleSync();
1042         waitUntilEmbeddedViewDrawn();
1043 
1044 
1045         // Check to see if the click went through - this only would happen if the surface package
1046         // was replaced
1047         globalTapOnWindowCenter(mEmbeddedView::getWindowToken);
1048         PollingCheck.waitFor(() -> mClicked);
1049     }
1050 
1051     class MotionRecordingSurfaceView extends SurfaceView {
1052         boolean mGotEvent = false;
1053 
MotionRecordingSurfaceView(Context c)1054         MotionRecordingSurfaceView(Context c) {
1055             super(c);
1056         }
1057 
onTouchEvent(MotionEvent e)1058         public boolean onTouchEvent(MotionEvent e) {
1059             super.onTouchEvent(e);
1060             synchronized (this) {
1061                 mGotEvent = true;
1062             }
1063             return true;
1064         }
1065 
gotEvent()1066         boolean gotEvent() {
1067             synchronized (this) {
1068                 return mGotEvent;
1069             }
1070         }
1071 
reset()1072         void reset() {
1073             synchronized (this) {
1074                 mGotEvent = false;
1075             }
1076         }
1077     }
1078 
1079     static class TouchPunchingView extends View {
TouchPunchingView(Context context)1080         TouchPunchingView(Context context) {
1081             super(context);
1082         }
1083 
punchHoleInTouchableRegion()1084         void punchHoleInTouchableRegion() {
1085             getRootSurfaceControl().setTouchableRegion(new Region());
1086         }
1087     }
1088 
addMotionRecordingSurfaceView(int width, int height)1089     private void addMotionRecordingSurfaceView(int width, int height) throws Throwable {
1090         mActivityRule.runOnUiThread(() -> {
1091             final FrameLayout content = new FrameLayout(mActivity);
1092             mSurfaceView = new MotionRecordingSurfaceView(mActivity);
1093             mSurfaceView.setZOrderOnTop(true);
1094             content.addView(mSurfaceView, new FrameLayout.LayoutParams(
1095                     width, height, Gravity.LEFT | Gravity.TOP));
1096             mActivity.setContentView(content, new ViewGroup.LayoutParams(width, height));
1097             mSurfaceView.getHolder().addCallback(this);
1098         });
1099     }
1100 
1101     class ForwardingSurfaceView extends SurfaceView {
1102         SurfaceControlViewHost.SurfacePackage mPackage;
1103 
ForwardingSurfaceView(Context c)1104         ForwardingSurfaceView(Context c) {
1105             super(c);
1106         }
1107 
1108         @Override
onDetachedFromWindow()1109         protected void onDetachedFromWindow() {
1110             mPackage.notifyDetachedFromWindow();
1111         }
1112 
1113         @Override
onConfigurationChanged(Configuration newConfig)1114         protected void onConfigurationChanged(Configuration newConfig) {
1115             super.onConfigurationChanged(newConfig);
1116             mPackage.notifyConfigurationChanged(newConfig);
1117         }
1118 
1119         @Override
setChildSurfacePackage(SurfaceControlViewHost.SurfacePackage p)1120         public void setChildSurfacePackage(SurfaceControlViewHost.SurfacePackage p) {
1121             super.setChildSurfacePackage(p);
1122             mPackage = p;
1123         }
1124     }
1125 
1126     class DetachRecordingView extends View {
1127         boolean mDetached = false;
1128 
DetachRecordingView(Context c)1129         DetachRecordingView(Context c) {
1130             super(c);
1131         }
1132 
1133         @Override
onDetachedFromWindow()1134         protected void onDetachedFromWindow() {
1135             mDetached = true;
1136         }
1137     }
1138 
1139     class ConfigRecordingView extends View {
1140         CountDownLatch mLatch;
1141 
ConfigRecordingView(Context c, CountDownLatch latch)1142         ConfigRecordingView(Context c, CountDownLatch latch) {
1143             super(c);
1144             mLatch = latch;
1145         }
1146 
1147         @Override
onConfigurationChanged(Configuration newConfig)1148         protected void onConfigurationChanged(Configuration newConfig) {
1149             mLatch.countDown();
1150         }
1151     }
1152 
addForwardingSurfaceView(int width, int height)1153     private void addForwardingSurfaceView(int width, int height) throws Throwable {
1154         mActivityRule.runOnUiThread(() -> {
1155             final FrameLayout content = new FrameLayout(mActivity);
1156             mSurfaceView = new ForwardingSurfaceView(mActivity);
1157             mSurfaceView.setZOrderOnTop(true);
1158             content.addView(mSurfaceView, new FrameLayout.LayoutParams(
1159                     width, height, Gravity.LEFT | Gravity.TOP));
1160             mViewParent = content;
1161             mActivity.setContentView(content, new ViewGroup.LayoutParams(width, height));
1162             mSurfaceView.getHolder().addCallback(this);
1163         });
1164     }
1165 
1166     @Test
testEmbeddedViewCanSetTouchableRegion()1167     public void testEmbeddedViewCanSetTouchableRegion() throws Throwable {
1168         TouchPunchingView tpv;
1169         mEmbeddedView = tpv = new TouchPunchingView(mActivity);
1170 
1171         addMotionRecordingSurfaceView(DEFAULT_SURFACE_VIEW_WIDTH, DEFAULT_SURFACE_VIEW_HEIGHT);
1172         mInstrumentation.waitForIdleSync();
1173         waitUntilEmbeddedViewDrawn();
1174         mCtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mActivityRule, mSurfaceView);
1175         mInstrumentation.waitForIdleSync();
1176 
1177         MotionRecordingSurfaceView mrsv = (MotionRecordingSurfaceView) mSurfaceView;
1178         assertFalse(mrsv.gotEvent());
1179         mActivityRule.runOnUiThread(() -> {
1180             tpv.punchHoleInTouchableRegion();
1181         });
1182         mInstrumentation.waitForIdleSync();
1183         mCtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mActivityRule, mSurfaceView);
1184         mInstrumentation.waitForIdleSync();
1185         assertTrue(mrsv.gotEvent());
1186     }
1187 
1188     @Test
forwardDetachedFromWindow()1189     public void forwardDetachedFromWindow() throws Throwable {
1190         DetachRecordingView drv = new DetachRecordingView(mActivity);
1191         mEmbeddedView = drv;
1192         addForwardingSurfaceView(100, 100);
1193         mInstrumentation.waitForIdleSync();
1194         waitUntilEmbeddedViewDrawn();
1195 
1196         assertFalse(drv.mDetached);
1197         mActivityRule.runOnUiThread(() -> {
1198             mViewParent.removeView(mSurfaceView);
1199         });
1200         mInstrumentation.waitForIdleSync();
1201         assertTrue(drv.mDetached);
1202     }
1203 
1204     @Test
forwardConfigurationChange()1205     public void forwardConfigurationChange() throws Throwable {
1206         if (!supportsOrientationRequest()) {
1207             return;
1208         }
1209         final CountDownLatch embeddedConfigLatch = new CountDownLatch(1);
1210         ConfigRecordingView crv = new ConfigRecordingView(mActivity, embeddedConfigLatch);
1211         mEmbeddedView = crv;
1212         addForwardingSurfaceView(100, 100);
1213         mInstrumentation.waitForIdleSync();
1214         waitUntilEmbeddedViewDrawn();
1215         mActivityRule.runOnUiThread(() -> {
1216             int orientation = mActivity.getResources().getConfiguration().orientation;
1217             if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
1218                 orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
1219             } else {
1220                 orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
1221             }
1222             mActivity.setRequestedOrientation(orientation);
1223         });
1224         embeddedConfigLatch.await(3, TimeUnit.SECONDS);
1225         mInstrumentation.waitForIdleSync();
1226         mActivityRule.runOnUiThread(() -> {
1227             assertEquals(mEmbeddedView.getResources().getConfiguration().orientation,
1228                     mSurfaceView.getResources().getConfiguration().orientation);
1229         });
1230     }
1231 
1232     @Test
1233     @DebugInputRule.DebugInput(bug = 329439551)
testEmbeddedViewReceivesInputOnBottom()1234     public void testEmbeddedViewReceivesInputOnBottom() throws Throwable {
1235         mEmbeddedView = new Button(mActivity);
1236         mEmbeddedView.setOnClickListener((View v) -> {
1237             mClicked = true;
1238         });
1239 
1240         addSurfaceView(DEFAULT_SURFACE_VIEW_WIDTH, DEFAULT_SURFACE_VIEW_HEIGHT, false);
1241         mInstrumentation.waitForIdleSync();
1242         waitUntilEmbeddedViewDrawn();
1243 
1244         // We should receive no input until we punch a hole
1245         globalTapOnViewCenter(mSurfaceView);
1246         mInstrumentation.waitForIdleSync();
1247         assertFalse(mClicked);
1248 
1249         String originalRegion = getTouchableRegionFromDump();
1250 
1251         mActivityRule.runOnUiThread(() -> {
1252             mSurfaceView.getRootSurfaceControl().setTouchableRegion(new Region(0, 0, 1, 1));
1253         });
1254         mInstrumentation.waitForIdleSync();
1255         // ViewRootImpl sends the touchable region to the WM via a one-way call, which is great
1256         // for performance...however not so good for testability, we have no way
1257         // to verify it has arrived! It doesn't make so much sense to bloat
1258         // the system image size with a completion callback for just this one test
1259         // so we settle for some inelegant spin-polling on the WM dump.
1260         // In the future when we revisit WM/Client interface and transactionalize
1261         // everything, we should have a standard way to wait on the completion of async
1262         // operations
1263         waitForTouchableRegionChanged(originalRegion);
1264         waitForStableWindowGeometry(WAIT_TIMEOUT_S, TimeUnit.SECONDS);
1265 
1266         globalTapOnViewCenter(mSurfaceView);
1267         PollingCheck.waitFor(() -> mClicked);
1268     }
1269 
getService()1270     private ICrossProcessSurfaceControlViewHostTestService getService() throws Exception {
1271         return mConnections.computeIfAbsent("android.server.wm.scvh", this::connect)
1272                 .get(TIMEOUT_MS);
1273     }
1274 
repackage(String packageName, ComponentName baseComponent)1275     private static ComponentName repackage(String packageName, ComponentName baseComponent) {
1276         return new ComponentName(packageName, baseComponent.getClassName());
1277     }
1278 
connect( String packageName)1279     private FutureConnection<ICrossProcessSurfaceControlViewHostTestService> connect(
1280             String packageName) {
1281         FutureConnection<ICrossProcessSurfaceControlViewHostTestService> connection =
1282                 new FutureConnection<>(
1283                         ICrossProcessSurfaceControlViewHostTestService.Stub::asInterface);
1284         Intent intent = new Intent();
1285         intent.setComponent(repackage(packageName,
1286                 Components.CrossProcessSurfaceControlViewHostTestService.COMPONENT));
1287         assertTrue(mInstrumentation.getContext().bindService(intent,
1288                 connection, Context.BIND_AUTO_CREATE));
1289         return connection;
1290     }
1291 
1292     @Test
testHostInputTokenAllowsObscuredTouches()1293     public void testHostInputTokenAllowsObscuredTouches() throws Throwable {
1294         mTestService = getService();
1295         assertTrue(mTestService != null);
1296 
1297         addSurfaceView(DEFAULT_SURFACE_VIEW_WIDTH, DEFAULT_SURFACE_VIEW_HEIGHT, false);
1298         assertTrue("Failed to wait for SV to get created",
1299                 mSvCreatedLatch.await(5, TimeUnit.SECONDS));
1300         mActivityRule.runOnUiThread(() -> {
1301             mSurfaceView.getRootSurfaceControl().setTouchableRegion(new Region());
1302         });
1303         // TODO(b/279051608): Add touchable regions in WindowInfo test so we can make sure the
1304         // touchable regions for the host have been set before proceeding.
1305         assertTrue("Failed to wait for host window to be visible",
1306                 waitForWindowVisible(mSurfaceView));
1307         assertTrue("Failed to wait for embedded window to be visible",
1308                 waitForWindowVisible(mTestService.getWindowToken()));
1309 
1310         waitForStableWindowGeometry(WAIT_TIMEOUT_S, TimeUnit.SECONDS);
1311         globalTapOnViewCenter(mSurfaceView);
1312 
1313         MotionEvent motionEvent = mTestService.getMotionEvent();
1314         assertThat(motionEvent, allOf(withMotionAction(MotionEvent.ACTION_DOWN),
1315                 withFlags(MotionEvent.FLAG_WINDOW_IS_OBSCURED)));
1316         motionEvent = mTestService.getMotionEvent();
1317         assertThat(motionEvent, allOf(withMotionAction(MotionEvent.ACTION_UP),
1318                 withFlags(MotionEvent.FLAG_WINDOW_IS_OBSCURED)));
1319 
1320     }
1321 
1322     @Test
testNoHostInputTokenDisallowsObscuredTouches()1323     public void testNoHostInputTokenDisallowsObscuredTouches() throws Throwable {
1324         mTestService = getService();
1325         mRemoteSurfacePackage = mTestService.getSurfacePackage(new Binder());
1326         assertTrue(mRemoteSurfacePackage != null);
1327 
1328         addSurfaceView(DEFAULT_SURFACE_VIEW_WIDTH, DEFAULT_SURFACE_VIEW_HEIGHT, false);
1329         assertTrue("Failed to wait for SV to get created",
1330                 mSvCreatedLatch.await(5, TimeUnit.SECONDS));
1331         mActivityRule.runOnUiThread(() -> {
1332             mSurfaceView.getRootSurfaceControl().setTouchableRegion(new Region());
1333         });
1334         // TODO(b/279051608): Add touchable regions in WindowInfo test so we can make sure the
1335         // touchable regions for the host have been set before proceeding.
1336         assertTrue("Failed to wait for host window to be visible",
1337                 waitForWindowVisible(mSurfaceView));
1338         assertTrue("Failed to wait for embedded window to be visible",
1339                 waitForWindowVisible(mTestService.getWindowToken()));
1340 
1341         globalTapOnViewCenter(mSurfaceView);
1342 
1343         assertNull(mTestService.getMotionEvent());
1344     }
1345 
1346     @Test
testPopupWindowReceivesInput()1347     public void testPopupWindowReceivesInput() throws Throwable {
1348         mEmbeddedView = new Button(mActivity);
1349         mEmbeddedView.setOnClickListener((View v) -> {
1350             mClicked = true;
1351         });
1352         addSurfaceView(DEFAULT_SURFACE_VIEW_WIDTH, DEFAULT_SURFACE_VIEW_HEIGHT);
1353         mInstrumentation.waitForIdleSync();
1354         waitUntilEmbeddedViewDrawn();
1355 
1356         mActivityRule.runOnUiThread(() -> {
1357             PopupWindow pw = new PopupWindow();
1358             mPopupWindow = pw;
1359             Button popupButton = new Button(mActivity);
1360             popupButton.setOnClickListener((View v) -> {
1361                 mPopupClicked = true;
1362             });
1363             pw.setWidth(DEFAULT_SURFACE_VIEW_WIDTH);
1364             pw.setHeight(DEFAULT_SURFACE_VIEW_HEIGHT);
1365             pw.setContentView(popupButton);
1366             pw.showAsDropDown(mEmbeddedView);
1367         });
1368         mInstrumentation.waitForIdleSync();
1369 
1370         mCtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mActivityRule, mSurfaceView);
1371         assertTrue(mPopupClicked);
1372         assertFalse(mClicked);
1373 
1374         mActivityRule.runOnUiThread(() -> {
1375             mPopupWindow.dismiss();
1376         });
1377         mInstrumentation.waitForIdleSync();
1378 
1379         mCtsTouchUtils.emulateTapOnViewCenter(mInstrumentation, mActivityRule, mSurfaceView);
1380         mInstrumentation.waitForIdleSync();
1381         assertTrue(mClicked);
1382     }
1383 
1384     @Test
testPopupWindowPosition()1385     public void testPopupWindowPosition() throws Throwable {
1386         mEmbeddedView = new View(mActivity);
1387         addSurfaceView(DEFAULT_SURFACE_VIEW_WIDTH, DEFAULT_SURFACE_VIEW_HEIGHT);
1388         mInstrumentation.waitForIdleSync();
1389         waitUntilEmbeddedViewDrawn();
1390 
1391         mActivityRule.runOnUiThread(() -> {
1392             View popupContent = new View(mActivity);
1393             popupContent.setBackgroundColor(Color.BLUE);
1394 
1395             mPopupWindow = new PopupWindow();
1396             mPopupWindow.setWidth(50);
1397             mPopupWindow.setHeight(50);
1398             mPopupWindow.setContentView(popupContent);
1399             mPopupWindow.showAtLocation(mEmbeddedView, Gravity.BOTTOM | Gravity.RIGHT, 0, 0);
1400         });
1401 
1402         Predicate<List<WindowInfo>> hasExpectedFrame = windowInfos -> {
1403             if (mPopupWindow == null) {
1404                 return false;
1405             }
1406 
1407             IBinder parentWindowToken = mEmbeddedView.getWindowToken();
1408             IBinder popupWindowToken = mPopupWindow.getContentView().getWindowToken();
1409             if (parentWindowToken == null || popupWindowToken == null) {
1410                 return false;
1411             }
1412 
1413             Rect parentBounds = null;
1414             Rect popupBounds = null;
1415             for (WindowInfo windowInfo : windowInfos) {
1416                 if (!windowInfo.isVisible) {
1417                     continue;
1418                 }
1419                 if (windowInfo.windowToken == parentWindowToken) {
1420                     parentBounds = windowInfo.bounds;
1421                 } else if (windowInfo.windowToken == popupWindowToken) {
1422                     popupBounds = windowInfo.bounds;
1423                 }
1424             }
1425 
1426             if (parentBounds == null) {
1427                 return false;
1428             }
1429 
1430             var expectedBounds = new Rect(parentBounds.left + 50, parentBounds.top + 50,
1431                     parentBounds.left + 100, parentBounds.top + 100);
1432             return expectedBounds.equals(popupBounds);
1433         };
1434         assertTrue(waitForWindowInfos(hasExpectedFrame, 5, TimeUnit.SECONDS));
1435     }
1436 
1437     @Test
testFloatingWindowWrapContent()1438     public void testFloatingWindowWrapContent() throws Throwable {
1439         mEmbeddedView = new View(mActivity);
1440         addSurfaceView(DEFAULT_SURFACE_VIEW_WIDTH, DEFAULT_SURFACE_VIEW_HEIGHT);
1441         mInstrumentation.waitForIdleSync();
1442         waitUntilEmbeddedViewDrawn();
1443 
1444         View popupContent = new View(mActivity);
1445         popupContent.setBackgroundColor(Color.BLUE);
1446         popupContent.setLayoutParams(new ViewGroup.LayoutParams(50, 50));
1447 
1448         FrameLayout popupView = new FrameLayout(mActivity);
1449         popupView.addView(popupContent);
1450 
1451         WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
1452         layoutParams.setTitle("FloatingWindow");
1453         layoutParams.gravity = Gravity.TOP | Gravity.LEFT;
1454         layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
1455         layoutParams.width = ViewGroup.LayoutParams.WRAP_CONTENT;
1456         layoutParams.token = mEmbeddedView.getWindowToken();
1457 
1458         mActivityRule.runOnUiThread(() -> {
1459             WindowManager windowManager = mActivity.getSystemService(WindowManager.class);
1460             windowManager.addView(popupView, layoutParams);
1461         });
1462 
1463         Predicate<WindowInfo> hasExpectedDimensions =
1464                 windowInfo -> windowInfo.bounds.width() == 50 && windowInfo.bounds.height() == 50;
1465         // We pass popupView::getWindowToken as a java.util.function.Supplier
1466         // because the popupView is initially unattached and doesn't have a
1467         // window token. The supplier is called each time the predicate is
1468         // tested, eventually returning the window token.
1469         assertTrue(waitForWindowInfo(hasExpectedDimensions, 5, TimeUnit.SECONDS,
1470                 popupView::getWindowToken, mActivity.getDisplay().getDisplayId()));
1471     }
1472 
1473     @Test
testFloatingWindowMatchParent()1474     public void testFloatingWindowMatchParent() throws Throwable {
1475         mEmbeddedView = new View(mActivity);
1476         mEmbeddedViewWidth = 50;
1477         mEmbeddedViewHeight = 50;
1478         addSurfaceView(100, 100);
1479         mInstrumentation.waitForIdleSync();
1480 
1481         View popupView = new FrameLayout(mActivity);
1482         popupView.setBackgroundColor(Color.BLUE);
1483 
1484         WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
1485         layoutParams.setTitle("FloatingWindow");
1486         layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT;
1487         layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
1488         layoutParams.token = mEmbeddedView.getWindowToken();
1489 
1490         mActivityRule.runOnUiThread(() -> {
1491             WindowManager windowManager = mActivity.getSystemService(WindowManager.class);
1492             windowManager.addView(popupView, layoutParams);
1493         });
1494 
1495         Predicate<WindowInfo> hasExpectedDimensions =
1496                 windowInfo -> windowInfo.bounds.width() == 50 && windowInfo.bounds.height() == 50;
1497         assertTrue(waitForWindowInfo(hasExpectedDimensions, 5, TimeUnit.SECONDS,
1498                 popupView::getWindowToken, mActivity.getDisplay().getDisplayId()));
1499     }
1500 
1501     class TouchTransferringView extends View {
1502         boolean mExpectsFirstMotion = true;
1503         boolean mExpectsCancel = false;
1504         boolean mGotCancel = false;
1505         // True if the test should use the WindowManager#transferTouchGesture API.
1506         private final boolean mUseTransferTouchGestureApi;
1507 
TouchTransferringView(Context c, boolean useTransferTouchGestureApi)1508         TouchTransferringView(Context c, boolean useTransferTouchGestureApi) {
1509             super(c);
1510             mUseTransferTouchGestureApi = useTransferTouchGestureApi;
1511         }
1512 
1513         @Override
onTouchEvent(MotionEvent ev)1514         public boolean onTouchEvent(MotionEvent ev) {
1515             int action = ev.getAction();
1516             synchronized (this) {
1517                 if (mExpectsFirstMotion) {
1518                     assertEquals(action, MotionEvent.ACTION_DOWN);
1519                     if (mUseTransferTouchGestureApi) {
1520                         assertTrue(mWm.transferTouchGesture(
1521                                 mVr.getSurfacePackage().getInputTransferToken(),
1522                                 mSurfaceView.getRootSurfaceControl().getInputTransferToken()));
1523                     } else {
1524                         assertTrue(mVr.transferTouchGestureToHost());
1525                     }
1526                     mExpectsFirstMotion = false;
1527                     mExpectsCancel = true;
1528                 } else if (mExpectsCancel) {
1529                     assertEquals(action, MotionEvent.ACTION_CANCEL);
1530                     mExpectsCancel = false;
1531                     mGotCancel = true;
1532                 }
1533                 this.notifyAll();
1534             }
1535             return true;
1536         }
1537 
waitForEmbeddedTouch()1538         void waitForEmbeddedTouch() {
1539             synchronized (this) {
1540                 if (!mExpectsFirstMotion) {
1541                     assertTrue(mExpectsCancel || mGotCancel);
1542                     return;
1543                 }
1544                 try {
1545                     this.wait();
1546                 } catch (Exception e) {
1547                 }
1548                 assertFalse(mExpectsFirstMotion);
1549             }
1550         }
1551 
waitForCancel()1552         void waitForCancel() {
1553             synchronized (this) {
1554                 if (!mExpectsCancel) {
1555                     return;
1556                 }
1557                 try {
1558                     this.wait();
1559                 } catch (Exception e) {
1560                 }
1561                 assertTrue(mGotCancel);
1562             }
1563         }
1564     }
1565 
testEmbeddedWindowCanTransferTouchGestureToHost(boolean useTransferTouchGestureApi)1566     private void testEmbeddedWindowCanTransferTouchGestureToHost(boolean useTransferTouchGestureApi)
1567             throws Throwable {
1568         // Inside the embedded view hierarchy, we set up a view that transfers touch
1569         // to the host upon receiving a touch event
1570         TouchTransferringView ttv = new TouchTransferringView(mActivity,
1571                 useTransferTouchGestureApi);
1572         mEmbeddedView = ttv;
1573         addSurfaceView(DEFAULT_SURFACE_VIEW_WIDTH, DEFAULT_SURFACE_VIEW_HEIGHT);
1574         mInstrumentation.waitForIdleSync();
1575         waitUntilEmbeddedViewDrawn();
1576         // On the host SurfaceView, we set a motion consumer which expects to receive one event.
1577         mHostGotEvent = false;
1578         mSurfaceViewMotionConsumer = (ev) -> {
1579             synchronized (this) {
1580                 mHostGotEvent = true;
1581                 this.notifyAll();
1582             }
1583         };
1584 
1585         // Prepare to inject an event offset one pixel from the top of the SurfaceViews location
1586         // on-screen.
1587         final int[] viewOnScreenXY = new int[2];
1588         mSurfaceView.getLocationOnScreen(viewOnScreenXY);
1589         final int injectedX = viewOnScreenXY[0] + 1;
1590         final int injectedY = viewOnScreenXY[1] + 1;
1591         final UiAutomation uiAutomation = mInstrumentation.getUiAutomation();
1592         long downTime = SystemClock.uptimeMillis();
1593 
1594         // We inject a down event
1595         mCtsTouchUtils.injectDownEvent(mInstrumentation, downTime, injectedX, injectedY, null);
1596 
1597 
1598         // And this down event should arrive on the embedded view, which should transfer the touch
1599         // focus
1600         ttv.waitForEmbeddedTouch();
1601         ttv.waitForCancel();
1602 
1603         downTime = SystemClock.uptimeMillis();
1604         // Now we inject an up event
1605         mCtsTouchUtils.injectUpEvent(mInstrumentation, downTime, false, injectedX, injectedY, null);
1606         // This should arrive on the host now, since we have transferred the touch focus
1607         synchronized (this) {
1608             if (!mHostGotEvent) {
1609                 try {
1610                     this.wait();
1611                 } catch (Exception e) {
1612                 }
1613             }
1614         }
1615         assertTrue(mHostGotEvent);
1616     }
1617 
1618     @Test
testEmbeddedWindowCanTransferTouchGestureToHost_transferTouchGestureToHost()1619     public void testEmbeddedWindowCanTransferTouchGestureToHost_transferTouchGestureToHost()
1620             throws Throwable {
1621         testEmbeddedWindowCanTransferTouchGestureToHost(false);
1622     }
1623 
1624     @RequiresFlagsEnabled(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER)
1625     @Test
testEmbeddedWindowCanTransferTouchGestureToHost_transferTouchGesture()1626     public void testEmbeddedWindowCanTransferTouchGestureToHost_transferTouchGesture()
1627             throws Throwable {
1628         testEmbeddedWindowCanTransferTouchGestureToHost(true);
1629     }
1630 
1631     @Test
testKeepScreenOn()1632     public void testKeepScreenOn() throws Throwable {
1633         mEmbeddedView = new Button(mActivity);
1634         addSurfaceView(DEFAULT_SURFACE_VIEW_WIDTH, DEFAULT_SURFACE_VIEW_HEIGHT);
1635         mInstrumentation.waitForIdleSync();
1636         waitUntilEmbeddedViewDrawn();
1637 
1638         mWmState.computeState();
1639         WindowManagerState.WindowState windowState = mWmState.getWindowState(TEST_ACTIVITY);
1640         // Assert the KEEP_SCREEN_ON flag is not set on the main window yet.
1641         assertNotEquals(FLAG_KEEP_SCREEN_ON, (windowState.getFlags() & FLAG_KEEP_SCREEN_ON));
1642 
1643         final CountDownLatch keepScreenOnSetLatch = new CountDownLatch(2);
1644         mActivityRule.runOnUiThread(() -> {
1645             mEmbeddedView.setKeepScreenOn(true);
1646             mEmbeddedView.getViewTreeObserver().addOnDrawListener(keepScreenOnSetLatch::countDown);
1647             mSurfaceView.getViewTreeObserver().addOnDrawListener(keepScreenOnSetLatch::countDown);
1648         });
1649         keepScreenOnSetLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS);
1650 
1651         mWmState.computeState();
1652         windowState = mWmState.getWindowState(TEST_ACTIVITY);
1653         // Assert the KEEP_SCREEN_ON flag is now set on the main window.
1654         assertEquals(FLAG_KEEP_SCREEN_ON, (windowState.getFlags() & FLAG_KEEP_SCREEN_ON));
1655 
1656         final CountDownLatch keepScreenOnUnsetLatch = new CountDownLatch(2);
1657         mActivityRule.runOnUiThread(() -> {
1658             mEmbeddedView.setKeepScreenOn(false);
1659             mEmbeddedView.getViewTreeObserver().addOnDrawListener(
1660                     keepScreenOnUnsetLatch::countDown);
1661             mSurfaceView.getViewTreeObserver().addOnDrawListener(keepScreenOnUnsetLatch::countDown);
1662         });
1663         keepScreenOnUnsetLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS);
1664 
1665         mWmState.computeState();
1666         windowState = mWmState.getWindowState(TEST_ACTIVITY);
1667         // Assert the KEEP_SCREEN_ON flag is removed from the main window.
1668         assertNotEquals(FLAG_KEEP_SCREEN_ON, (windowState.getFlags() & FLAG_KEEP_SCREEN_ON));
1669     }
1670 
1671     @Test
testKeepScreenOnCrossProcess()1672     public void testKeepScreenOnCrossProcess() throws Throwable {
1673         mTestService = getService();
1674         assertNotNull(mTestService);
1675 
1676         addSurfaceView(DEFAULT_SURFACE_VIEW_WIDTH, DEFAULT_SURFACE_VIEW_HEIGHT);
1677         mSvCreatedLatch.await(5, TimeUnit.SECONDS);
1678 
1679         mWmState.computeState();
1680         WindowManagerState.WindowState windowState = mWmState.getWindowState(TEST_ACTIVITY);
1681         // Assert the KEEP_SCREEN_ON flag is not set on the main window yet.
1682         assertNotEquals(FLAG_KEEP_SCREEN_ON, (windowState.getFlags() & FLAG_KEEP_SCREEN_ON));
1683 
1684         final CountDownLatch countDownLatch = new CountDownLatch(1);
1685         mActivityRule.runOnUiThread(() -> mSurfaceView.getViewTreeObserver().addOnDrawListener(
1686                 countDownLatch::countDown));
1687         mTestService.setKeepScreenOnFlag(true);
1688         countDownLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS);
1689 
1690         mWmState.computeState();
1691         windowState = mWmState.getWindowState(TEST_ACTIVITY);
1692         // Assert the KEEP_SCREEN_ON flag is now set on the main window.
1693         assertEquals(FLAG_KEEP_SCREEN_ON, (windowState.getFlags() & FLAG_KEEP_SCREEN_ON));
1694 
1695         final CountDownLatch countDownLatch2 = new CountDownLatch(1);
1696         mActivityRule.runOnUiThread(() -> mSurfaceView.getViewTreeObserver().addOnDrawListener(
1697                 countDownLatch2::countDown));
1698         mTestService.setKeepScreenOnFlag(false);
1699         countDownLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS);
1700 
1701         mWmState.computeState();
1702         windowState = mWmState.getWindowState(TEST_ACTIVITY);
1703         // Assert the KEEP_SCREEN_ON flag is removed from the main window.
1704         assertNotEquals(FLAG_KEEP_SCREEN_ON, (windowState.getFlags() & FLAG_KEEP_SCREEN_ON));
1705     }
1706 
1707     @Test
testKeepScreenOnAfterDetachSCVH()1708     public void testKeepScreenOnAfterDetachSCVH() throws Throwable {
1709         mEmbeddedView = new Button(mActivity);
1710         mEmbeddedView.setKeepScreenOn(true);
1711         addSurfaceView(DEFAULT_SURFACE_VIEW_WIDTH, DEFAULT_SURFACE_VIEW_HEIGHT);
1712         mInstrumentation.waitForIdleSync();
1713         waitUntilEmbeddedViewDrawn();
1714 
1715         mWmState.computeState();
1716         WindowManagerState.WindowState windowState = mWmState.getWindowState(TEST_ACTIVITY);
1717         // Assert the KEEP_SCREEN_ON flag is not set on the main window yet.
1718         assertEquals(FLAG_KEEP_SCREEN_ON, (windowState.getFlags() & FLAG_KEEP_SCREEN_ON));
1719 
1720         // Remove the SurfaceView from main window.
1721         final CountDownLatch countDownLatch = new CountDownLatch(1);
1722         mActivityRule.runOnUiThread(() -> {
1723             mViewParent.removeView(mSurfaceView);
1724             mSurfaceView.getViewTreeObserver().addOnDrawListener(countDownLatch::countDown);
1725         });
1726         countDownLatch.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS);
1727 
1728         mWmState.computeState();
1729         windowState = mWmState.getWindowState(TEST_ACTIVITY);
1730         // Assert the KEEP_SCREEN_ON flag is removed from the main window.
1731         assertNotEquals(FLAG_KEEP_SCREEN_ON, (windowState.getFlags() & FLAG_KEEP_SCREEN_ON));
1732     }
1733 
1734     @RequiresFlagsEnabled(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER)
1735     @Test
testTransferHostTouchGestureToEmbedded()1736     public void testTransferHostTouchGestureToEmbedded() throws Throwable {
1737         mEmbeddedView = new Button(mActivity);
1738         addSurfaceView(DEFAULT_SURFACE_VIEW_WIDTH, DEFAULT_SURFACE_VIEW_HEIGHT, false /* onTop */);
1739         waitUntilEmbeddedViewDrawn();
1740 
1741         CountDownLatch receivedTouches = new CountDownLatch(1);
1742         boolean[] hostGotEvent = new boolean[1];
1743         boolean[] embeddedGotEvent = new boolean[1];
1744         mSurfaceViewMotionConsumer = (ev) -> {
1745             if (hostGotEvent[0]) {
1746                 return;
1747             }
1748             hostGotEvent[0] = true;
1749             mActivity.getWindowManager().transferTouchGesture(
1750                     mSurfaceView.getRootSurfaceControl().getInputTransferToken(),
1751                     mVr.getSurfacePackage().getInputTransferToken());
1752             receivedTouches.countDown();
1753         };
1754 
1755         mEmbeddedView.setOnTouchListener((v, event) -> {
1756             if (embeddedGotEvent[0]) {
1757                 return false;
1758             }
1759             embeddedGotEvent[0] = true;
1760             receivedTouches.countDown();
1761             return false;
1762         });
1763 
1764         final int[] viewInWindow = new int[2];
1765         mSurfaceView.getLocationInWindow(viewInWindow);
1766         Point point = new Point(viewInWindow[0] + 1, viewInWindow[1] + 1);
1767 
1768         CtsWindowInfoUtils.tapOnWindow(mInstrumentation, mSurfaceView::getWindowToken, point);
1769 
1770         assertTrue("Failed to receive touch from host=" + hostGotEvent[0] + " or embedded="
1771                 + embeddedGotEvent[0], receivedTouches.await(WAIT_TIMEOUT_S, TimeUnit.SECONDS));
1772 
1773         assertTrue("Failed to receive touch event in host window", hostGotEvent[0]);
1774         assertTrue("Failed to receive touch event in embedded window", embeddedGotEvent[0]);
1775     }
1776 }
1777