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