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