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