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