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