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