1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.server.wm; 18 19 import static android.Manifest.permission.ACCESS_SURFACE_FLINGER; 20 import static android.app.AppOpsManager.MODE_ALLOWED; 21 import static android.app.AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW; 22 import static android.server.wm.BuildUtils.HW_TIMEOUT_MULTIPLIER; 23 import static android.server.wm.UiDeviceUtils.pressUnlockButton; 24 import static android.server.wm.UiDeviceUtils.pressWakeupButton; 25 import static android.server.wm.WindowManagerState.STATE_RESUMED; 26 import static android.server.wm.overlay.Components.OverlayActivity.EXTRA_TOKEN; 27 import static android.view.WindowInsets.Type.navigationBars; 28 29 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; 30 31 import static com.google.common.truth.Truth.assertThat; 32 33 import static junit.framework.Assert.assertEquals; 34 import static junit.framework.Assert.assertTrue; 35 import static junit.framework.Assert.fail; 36 37 import static org.junit.Assume.assumeFalse; 38 import static org.junit.Assume.assumeTrue; 39 40 import android.app.Activity; 41 import android.app.ActivityManager; 42 import android.app.ActivityOptions; 43 import android.app.Instrumentation; 44 import android.app.NotificationManager; 45 import android.app.WindowConfiguration; 46 import android.content.ComponentName; 47 import android.content.ContentResolver; 48 import android.content.Context; 49 import android.content.Intent; 50 import android.content.res.Resources; 51 import android.graphics.Rect; 52 import android.hardware.input.InputManager; 53 import android.hardware.input.InputSettings; 54 import android.os.Bundle; 55 import android.os.ConditionVariable; 56 import android.os.Handler; 57 import android.os.IBinder; 58 import android.os.Looper; 59 import android.os.SystemClock; 60 import android.platform.test.annotations.Presubmit; 61 import android.server.wm.overlay.Components; 62 import android.server.wm.overlay.R; 63 import android.server.wm.shared.BlockingResultReceiver; 64 import android.server.wm.shared.IUntrustedTouchTestService; 65 import android.util.ArrayMap; 66 import android.util.ArraySet; 67 import android.view.Display; 68 import android.view.Gravity; 69 import android.view.MotionEvent; 70 import android.view.View; 71 import android.view.WindowManager; 72 import android.view.WindowManager.LayoutParams; 73 import android.widget.Toast; 74 75 import androidx.annotation.AnimRes; 76 import androidx.annotation.Nullable; 77 import androidx.test.ext.junit.rules.ActivityScenarioRule; 78 79 import com.android.compatibility.common.util.AppOpsUtils; 80 import com.android.compatibility.common.util.FeatureUtil; 81 import com.android.compatibility.common.util.SystemUtil; 82 83 import org.junit.After; 84 import org.junit.Before; 85 import org.junit.ClassRule; 86 import org.junit.Rule; 87 import org.junit.Test; 88 import org.junit.rules.TestName; 89 90 import java.util.Map; 91 import java.util.Set; 92 import java.util.concurrent.TimeUnit; 93 import java.util.concurrent.atomic.AtomicInteger; 94 95 @Presubmit 96 public class WindowUntrustedTouchTest { 97 private static final String TAG = "WindowUntrustedTouchTest"; 98 99 /** 100 * Opacity (or alpha) is represented as a half-precision floating point number (16b) in surface 101 * flinger and the conversion from the single-precision float provided to window manager happens 102 * in Layer::setAlpha() by android::half::ftoh(). So, many small non-zero values provided to 103 * window manager end up becoming zero due to loss of precision (this is fine as long as the 104 * zeros are also used to render the pixels on the screen). So, the minimum opacity possible is 105 * actually the minimum positive value representable in half-precision float, which is 106 * 0_00001_0000000000, whose equivalent in float is 0_01110001_00000000000000000000000. 107 * 108 * Note that from float -> half conversion code we don't produce any subnormal half-precision 109 * floats during conversion. 110 */ 111 public static final float MIN_POSITIVE_OPACITY = 112 Float.intBitsToFloat(0b00111000100000000000000000000000); 113 114 private static final float MAXIMUM_OBSCURING_OPACITY = .8f; 115 private static final long TIMEOUT_MS = 3000L; 116 private static final long MAX_ANIMATION_DURATION_MS = 3000L; 117 private static final long ANIMATION_DURATION_TOLERANCE_MS = 500L; 118 119 private static final int OVERLAY_COLOR = 0xFFFF0000; 120 private static final int ACTIVITY_COLOR = 0xFFFFFFFF; 121 122 private static final String APP_SELF = 123 WindowUntrustedTouchTest.class.getPackage().getName() + ".cts"; 124 private static final String APP_A = 125 android.server.wm.second.Components.class.getPackage().getName(); 126 private static final String APP_B = 127 android.server.wm.third.Components.class.getPackage().getName(); 128 private static final String WINDOW_1 = "W1"; 129 private static final String WINDOW_2 = "W2"; 130 131 private static final String[] APPS = {APP_A, APP_B}; 132 133 private static final String SETTING_MAXIMUM_OBSCURING_OPACITY = 134 "maximum_obscuring_opacity_for_touch"; 135 136 private final WindowManagerStateHelper mWmState = new WindowManagerStateHelper(); 137 private final Map<String, FutureConnection<IUntrustedTouchTestService>> mConnections = 138 new ArrayMap<>(); 139 private Instrumentation mInstrumentation; 140 private Context mContext; 141 private Resources mResources; 142 private ContentResolver mContentResolver; 143 private TouchHelper mTouchHelper; 144 private Handler mMainHandler; 145 private InputManager mInputManager; 146 private WindowManager mWindowManager; 147 private ActivityManager mActivityManager; 148 private NotificationManager mNotificationManager; 149 private TestActivity mActivity; 150 private View mContainer; 151 private Toast mToast; 152 private float mPreviousTouchOpacity; 153 private int mPreviousSawAppOp; 154 private final Set<String> mSawWindowsAdded = new ArraySet<>(); 155 private final AtomicInteger mTouchesReceived = new AtomicInteger(0); 156 157 @ClassRule 158 public static ActivityManagerTestBase.DisableImmersiveModeConfirmationRule 159 mDisableImmersiveModeConfirmationRule = 160 new ActivityManagerTestBase.DisableImmersiveModeConfirmationRule(); 161 162 @Rule 163 public TestName testNameRule = new TestName(); 164 165 @Rule 166 public ActivityScenarioRule<TestActivity> activityRule = 167 new ActivityScenarioRule<>(TestActivity.class, createLaunchActivityOptionsBundle()); 168 169 @Before setUp()170 public void setUp() throws Exception { 171 activityRule.getScenario().onActivity(activity -> { 172 mActivity = activity; 173 mContainer = mActivity.view; 174 // On ARC++, text toast is fixed on the screen. Its position may overlays the navigation 175 // bar. Hide it to ensure the text toast overlays the app. b/191075641 176 mContainer.getWindowInsetsController().hide(navigationBars()); 177 mContainer.setOnTouchListener(this::onTouchEvent); 178 }); 179 mInstrumentation = getInstrumentation(); 180 mContext = mInstrumentation.getContext(); 181 mResources = mContext.getResources(); 182 mContentResolver = mContext.getContentResolver(); 183 mTouchHelper = new TouchHelper(mInstrumentation, mWmState); 184 mMainHandler = new Handler(Looper.getMainLooper()); 185 mInputManager = mContext.getSystemService(InputManager.class); 186 mWindowManager = mContext.getSystemService(WindowManager.class); 187 mActivityManager = mContext.getSystemService(ActivityManager.class); 188 mNotificationManager = mContext.getSystemService(NotificationManager.class); 189 190 mPreviousSawAppOp = AppOpsUtils.getOpMode(APP_SELF, OPSTR_SYSTEM_ALERT_WINDOW); 191 AppOpsUtils.setOpMode(APP_SELF, OPSTR_SYSTEM_ALERT_WINDOW, MODE_ALLOWED); 192 mPreviousTouchOpacity = setMaximumObscuringOpacityForTouch(MAXIMUM_OBSCURING_OPACITY); 193 SystemUtil.runWithShellPermissionIdentity( 194 () -> mNotificationManager.setToastRateLimitingEnabled(false)); 195 196 pressWakeupButton(); 197 pressUnlockButton(); 198 } 199 200 @After tearDown()201 public void tearDown() throws Throwable { 202 mWmState.waitForAppTransitionIdleOnDisplay(Display.DEFAULT_DISPLAY); 203 mTouchesReceived.set(0); 204 removeOverlays(); 205 for (FutureConnection<IUntrustedTouchTestService> connection : mConnections.values()) { 206 mContext.unbindService(connection); 207 } 208 mConnections.clear(); 209 for (String app : APPS) { 210 stopPackage(app); 211 } 212 SystemUtil.runWithShellPermissionIdentity( 213 () -> mNotificationManager.setToastRateLimitingEnabled(true)); 214 setMaximumObscuringOpacityForTouch(mPreviousTouchOpacity); 215 AppOpsUtils.setOpMode(APP_SELF, OPSTR_SYSTEM_ALERT_WINDOW, mPreviousSawAppOp); 216 } 217 218 @Test testMaximumObscuringOpacity()219 public void testMaximumObscuringOpacity() throws Throwable { 220 // Setting the previous value since we override this on setUp() 221 setMaximumObscuringOpacityForTouch(mPreviousTouchOpacity); 222 223 assertEquals(0.8f, mInputManager.getMaximumObscuringOpacityForTouch()); 224 } 225 226 @Test testAfterSettingThreshold_returnsThresholdSet()227 public void testAfterSettingThreshold_returnsThresholdSet() 228 throws Throwable { 229 float threshold = .123f; 230 setMaximumObscuringOpacityForTouch(threshold); 231 232 assertEquals(threshold, mInputManager.getMaximumObscuringOpacityForTouch()); 233 } 234 235 @Test(expected = IllegalArgumentException.class) testAfterSettingThresholdLessThan0_throws()236 public void testAfterSettingThresholdLessThan0_throws() throws Throwable { 237 setMaximumObscuringOpacityForTouch(-.5f); 238 } 239 240 @Test(expected = IllegalArgumentException.class) testAfterSettingThresholdGreaterThan1_throws()241 public void testAfterSettingThresholdGreaterThan1_throws() throws Throwable { 242 setMaximumObscuringOpacityForTouch(1.5f); 243 } 244 245 /** SAWs */ 246 247 @Test testWhenOneSawWindowAboveThreshold_allowsTouch()248 public void testWhenOneSawWindowAboveThreshold_allowsTouch() throws Throwable { 249 addSawOverlay(APP_A, WINDOW_1, .9f); 250 251 mTouchHelper.tapOnViewCenter(mContainer); 252 253 // Opacity will be automatically capped and touches will pass through. 254 assertTouchReceived(); 255 } 256 257 @Test testWhenOneSawWindowBelowThreshold_allowsTouch()258 public void testWhenOneSawWindowBelowThreshold_allowsTouch() throws Throwable { 259 addSawOverlay(APP_A, WINDOW_1, .7f); 260 261 mTouchHelper.tapOnViewCenter(mContainer); 262 263 assertTouchReceived(); 264 } 265 266 @Test testWhenOneSawWindowWithZeroOpacity_allowsTouch()267 public void testWhenOneSawWindowWithZeroOpacity_allowsTouch() throws Throwable { 268 addSawOverlay(APP_A, WINDOW_1, 0f); 269 270 mTouchHelper.tapOnViewCenter(mContainer); 271 272 assertTouchReceived(); 273 } 274 275 @Test testWhenOneSawWindowAtThreshold_allowsTouch()276 public void testWhenOneSawWindowAtThreshold_allowsTouch() throws Throwable { 277 addSawOverlay(APP_A, WINDOW_1, MAXIMUM_OBSCURING_OPACITY); 278 279 mTouchHelper.tapOnViewCenter(mContainer); 280 281 assertTouchReceived(); 282 } 283 284 @Test testWhenTwoSawWindowsFromSameAppTogetherBelowThreshold_allowsTouch()285 public void testWhenTwoSawWindowsFromSameAppTogetherBelowThreshold_allowsTouch() 286 throws Throwable { 287 // Resulting opacity = 1 - (1 - 0.5)*(1 - 0.5) = .75 288 addSawOverlay(APP_A, WINDOW_1, .5f); 289 addSawOverlay(APP_A, WINDOW_2, .5f); 290 291 mTouchHelper.tapOnViewCenter(mContainer); 292 293 assertTouchReceived(); 294 } 295 296 @Test testWhenTwoSawWindowsFromSameAppTogetherAboveThreshold_blocksTouch()297 public void testWhenTwoSawWindowsFromSameAppTogetherAboveThreshold_blocksTouch() 298 throws Throwable { 299 // Resulting opacity = 1 - (1 - 0.7)*(1 - 0.7) = .91 300 addSawOverlay(APP_A, WINDOW_1, .7f); 301 addSawOverlay(APP_A, WINDOW_2, .7f); 302 303 mTouchHelper.tapOnViewCenter(mContainer); 304 305 assertTouchNotReceived(); 306 } 307 308 @Test testWhenTwoSawWindowsFromDifferentAppsEachBelowThreshold_allowsTouch()309 public void testWhenTwoSawWindowsFromDifferentAppsEachBelowThreshold_allowsTouch() 310 throws Throwable { 311 addSawOverlay(APP_A, WINDOW_1, .7f); 312 addSawOverlay(APP_B, WINDOW_2, .7f); 313 314 mTouchHelper.tapOnViewCenter(mContainer); 315 316 assertTouchReceived(); 317 } 318 319 @Test testWhenOneSawWindowAboveThresholdAndSelfSawWindow_allowsTouch()320 public void testWhenOneSawWindowAboveThresholdAndSelfSawWindow_allowsTouch() 321 throws Throwable { 322 addSawOverlay(APP_A, WINDOW_1, .9f); 323 addSawOverlay(APP_SELF, WINDOW_1, .7f); 324 325 mTouchHelper.tapOnViewCenter(mContainer); 326 327 // Opacity will be automatically capped and touches will pass through. 328 assertTouchReceived(); 329 } 330 331 @Test testWhenOneSawWindowBelowThresholdAndSelfSawWindow_allowsTouch()332 public void testWhenOneSawWindowBelowThresholdAndSelfSawWindow_allowsTouch() 333 throws Throwable { 334 addSawOverlay(APP_A, WINDOW_1, .7f); 335 addSawOverlay(APP_SELF, WINDOW_1, .7f); 336 337 mTouchHelper.tapOnViewCenter(mContainer); 338 339 assertTouchReceived(); 340 } 341 342 @Test testWhenTwoSawWindowsTogetherBelowThresholdAndSelfSawWindow_allowsTouch()343 public void testWhenTwoSawWindowsTogetherBelowThresholdAndSelfSawWindow_allowsTouch() 344 throws Throwable { 345 // Resulting opacity for A = 1 - (1 - 0.5)*(1 - 0.5) = .75 346 addSawOverlay(APP_A, WINDOW_1, .5f); 347 addSawOverlay(APP_A, WINDOW_1, .5f); 348 addSawOverlay(APP_SELF, WINDOW_1, .7f); 349 350 mTouchHelper.tapOnViewCenter(mContainer); 351 352 assertTouchReceived(); 353 } 354 355 @Test testWhenThresholdIs0AndSawWindowAtThreshold_allowsTouch()356 public void testWhenThresholdIs0AndSawWindowAtThreshold_allowsTouch() 357 throws Throwable { 358 setMaximumObscuringOpacityForTouch(0); 359 addSawOverlay(APP_A, WINDOW_1, 0); 360 361 mTouchHelper.tapOnViewCenter(mContainer); 362 363 assertTouchReceived(); 364 } 365 366 @Test testWhenThresholdIs0AndSawWindowAboveThreshold_allowsTouch()367 public void testWhenThresholdIs0AndSawWindowAboveThreshold_allowsTouch() 368 throws Throwable { 369 setMaximumObscuringOpacityForTouch(0); 370 addSawOverlay(APP_A, WINDOW_1, .1f); 371 372 mTouchHelper.tapOnViewCenter(mContainer); 373 374 // Opacity will be automatically capped and touches will pass through. 375 assertTouchReceived(); 376 } 377 378 @Test testWhenThresholdIs1AndSawWindowAtThreshold_allowsTouch()379 public void testWhenThresholdIs1AndSawWindowAtThreshold_allowsTouch() 380 throws Throwable { 381 setMaximumObscuringOpacityForTouch(1); 382 addSawOverlay(APP_A, WINDOW_1, 1); 383 384 mTouchHelper.tapOnViewCenter(mContainer); 385 386 assertTouchReceived(); 387 } 388 389 @Test testWhenThresholdIs1AndSawWindowBelowThreshold_allowsTouch()390 public void testWhenThresholdIs1AndSawWindowBelowThreshold_allowsTouch() 391 throws Throwable { 392 setMaximumObscuringOpacityForTouch(1); 393 addSawOverlay(APP_A, WINDOW_1, .9f); 394 395 mTouchHelper.tapOnViewCenter(mContainer); 396 397 assertTouchReceived(); 398 } 399 400 /** Activity windows */ 401 402 @Test testWhenOneActivityWindowBelowThreshold_blocksTouch()403 public void testWhenOneActivityWindowBelowThreshold_blocksTouch() 404 throws Throwable { 405 addActivityOverlay(APP_A, /* opacity */ .5f); 406 407 mTouchHelper.tapOnViewCenter(mContainer); 408 409 assertTouchNotReceived(); 410 } 411 412 @Test testWhenOneActivityWindowAboveThreshold_blocksTouch()413 public void testWhenOneActivityWindowAboveThreshold_blocksTouch() 414 throws Throwable { 415 addActivityOverlay(APP_A, /* opacity */ .9f); 416 417 mTouchHelper.tapOnViewCenter(mContainer); 418 419 assertTouchNotReceived(); 420 } 421 422 @Test testWhenOneActivityWindowWithZeroOpacity_allowsTouch()423 public void testWhenOneActivityWindowWithZeroOpacity_allowsTouch() 424 throws Throwable { 425 addActivityOverlay(APP_A, /* opacity */ 0f); 426 427 mTouchHelper.tapOnViewCenter(mContainer); 428 429 assertTouchReceived(); 430 } 431 432 @Test testWhenOneActivityWindowWithMinPositiveOpacity_blocksTouch()433 public void testWhenOneActivityWindowWithMinPositiveOpacity_blocksTouch() 434 throws Throwable { 435 addActivityOverlay(APP_A, /* opacity */ MIN_POSITIVE_OPACITY); 436 437 mTouchHelper.tapOnViewCenter(mContainer); 438 439 assertTouchNotReceived(); 440 } 441 442 @Test testWhenOneActivityWindowWithSmallOpacity_blocksTouch()443 public void testWhenOneActivityWindowWithSmallOpacity_blocksTouch() 444 throws Throwable { 445 addActivityOverlay(APP_A, /* opacity */ .01f); 446 447 mTouchHelper.tapOnViewCenter(mContainer); 448 449 assertTouchNotReceived(); 450 } 451 452 @Test testWhenOneSelfActivityWindow_allowsTouch()453 public void testWhenOneSelfActivityWindow_allowsTouch() throws Throwable { 454 addActivityOverlay(APP_SELF, /* opacity */ .9f); 455 456 mTouchHelper.tapOnViewCenter(mContainer); 457 458 assertTouchReceived(); 459 } 460 461 @Test testWhenTwoActivityWindowsFromDifferentAppsTogetherBelowThreshold_blocksTouch()462 public void testWhenTwoActivityWindowsFromDifferentAppsTogetherBelowThreshold_blocksTouch() 463 throws Throwable { 464 addActivityOverlay(APP_A, /* opacity */ .7f); 465 addActivityOverlay(APP_B, /* opacity */ .7f); 466 467 mTouchHelper.tapOnViewCenter(mContainer); 468 469 assertTouchNotReceived(); 470 } 471 472 @Test testWhenOneActivityWindowAndOneSawWindowTogetherBelowThreshold_blocksTouch()473 public void testWhenOneActivityWindowAndOneSawWindowTogetherBelowThreshold_blocksTouch() 474 throws Throwable { 475 addActivityOverlay(APP_A, /* opacity */ .5f); 476 addSawOverlay(APP_A, WINDOW_1, .5f); 477 478 mTouchHelper.tapOnViewCenter(mContainer); 479 480 assertTouchNotReceived(); 481 } 482 483 @Test testWhenOneActivityWindowAndOneSelfCustomToastWindow_blocksTouch()484 public void testWhenOneActivityWindowAndOneSelfCustomToastWindow_blocksTouch() 485 throws Throwable { 486 // Toast has to be before otherwise it would be blocked from background 487 addToastOverlay(APP_SELF, /* custom */ true); 488 addActivityOverlay(APP_A, /* opacity */ .5f); 489 490 mTouchHelper.tapOnViewCenter(mContainer); 491 492 assertTouchNotReceived(); 493 } 494 495 @Test testWhenOneActivityWindowAndOneSelfSawWindow_blocksTouch()496 public void testWhenOneActivityWindowAndOneSelfSawWindow_blocksTouch() 497 throws Throwable { 498 addActivityOverlay(APP_A, /* opacity */ .5f); 499 addSawOverlay(APP_SELF, WINDOW_1, .5f); 500 501 mTouchHelper.tapOnViewCenter(mContainer); 502 503 assertTouchNotReceived(); 504 } 505 506 @Test testWhenOneActivityWindowAndOneSawWindowBelowThreshold_blocksTouch()507 public void testWhenOneActivityWindowAndOneSawWindowBelowThreshold_blocksTouch() 508 throws Throwable { 509 addActivityOverlay(APP_A, /* opacity */ .5f); 510 addSawOverlay(APP_A, WINDOW_1, .5f); 511 512 mTouchHelper.tapOnViewCenter(mContainer); 513 514 assertTouchNotReceived(); 515 } 516 517 @Test testWhenOneActivityWindowAndOneSawWindowBelowThresholdFromDifferentApp_blocksTouch()518 public void testWhenOneActivityWindowAndOneSawWindowBelowThresholdFromDifferentApp_blocksTouch() 519 throws Throwable { 520 addActivityOverlay(APP_A, /* opacity */ .5f); 521 addSawOverlay(APP_B, WINDOW_1, .5f); 522 523 mTouchHelper.tapOnViewCenter(mContainer); 524 525 assertTouchNotReceived(); 526 } 527 528 /** Activity-type child windows on same activity */ 529 530 @Test testWhenActivityChildWindowWithSameTokenFromDifferentApp_allowsTouch()531 public void testWhenActivityChildWindowWithSameTokenFromDifferentApp_allowsTouch() 532 throws Exception { 533 IBinder token = mActivity.getWindow().getAttributes().token; 534 addActivityChildWindow(APP_A, WINDOW_1, token); 535 536 mTouchHelper.tapOnViewCenter(mContainer); 537 538 assertTouchReceived(); 539 } 540 541 @Test testWhenActivityChildWindowWithDifferentTokenFromDifferentApp_blocksTouch()542 public void testWhenActivityChildWindowWithDifferentTokenFromDifferentApp_blocksTouch() 543 throws Exception { 544 // Creates a new activity with 0 opacity 545 BlockingResultReceiver receiver = new BlockingResultReceiver(); 546 addActivityOverlay(APP_A, /* opacity */ 0f, receiver); 547 // Verify it allows touches 548 mTouchHelper.tapOnViewCenter(mContainer); 549 assertTouchReceived(); 550 // Now get its token and put a child window from another app with it 551 IBinder token = receiver.getData(TIMEOUT_MS).getBinder(EXTRA_TOKEN); 552 addActivityChildWindow(APP_B, WINDOW_1, token); 553 554 mTouchHelper.tapOnViewCenter(mContainer); 555 556 assertTouchNotReceived(); 557 } 558 559 @Test testWhenActivityChildWindowWithDifferentTokenFromSameApp_allowsTouch()560 public void testWhenActivityChildWindowWithDifferentTokenFromSameApp_allowsTouch() 561 throws Exception { 562 // Creates a new activity with 0 opacity 563 BlockingResultReceiver receiver = new BlockingResultReceiver(); 564 addActivityOverlay(APP_A, /* opacity */ 0f, receiver); 565 // Now get its token and put a child window owned by us 566 IBinder token = receiver.getData(TIMEOUT_MS).getBinder(EXTRA_TOKEN); 567 addActivityChildWindow(APP_SELF, WINDOW_1, token); 568 569 mTouchHelper.tapOnViewCenter(mContainer); 570 571 assertTouchReceived(); 572 } 573 574 /** Activity transitions */ 575 576 @Test testLongEnterAnimations_areLimited()577 public void testLongEnterAnimations_areLimited() { 578 long durationSet = mResources.getInteger(R.integer.long_animation_duration); 579 assertThat(durationSet).isGreaterThan( 580 MAX_ANIMATION_DURATION_MS + ANIMATION_DURATION_TOLERANCE_MS); 581 addAnimatedActivityOverlay(APP_A, /* touchable */ false, R.anim.long_alpha_0_7, 582 R.anim.long_alpha_1); 583 assertTrue(mWmState.waitForAppTransitionRunningOnDisplay(Display.DEFAULT_DISPLAY)); 584 long start = SystemClock.elapsedRealtime(); 585 586 assertTrue(mWmState.waitForAppTransitionIdleOnDisplay(Display.DEFAULT_DISPLAY)); 587 long duration = SystemClock.elapsedRealtime() - start; 588 assertThat(duration).isAtMost(MAX_ANIMATION_DURATION_MS + ANIMATION_DURATION_TOLERANCE_MS); 589 } 590 591 @Test testLongExitAnimations_areLimited()592 public void testLongExitAnimations_areLimited() { 593 long durationSet = mResources.getInteger(R.integer.long_animation_duration); 594 assertThat(durationSet).isGreaterThan( 595 MAX_ANIMATION_DURATION_MS + ANIMATION_DURATION_TOLERANCE_MS); 596 addExitAnimationActivity(APP_A); 597 598 // Wait for ExitAnimationActivity open transition to complete to avoid counting this 599 // transition in the duration of the exit animation below. Otherwise 600 // waitForAppTransitionRunningOnDisplay might return immediately if this transition is not 601 // done by then instead of waiting for the exit animation to start running. 602 assertTrue(mWmState.waitForAppTransitionIdleOnDisplay(Display.DEFAULT_DISPLAY)); 603 604 sendFinishToExitAnimationActivity(APP_A, 605 Components.ExitAnimationActivityReceiver.EXTRA_VALUE_LONG_ANIMATION_0_7); 606 assertTrue(mWmState.waitForAppTransitionRunningOnDisplay(Display.DEFAULT_DISPLAY)); 607 long start = SystemClock.elapsedRealtime(); 608 609 assertTrue(mWmState.waitForAppTransitionIdleOnDisplay(Display.DEFAULT_DISPLAY)); 610 long duration = SystemClock.elapsedRealtime() - start; 611 assertThat(duration).isAtMost(MAX_ANIMATION_DURATION_MS + ANIMATION_DURATION_TOLERANCE_MS); 612 } 613 614 @Test testWhenEnterAnimationAboveThresholdAndNewActivityNotTouchable_blocksTouch()615 public void testWhenEnterAnimationAboveThresholdAndNewActivityNotTouchable_blocksTouch() { 616 addAnimatedActivityOverlay(APP_A, /* touchable */ false, R.anim.alpha_0_9, R.anim.alpha_1); 617 assertTrue(mWmState.waitForAppTransitionRunningOnDisplay(Display.DEFAULT_DISPLAY)); 618 619 mTouchHelper.tapOnViewCenter(mContainer, /* waitAnimations*/ false); 620 621 assertAnimationRunning(); 622 assertTouchNotReceived(); 623 } 624 625 @Test testWhenEnterAnimationBelowThresholdAndNewActivityNotTouchable_allowsTouch()626 public void testWhenEnterAnimationBelowThresholdAndNewActivityNotTouchable_allowsTouch() { 627 addAnimatedActivityOverlay(APP_A, /* touchable */ false, R.anim.alpha_0_7, R.anim.alpha_1); 628 assertTrue(mWmState.waitForAppTransitionRunningOnDisplay(Display.DEFAULT_DISPLAY)); 629 630 mTouchHelper.tapOnViewCenter(mContainer, /* waitAnimations*/ false); 631 632 assertAnimationRunning(); 633 assertTouchReceived(); 634 } 635 636 @Test testWhenEnterAnimationBelowThresholdAndNewActivityTouchable_blocksTouch()637 public void testWhenEnterAnimationBelowThresholdAndNewActivityTouchable_blocksTouch() { 638 addAnimatedActivityOverlay(APP_A, /* touchable */ true, R.anim.alpha_0_7, R.anim.alpha_1); 639 assertTrue(mWmState.waitForAppTransitionRunningOnDisplay(Display.DEFAULT_DISPLAY)); 640 641 mTouchHelper.tapOnViewCenter(mContainer, /* waitAnimations*/ false); 642 643 assertAnimationRunning(); 644 assertTouchNotReceived(); 645 } 646 647 @Test testWhenExitAnimationBelowThreshold_allowsTouch()648 public void testWhenExitAnimationBelowThreshold_allowsTouch() { 649 addExitAnimationActivity(APP_A); 650 651 // Wait for ExitAnimationActivity open transition to complete to avoid 652 // waitForAppTransitionRunningOnDisplay returning immediately if this transition is not 653 // done by then instead of waiting for the exit animation to start running. 654 assertTrue(mWmState.waitForAppTransitionIdleOnDisplay(Display.DEFAULT_DISPLAY)); 655 656 sendFinishToExitAnimationActivity(APP_A, 657 Components.ExitAnimationActivityReceiver.EXTRA_VALUE_ANIMATION_0_7); 658 assertTrue(mWmState.waitForAppTransitionRunningOnDisplay(Display.DEFAULT_DISPLAY)); 659 660 mTouchHelper.tapOnViewCenter(mContainer, /* waitAnimations*/ false); 661 662 assertAnimationRunning(); 663 assertTouchReceived(); 664 } 665 666 @Test testWhenExitAnimationAboveThreshold_blocksTouch()667 public void testWhenExitAnimationAboveThreshold_blocksTouch() { 668 addExitAnimationActivity(APP_A); 669 sendFinishToExitAnimationActivity(APP_A, 670 Components.ExitAnimationActivityReceiver.EXTRA_VALUE_ANIMATION_0_9); 671 assertTrue(mWmState.waitForAppTransitionRunningOnDisplay(Display.DEFAULT_DISPLAY)); 672 673 mTouchHelper.tapOnViewCenter(mContainer, /* waitAnimations*/ false); 674 675 assertAnimationRunning(); 676 assertTouchNotReceived(); 677 } 678 679 @Test testWhenExitAnimationAboveThresholdFromSameUid_allowsTouch()680 public void testWhenExitAnimationAboveThresholdFromSameUid_allowsTouch() { 681 addExitAnimationActivity(APP_SELF); 682 sendFinishToExitAnimationActivity(APP_SELF, 683 Components.ExitAnimationActivityReceiver.EXTRA_VALUE_ANIMATION_0_9); 684 assertTrue(mWmState.waitForAppTransitionRunningOnDisplay(Display.DEFAULT_DISPLAY)); 685 686 mTouchHelper.tapOnViewCenter(mContainer, /* waitAnimations*/ false); 687 688 assertAnimationRunning(); 689 assertTouchReceived(); 690 } 691 692 /** Toast windows */ 693 694 @Test testWhenSelfTextToastWindow_allowsTouch()695 public void testWhenSelfTextToastWindow_allowsTouch() throws Throwable { 696 addToastOverlay(APP_SELF, /* custom */ false); 697 Rect toast = mWmState.waitForResult("toast bounds", 698 state -> state.findFirstWindowWithType(LayoutParams.TYPE_TOAST).getFrame()); 699 int[] viewXY = new int[2]; 700 mContainer.getLocationOnScreen(viewXY); 701 Rect containerRect = new Rect(viewXY[0], viewXY[1], viewXY[0] + mContainer.getWidth(), 702 viewXY[1] + mContainer.getHeight()); 703 assumeTrue("Toast displayed outside of activity bounds.", 704 containerRect.contains(toast.centerX(), toast.centerY())); 705 706 mTouchHelper.tapOnCenter(toast, mActivity.getDisplayId()); 707 708 assertTouchReceived(); 709 } 710 711 @Test testWhenTextToastWindow_allowsTouch()712 public void testWhenTextToastWindow_allowsTouch() throws Throwable { 713 assumeFalse("Watch does not support new Toast behavior yet.", FeatureUtil.isWatch()); 714 addToastOverlay(APP_A, /* custom */ false); 715 Rect toast = mWmState.waitForResult("toast bounds", 716 state -> state.findFirstWindowWithType(LayoutParams.TYPE_TOAST).getFrame()); 717 718 mTouchHelper.tapOnCenter(toast, mActivity.getDisplayId()); 719 720 assertTouchReceived(); 721 } 722 723 @Test testWhenOneCustomToastWindow_blocksTouch()724 public void testWhenOneCustomToastWindow_blocksTouch() throws Throwable { 725 addToastOverlay(APP_A, /* custom */ true); 726 727 mTouchHelper.tapOnViewCenter(mContainer); 728 729 assertTouchNotReceived(); 730 } 731 732 @Test testWhenOneSelfCustomToastWindow_allowsTouch()733 public void testWhenOneSelfCustomToastWindow_allowsTouch() throws Throwable { 734 addToastOverlay(APP_SELF, /* custom */ true); 735 736 mTouchHelper.tapOnViewCenter(mContainer); 737 738 assertTouchReceived(); 739 } 740 741 @Test testWhenOneCustomToastWindowAndOneSelfSawWindow_blocksTouch()742 public void testWhenOneCustomToastWindowAndOneSelfSawWindow_blocksTouch() 743 throws Throwable { 744 addSawOverlay(APP_SELF, WINDOW_1, .9f); 745 addToastOverlay(APP_A, /* custom */ true); 746 747 mTouchHelper.tapOnViewCenter(mContainer); 748 749 assertTouchNotReceived(); 750 } 751 752 @Test testWhenOneCustomToastWindowAndOneSawWindowBelowThreshold_blocksTouch()753 public void testWhenOneCustomToastWindowAndOneSawWindowBelowThreshold_blocksTouch() 754 throws Throwable { 755 addSawOverlay(APP_A, WINDOW_1, .5f); 756 addToastOverlay(APP_A, /* custom */ true); 757 758 mTouchHelper.tapOnViewCenter(mContainer); 759 760 assertTouchNotReceived(); 761 } 762 763 @Test testWhenOneCustomToastWindowAndOneSawWindowBelowThresholdFromDifferentApp_blocksTouch()764 public void testWhenOneCustomToastWindowAndOneSawWindowBelowThresholdFromDifferentApp_blocksTouch() 765 throws Throwable { 766 addSawOverlay(APP_A, WINDOW_1, .5f); 767 addToastOverlay(APP_B, /* custom */ true); 768 769 mTouchHelper.tapOnViewCenter(mContainer); 770 771 assertTouchNotReceived(); 772 } 773 774 @Test testWhenOneSelfCustomToastWindowOneSelfActivityWindowAndOneSawBelowThreshold_allowsTouch()775 public void testWhenOneSelfCustomToastWindowOneSelfActivityWindowAndOneSawBelowThreshold_allowsTouch() 776 throws Throwable { 777 addActivityOverlay(APP_SELF, /* opacity */ .9f); 778 addSawOverlay(APP_A, WINDOW_1, .5f); 779 addToastOverlay(APP_SELF, /* custom */ true); 780 781 mTouchHelper.tapOnViewCenter(mContainer); 782 783 assertTouchReceived(); 784 } 785 onTouchEvent(View view, MotionEvent event)786 private boolean onTouchEvent(View view, MotionEvent event) { 787 if (event.getAction() == MotionEvent.ACTION_DOWN) { 788 mTouchesReceived.incrementAndGet(); 789 } 790 return true; 791 } 792 assertTouchReceived()793 private void assertTouchReceived() { 794 mInstrumentation.waitForIdleSync(); 795 assertThat(mTouchesReceived.get()).isEqualTo(1); 796 mTouchesReceived.set(0); 797 } 798 assertTouchNotReceived()799 private void assertTouchNotReceived() { 800 mInstrumentation.waitForIdleSync(); 801 assertThat(mTouchesReceived.get()).isEqualTo(0); 802 mTouchesReceived.set(0); 803 } 804 assertAnimationRunning()805 private void assertAnimationRunning() { 806 assertThat(mWmState.getDisplay(Display.DEFAULT_DISPLAY).getAppTransitionState()).isEqualTo( 807 WindowManagerStateHelper.APP_STATE_RUNNING); 808 } 809 addToastOverlay(String packageName, boolean custom)810 private void addToastOverlay(String packageName, boolean custom) throws Exception { 811 // Making sure there are no toasts currently since we can only check for the presence of 812 // *any* toast afterwards and we don't want to be in a situation where this method returned 813 // because another toast was being displayed. 814 waitForNoToastOverlays(); 815 if (custom) { 816 if (packageName.equals(APP_SELF)) { 817 // We add the custom toast here because we already have foreground status due to 818 // the activity rule, so no need to start another activity. 819 addMyCustomToastOverlay(); 820 } else { 821 // We have to use an activity that will display the toast then finish itself because 822 // custom toasts cannot be posted from the background. 823 Intent intent = new Intent(); 824 intent.setComponent(repackage(packageName, Components.ToastActivity.COMPONENT)); 825 mActivity.startActivity(intent); 826 } 827 } else { 828 getService(packageName).showToast(); 829 } 830 String message = "Toast from app " + packageName + " did not appear on time"; 831 // TODO: WindowStateProto does not have package/UID information from the window, the current 832 // package test relies on the window name, which is not how toast windows are named. We 833 // should ideally incorporate that information in WindowStateProto and use here. 834 if (!mWmState.waitFor("toast window", this::hasVisibleToast)) { 835 fail(message); 836 } 837 } 838 hasVisibleToast(WindowManagerState state)839 private boolean hasVisibleToast(WindowManagerState state) { 840 return !state.getMatchingWindowType(LayoutParams.TYPE_TOAST).isEmpty() 841 && state.findFirstWindowWithType(LayoutParams.TYPE_TOAST).isSurfaceShown(); 842 } 843 addMyCustomToastOverlay()844 private void addMyCustomToastOverlay() { 845 mActivity.runOnUiThread(() -> { 846 mToast = new Toast(mContext); 847 View view = new View(mContext); 848 view.setBackgroundColor(OVERLAY_COLOR); 849 mToast.setView(view); 850 mToast.setGravity(Gravity.FILL, 0, 0); 851 mToast.setDuration(Toast.LENGTH_LONG); 852 mToast.show(); 853 }); 854 mInstrumentation.waitForIdleSync(); 855 } 856 removeMyCustomToastOverlay()857 private void removeMyCustomToastOverlay() { 858 mActivity.runOnUiThread(() -> { 859 if (mToast != null) { 860 mToast.cancel(); 861 mToast = null; 862 } 863 }); 864 mInstrumentation.waitForIdleSync(); 865 } 866 waitForNoToastOverlays()867 private void waitForNoToastOverlays() { 868 waitForNoToastOverlays("Toast windows did not hide on time"); 869 } 870 waitForNoToastOverlays(String message)871 private void waitForNoToastOverlays(String message) { 872 if (!mWmState.waitFor("no toast windows", 873 state -> state.getMatchingWindowType(LayoutParams.TYPE_TOAST).isEmpty())) { 874 fail(message); 875 } 876 } 877 addExitAnimationActivity(String packageName)878 private void addExitAnimationActivity(String packageName) { 879 // This activity responds to broadcasts to exit with animations and it's opaque (translucent 880 // activities don't honor custom exit animations). 881 addActivity(repackage(packageName, Components.ExitAnimationActivity.COMPONENT), 882 /* extras */ null, /* options */ null); 883 } 884 sendFinishToExitAnimationActivity(String packageName, int exitAnimation)885 private void sendFinishToExitAnimationActivity(String packageName, int exitAnimation) { 886 Intent intent = new Intent(Components.ExitAnimationActivityReceiver.ACTION_FINISH); 887 intent.setPackage(packageName); 888 intent.putExtra(Components.ExitAnimationActivityReceiver.EXTRA_ANIMATION, exitAnimation); 889 mContext.sendBroadcast(intent); 890 } 891 addAnimatedActivityOverlay(String packageName, boolean touchable, @AnimRes int enterAnim, @AnimRes int exitAnim)892 private void addAnimatedActivityOverlay(String packageName, boolean touchable, 893 @AnimRes int enterAnim, @AnimRes int exitAnim) { 894 ConditionVariable animationsStarted = new ConditionVariable(false); 895 ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext, enterAnim, exitAnim, 896 0, mMainHandler, (t) -> animationsStarted.open(), /* finishedListener */ null); 897 // We're testing the opacity coming from the animation here, not the one declared in the 898 // activity, so we set its opacity to 1 899 addActivityOverlay(packageName, /* opacity */ 1, touchable, options.toBundle()); 900 animationsStarted.block(); 901 } 902 addActivityChildWindow(String packageName, String windowSuffix, IBinder token)903 private void addActivityChildWindow(String packageName, String windowSuffix, IBinder token) 904 throws Exception { 905 String name = getWindowName(packageName, windowSuffix); 906 getService(packageName).showActivityChildWindow(name, token); 907 if (!mWmState.waitFor("activity child window " + name, 908 state -> state.isWindowVisible(name) && state.isWindowSurfaceShown(name))) { 909 fail("Activity child window " + name + " did not appear on time"); 910 } 911 } 912 addActivityOverlay(String packageName, float opacity)913 private void addActivityOverlay(String packageName, float opacity) { 914 addActivityOverlay(packageName, opacity, /* touchable */ false, /* options */ null); 915 } 916 addActivityOverlay(String packageName, float opacity, boolean touchable, @Nullable Bundle options)917 private void addActivityOverlay(String packageName, float opacity, boolean touchable, 918 @Nullable Bundle options) { 919 Bundle extras = new Bundle(); 920 extras.putFloat(Components.OverlayActivity.EXTRA_OPACITY, opacity); 921 extras.putBoolean(Components.OverlayActivity.EXTRA_TOUCHABLE, touchable); 922 addActivityOverlay(packageName, extras, options); 923 } 924 addActivityOverlay(String packageName, float opacity, BlockingResultReceiver tokenReceiver)925 private void addActivityOverlay(String packageName, float opacity, 926 BlockingResultReceiver tokenReceiver) { 927 Bundle extras = new Bundle(); 928 extras.putFloat(Components.OverlayActivity.EXTRA_OPACITY, opacity); 929 extras.putParcelable(Components.OverlayActivity.EXTRA_TOKEN_RECEIVER, tokenReceiver); 930 addActivityOverlay(packageName, extras, /* options */ null); 931 } 932 addActivityOverlay(String packageName, @Nullable Bundle extras, @Nullable Bundle options)933 private void addActivityOverlay(String packageName, @Nullable Bundle extras, 934 @Nullable Bundle options) { 935 addActivity(repackage(packageName, Components.OverlayActivity.COMPONENT), extras, options); 936 } 937 addActivity(ComponentName component, @Nullable Bundle extras, @Nullable Bundle options)938 private void addActivity(ComponentName component, @Nullable Bundle extras, 939 @Nullable Bundle options) { 940 Intent intent = new Intent(); 941 intent.setComponent(component); 942 if (extras != null) { 943 intent.putExtras(extras); 944 } 945 mActivity.startActivity(intent, options); 946 String packageName = component.getPackageName(); 947 String activity = ComponentNameUtils.getActivityName(component); 948 if (!mWmState.waitFor("activity window " + activity, 949 state -> activity.equals(state.getFocusedActivity()) 950 && state.hasActivityState(component, STATE_RESUMED) 951 && state.isWindowSurfaceShown(activity))) { 952 fail("Activity from app " + packageName + " did not appear on time"); 953 } 954 955 // We need to make sure that InputFlinger has populated window info with correct bounds 956 // before proceeding. 957 // Note that com.android.server.wm.WindowState computes InputWindowHandle's name by 958 // concatenating its hash and title. 959 WindowManagerState.WindowState focusedWindowState = mWmState.getWindowState(component); 960 Rect expectedBounds = mWmState.getActivity(component).getBounds(); 961 SystemUtil.runWithShellPermissionIdentity(() -> { 962 if (!CtsWindowInfoUtils.waitForWindowOnTop(5 * HW_TIMEOUT_MULTIPLIER, TimeUnit.SECONDS, 963 window -> window.name.contains(focusedWindowState.getToken()) 964 && window.name.contains(focusedWindowState.getName()))) { 965 fail("Window " + focusedWindowState.getName() + " did not appear in InputFlinger " 966 + "with an expected bounds " + expectedBounds); 967 } 968 }, ACCESS_SURFACE_FLINGER); 969 } 970 removeActivityOverlays()971 private void removeActivityOverlays() { 972 Intent intent = new Intent(mContext, mActivity.getClass()); 973 // Will clear any activity on top of it and it will become the new top 974 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 975 intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); 976 mActivity.startActivity(intent); 977 } 978 waitForNoActivityOverlays(String message)979 private void waitForNoActivityOverlays(String message) { 980 // Base activity focused means no activities on top 981 ComponentName component = mActivity.getComponentName(); 982 String name = ComponentNameUtils.getActivityName(component); 983 if (!mWmState.waitFor("test rule activity focused", 984 state -> name.equals(state.getFocusedActivity()) 985 && state.hasActivityState(component, STATE_RESUMED))) { 986 fail(message); 987 } 988 } 989 addSawOverlay(String packageName, String windowSuffix, float opacity)990 private void addSawOverlay(String packageName, String windowSuffix, float opacity) 991 throws Throwable { 992 String name = getWindowName(packageName, windowSuffix); 993 int[] viewXY = new int[2]; 994 mContainer.getLocationOnScreen(viewXY); 995 getService(packageName).showSystemAlertWindow(name, opacity, viewXY[0], viewXY[1]); 996 mSawWindowsAdded.add(name); 997 if (!mWmState.waitFor("saw window " + name, 998 state -> state.isWindowVisible(name) && state.isWindowSurfaceShown(name))) { 999 fail("Saw window " + name + " did not appear on time"); 1000 } 1001 } 1002 waitForNoSawOverlays(String message)1003 private void waitForNoSawOverlays(String message) { 1004 if (!mWmState.waitFor("no SAW windows", 1005 state -> mSawWindowsAdded.stream().allMatch(w -> !state.isWindowVisible(w)))) { 1006 fail(message); 1007 } 1008 mSawWindowsAdded.clear(); 1009 } 1010 removeOverlays()1011 private void removeOverlays() throws Throwable { 1012 for (FutureConnection<IUntrustedTouchTestService> connection : mConnections.values()) { 1013 connection.getCurrent().removeOverlays(); 1014 } 1015 // We need to stop the app because not every overlay is created via the service (eg. 1016 // activity overlays and custom toasts) 1017 for (String app : APPS) { 1018 stopPackage(app); 1019 } 1020 waitForNoSawOverlays("SAWs not removed on time"); 1021 removeActivityOverlays(); 1022 waitForNoActivityOverlays("Activities not removed on time"); 1023 removeMyCustomToastOverlay(); 1024 waitForNoToastOverlays("Toasts not removed on time"); 1025 } 1026 stopPackage(String packageName)1027 private void stopPackage(String packageName) { 1028 SystemUtil.runWithShellPermissionIdentity( 1029 () -> mActivityManager.forceStopPackage(packageName)); 1030 } 1031 setMaximumObscuringOpacityForTouch(float opacity)1032 private float setMaximumObscuringOpacityForTouch(float opacity) throws Exception { 1033 return SystemUtil.callWithShellPermissionIdentity(() -> { 1034 float previous = mInputManager.getMaximumObscuringOpacityForTouch(); 1035 InputSettings.setMaximumObscuringOpacityForTouch(mContext, opacity); 1036 return previous; 1037 }); 1038 } 1039 getService(String packageName)1040 private IUntrustedTouchTestService getService(String packageName) throws Exception { 1041 return mConnections.computeIfAbsent(packageName, this::connect).get(TIMEOUT_MS); 1042 } 1043 connect(String packageName)1044 private FutureConnection<IUntrustedTouchTestService> connect(String packageName) { 1045 FutureConnection<IUntrustedTouchTestService> connection = 1046 new FutureConnection<>(IUntrustedTouchTestService.Stub::asInterface); 1047 Intent intent = new Intent(); 1048 intent.setComponent(repackage(packageName, Components.UntrustedTouchTestService.COMPONENT)); 1049 assertTrue(mContext.bindService(intent, connection, Context.BIND_AUTO_CREATE)); 1050 return connection; 1051 } 1052 getWindowName(String packageName, String windowSuffix)1053 private static String getWindowName(String packageName, String windowSuffix) { 1054 return packageName + "." + windowSuffix; 1055 } 1056 repackage(String packageName, ComponentName baseComponent)1057 private static ComponentName repackage(String packageName, ComponentName baseComponent) { 1058 return new ComponentName(packageName, baseComponent.getClassName()); 1059 } 1060 createLaunchActivityOptionsBundle()1061 private static Bundle createLaunchActivityOptionsBundle() { 1062 final ActivityOptions options = ActivityOptions.makeBasic(); 1063 // Launch test in the fullscreen mode with navigation bar hidden, 1064 // in order to ensure text toast is tappable and overlays above the test app 1065 // on freeform first devices. b/191075641. 1066 options.setLaunchWindowingMode(WindowConfiguration.WINDOWING_MODE_FULLSCREEN); 1067 return options.toBundle(); 1068 } 1069 1070 public static class TestActivity extends Activity { 1071 public View view; 1072 1073 @Override onCreate(@ullable Bundle savedInstanceState)1074 protected void onCreate(@Nullable Bundle savedInstanceState) { 1075 super.onCreate(savedInstanceState); 1076 view = new View(this); 1077 view.setBackgroundColor(ACTIVITY_COLOR); 1078 setContentView(view); 1079 } 1080 } 1081 } 1082