• 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.app.cts;
18 
19 import static android.app.UiAutomation.FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES;
20 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
21 
22 import static com.google.common.truth.Truth.assertThat;
23 
24 import static junit.framework.Assert.assertTrue;
25 
26 import static org.junit.Assume.assumeTrue;
27 import static org.testng.Assert.assertThrows;
28 
29 import android.Manifest;
30 import android.app.ActivityManager;
31 import android.app.Instrumentation;
32 import android.app.UiAutomation;
33 import android.app.stubs.shared.FakeView;
34 import android.app.stubs.shared.FutureServiceConnection;
35 import android.app.stubs.shared.ICloseSystemDialogsTestsService;
36 import android.app.stubs.shared.NotificationHelper;
37 import android.app.stubs.shared.TestNotificationListener;
38 import android.content.BroadcastReceiver;
39 import android.content.ComponentName;
40 import android.content.ContentResolver;
41 import android.content.Context;
42 import android.content.Intent;
43 import android.content.IntentFilter;
44 import android.database.ContentObserver;
45 import android.hardware.display.DisplayManager;
46 import android.os.Bundle;
47 import android.os.ConditionVariable;
48 import android.os.Handler;
49 import android.os.Looper;
50 import android.os.Process;
51 import android.os.ResultReceiver;
52 import android.permission.PermissionManager;
53 import android.permission.cts.PermissionUtils;
54 import android.provider.Settings;
55 import android.server.wm.WindowManagerStateHelper;
56 import android.view.Display;
57 import android.view.WindowManager;
58 import android.view.WindowManager.LayoutParams;
59 
60 import androidx.test.platform.app.InstrumentationRegistry;
61 import androidx.test.runner.AndroidJUnit4;
62 
63 import com.android.bedstead.harrier.DeviceState;
64 import com.android.bedstead.multiuser.annotations.RequireRunNotOnVisibleBackgroundNonProfileUser;
65 import com.android.compatibility.common.util.SystemUtil;
66 import com.android.compatibility.common.util.UserHelper;
67 
68 import org.junit.After;
69 import org.junit.Before;
70 import org.junit.ClassRule;
71 import org.junit.Rule;
72 import org.junit.Test;
73 import org.junit.runner.RunWith;
74 
75 import java.util.concurrent.CompletableFuture;
76 import java.util.concurrent.CountDownLatch;
77 import java.util.concurrent.TimeUnit;
78 
79 @RunWith(AndroidJUnit4.class)
80 public class CloseSystemDialogsTest {
81     @ClassRule @Rule
82     public static final DeviceState sDeviceState = new DeviceState();
83 
84     private static final String TEST_SERVICE =
85             "android.app.stubs.shared.CloseSystemDialogsTestService";
86     private static final String APP_COMPAT_ENABLE = "enable";
87     private static final String APP_COMPAT_DISABLE = "disable";
88     private static final String APP_COMPAT_RESET = "reset";
89     private static final String ACTION_SENTINEL = "sentinel";
90     private static final String REASON = "test";
91     private static final long TIMEOUT_MS = 3000;
92     private static final String ACCESSIBILITY_SERVICE =
93             "android.app.stubs.shared.AppAccessibilityService";
94 
95     /**
96      * This test is not self-instrumenting, so we need to bind to the service in the instrumentation
97      * target package (instead of our package).
98      */
99     private static final String APP_SELF = "android.app.stubs";
100 
101     /**
102      * Use com.android.app1 instead of android.app.stubs because the latter is the target of
103      * instrumentation, hence it also has shell powers for {@link
104      * Intent#ACTION_CLOSE_SYSTEM_DIALOGS} and we don't want those powers under simulation.
105      */
106     private static final String APP_HELPER = "com.android.app4";
107 
108     private static final UserHelper USER_HELPER = new UserHelper();
109 
110     private Instrumentation mInstrumentation;
111     private FutureServiceConnection mConnection;
112     private Context mContext;
113     private ContentResolver mResolver;
114     private ICloseSystemDialogsTestsService mService;
115     private volatile WindowManager mSawWindowManager;
116     private volatile Context mSawContext;
117     private volatile CompletableFuture<Void> mCloseSystemDialogsReceived;
118     private volatile ConditionVariable mSentinelReceived;
119     private volatile FakeView mFakeView;
120     private WindowManagerStateHelper mWindowState;
121     private IntentReceiver mIntentReceiver;
122     private Handler mMainHandler;
123     private TestNotificationListener mNotificationListener;
124     private NotificationHelper mNotificationHelper;
125     private String mPreviousHiddenApiPolicy;
126     private String mPreviousAccessibilityServices;
127     private String mPreviousAccessibilityEnabled;
128     private boolean mResetAccessibility;
129 
130 
131     @Before
setUp()132     public void setUp() throws Exception {
133         mInstrumentation = InstrumentationRegistry.getInstrumentation();
134         mContext = mInstrumentation.getTargetContext();
135         PermissionUtils.grantPermission(APP_SELF, Manifest.permission.POST_NOTIFICATIONS);
136         mResolver = mContext.getContentResolver();
137         mMainHandler = new Handler(Looper.getMainLooper());
138         mNotificationHelper = new NotificationHelper(mContext);
139         mNotificationHelper.enableListener(APP_SELF);
140         mNotificationListener = TestNotificationListener.getInstance();
141         mWindowState = new WindowManagerStateHelper();
142         enableUserFinal();
143 
144         final CountDownLatch latch = new CountDownLatch(1);
145         mContext.getContentResolver().registerContentObserver(
146                 Settings.Global.getUriFor(Settings.Global.HIDDEN_API_POLICY), false,
147                 new ContentObserver(null) {
148                     @Override
149                     public void onChange(boolean selfChange) {
150                         super.onChange(selfChange);
151                         latch.countDown();
152                     }
153                 });
154 
155         // We need to test that a few hidden APIs are properly protected in the helper app. The
156         // helper app we're using doesn't have the checks disabled because it's not the target of
157         // instrumentation, see comment on APP_HELPER for details.
158         mPreviousHiddenApiPolicy = setHiddenApiPolicy("1");
159         latch.await(3, TimeUnit.SECONDS);
160 
161         // Add a receiver that will verify if the intent was sent or not
162         mIntentReceiver = new IntentReceiver();
163         mCloseSystemDialogsReceived = new CompletableFuture<>();
164         IntentFilter filter = new IntentFilter();
165         filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
166         filter.addAction(ACTION_SENTINEL);
167         mContext.registerReceiver(mIntentReceiver, filter, Context.RECEIVER_EXPORTED_UNAUDITED);
168 
169         // Add a view to verify if the view got the callback or not
170         mSawContext = getContextForSaw(mContext);
171         mSawWindowManager = mSawContext.getSystemService(WindowManager.class);
172         mMainHandler.post(() -> {
173             mFakeView = new FakeView(mSawContext);
174             mSawWindowManager.addView(mFakeView, new LayoutParams(TYPE_APPLICATION_OVERLAY));
175         });
176     }
177 
178     @After
tearDown()179     public void tearDown() throws Exception {
180         if (mConnection != null) {
181             mContext.unbindService(mConnection);
182         }
183         if (mResetAccessibility) {
184             setAccessibilityState(mPreviousAccessibilityEnabled, mPreviousAccessibilityServices);
185         }
186         mMainHandler.post(() -> mSawWindowManager.removeViewImmediate(mFakeView));
187         mContext.unregisterReceiver(mIntentReceiver);
188         resetUserFinal();
189         setHiddenApiPolicy(mPreviousHiddenApiPolicy);
190         compat(APP_COMPAT_RESET, ActivityManager.LOCK_DOWN_CLOSE_SYSTEM_DIALOGS, APP_HELPER);
191         compat(APP_COMPAT_RESET, "NOTIFICATION_TRAMPOLINE_BLOCK", APP_HELPER);
192         mNotificationHelper.disableListener(APP_SELF);
193         if (mNotificationListener != null) {
194             mNotificationListener.resetData();
195         }
196         // Use test API to prevent PermissionManager from killing the test process when revoking
197         // permission.
198         SystemUtil.runWithShellPermissionIdentity(
199                 () -> mContext.getSystemService(PermissionManager.class)
200                         .revokePostNotificationPermissionWithoutKillForTest(
201                                 mContext.getPackageName(),
202                                 Process.myUserHandle().getIdentifier()),
203                 Manifest.permission.REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL,
204                 Manifest.permission.REVOKE_RUNTIME_PERMISSIONS);
205     }
206 
207     /** Intent.ACTION_CLOSE_SYSTEM_DIALOGS */
208 
209     @Test
testCloseSystemDialogs_whenTargetSdkCurrent_isBlockedAndThrows()210     public void testCloseSystemDialogs_whenTargetSdkCurrent_isBlockedAndThrows() throws Exception {
211         setTargetCurrent();
212         mService = getService(APP_HELPER);
213 
214         assertThrows(SecurityException.class, () -> mService.sendCloseSystemDialogsBroadcast());
215 
216         assertCloseSystemDialogsNotReceived();
217     }
218 
219     @Test
testCloseSystemDialogs_whenTargetSdk30_isBlockedButDoesNotThrow()220     public void testCloseSystemDialogs_whenTargetSdk30_isBlockedButDoesNotThrow() throws Exception {
221         mService = getService(APP_HELPER);
222 
223         mService.sendCloseSystemDialogsBroadcast();
224 
225         assertCloseSystemDialogsNotReceived();
226     }
227 
228     @Test
testCloseSystemDialogs_whenTestInstrumentedViaShell_isSent()229     public void testCloseSystemDialogs_whenTestInstrumentedViaShell_isSent() throws Exception {
230         mContext.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
231 
232         assertCloseSystemDialogsReceived();
233     }
234 
235     @Test
testCloseSystemDialogs_whenRunningAsShell_isSent()236     public void testCloseSystemDialogs_whenRunningAsShell_isSent() throws Exception {
237         SystemUtil.runWithShellPermissionIdentity(
238                 () -> mContext.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)));
239 
240         assertCloseSystemDialogsReceived();
241     }
242 
243     /**
244      * TODO(b/355106764, b/340960913): remove the annotation once populating components of
245      * ManagedServices supports visible background users.
246      */
247     @RequireRunNotOnVisibleBackgroundNonProfileUser(reason = "This test relies on populating"
248             + " components of ManagedServices. The populating components of ManagedServices does"
249             + " not support visible background users at the moment, so skipping these tests for"
250             + " secondary_user_on_secondary_display.")
251     @Test
testCloseSystemDialogs_inTrampolineWhenTargetSdkCurrent_isBlockedAndThrows()252     public void testCloseSystemDialogs_inTrampolineWhenTargetSdkCurrent_isBlockedAndThrows()
253             throws Exception {
254         setTargetCurrent();
255         int notificationId = 42;
256         CompletableFuture<Integer> result = new CompletableFuture<>();
257         mService = getService(APP_HELPER);
258 
259         mService.postNotification(notificationId, new FutureReceiver(result),
260                 /* usePendingIntent */ false);
261 
262         mNotificationHelper.clickNotification(notificationId, /* searchAll */ true);
263         assertThat(result.get()).isEqualTo(
264                 ICloseSystemDialogsTestsService.RESULT_SECURITY_EXCEPTION);
265         assertCloseSystemDialogsNotReceived();
266     }
267 
268     /**
269      * TODO(b/355106764, b/340960913): remove the annotation once populating components of
270      * ManagedServices supports visible background users.
271      */
272     @RequireRunNotOnVisibleBackgroundNonProfileUser(reason = "This test relies on populating"
273             + " components of ManagedServices. The populating components of ManagedServices does"
274             + " not support visible background users at the moment, so skipping these tests for"
275             + " secondary_user_on_secondary_display.")
276     @Test
testCloseSystemDialogs_inTrampolineWhenTargetSdk30_isSent()277     public void testCloseSystemDialogs_inTrampolineWhenTargetSdk30_isSent() throws Exception {
278         int notificationId = 43;
279         CompletableFuture<Integer> result = new CompletableFuture<>();
280         mService = getService(APP_HELPER);
281 
282         mService.postNotification(notificationId, new FutureReceiver(result),
283                 /* usePendingIntent */ false);
284 
285         mNotificationHelper.clickNotification(notificationId, /* searchAll */ true);
286         assertThat(result.get()).isEqualTo(ICloseSystemDialogsTestsService.RESULT_OK);
287         assertCloseSystemDialogsReceived();
288     }
289 
290     /** System doesn't throw on the PI's sender call stack. */
291     /**
292      * TODO(b/355106764, b/340960913): remove the annotation once populating components of
293      * ManagedServices supports visible background users.
294      */
295     @RequireRunNotOnVisibleBackgroundNonProfileUser(reason = "This test relies on populating"
296             + " components of ManagedServices. The populating components of ManagedServices does"
297             + " not support visible background users at the moment, so skipping these tests for"
298             + " secondary_user_on_secondary_display.")
299     @Test
testCloseSystemDialogs_inTrampolineViaPendingIntentWhenTargetSdkCurrent_isBlocked()300     public void testCloseSystemDialogs_inTrampolineViaPendingIntentWhenTargetSdkCurrent_isBlocked()
301             throws Exception {
302         setTargetCurrent();
303         int notificationId = 44;
304         CompletableFuture<Integer> result = new CompletableFuture<>();
305         mService = getService(APP_HELPER);
306 
307         mService.postNotification(notificationId, new FutureReceiver(result),
308                 /* usePendingIntent */ true);
309 
310         mNotificationHelper.clickNotification(notificationId, /* searchAll */ true);
311         assertThat(result.get()).isEqualTo(ICloseSystemDialogsTestsService.RESULT_OK);
312         assertCloseSystemDialogsNotReceived();
313     }
314 
315     /**
316      * TODO(b/355106764, b/340960913): remove the annotation once populating components of
317      * ManagedServices supports visible background users.
318      */
319     @RequireRunNotOnVisibleBackgroundNonProfileUser(reason = "This test relies on populating"
320             + " components of ManagedServices. The populating components of ManagedServices does"
321             + " not support visible background users at the moment, so skipping these tests for"
322             + " secondary_user_on_secondary_display.")
323     @Test
testCloseSystemDialogs_inTrampolineViaPendingIntentWhenTargetSdk30_isSent()324     public void testCloseSystemDialogs_inTrampolineViaPendingIntentWhenTargetSdk30_isSent()
325             throws Exception {
326         int notificationId = 45;
327         CompletableFuture<Integer> result = new CompletableFuture<>();
328         mService = getService(APP_HELPER);
329 
330         mService.postNotification(notificationId, new FutureReceiver(result),
331                 /* usePendingIntent */ true);
332 
333         mNotificationHelper.clickNotification(notificationId, /* searchAll */ true);
334         assertThat(result.get()).isEqualTo(ICloseSystemDialogsTestsService.RESULT_OK);
335         assertCloseSystemDialogsReceived();
336     }
337 
338     // TODO(b/340960913): remove the annotation once accessibility supports visible background users
339     @RequireRunNotOnVisibleBackgroundNonProfileUser(reason = "This test relies on accessibility."
340             + " The accessibility does not support visible background users at the moment,"
341             + " so skipping these tests for secondary_user_on_secondary_display.")
342     @Test
testCloseSystemDialogs_withWindowAboveShadeAndTargetSdk30_isSent()343     public void testCloseSystemDialogs_withWindowAboveShadeAndTargetSdk30_isSent()
344             throws Exception {
345         // Test is only applicable to devices that have a notification shade.
346         assumeTrue(mWindowState.hasNotificationShade());
347         mService = getService(APP_HELPER);
348         setAccessibilityService(APP_HELPER, ACCESSIBILITY_SERVICE);
349         assertTrue(mService.waitForAccessibilityServiceWindow(TIMEOUT_MS));
350 
351         mService.sendCloseSystemDialogsBroadcast();
352 
353         assertCloseSystemDialogsReceived();
354     }
355 
356     /** IWindowManager.closeSystemDialogs() */
357 
358     @Test
testCloseSystemDialogsViaWindowManager_whenTestInstrumentedViaShell_isSent()359     public void testCloseSystemDialogsViaWindowManager_whenTestInstrumentedViaShell_isSent()
360             throws Exception {
361         mService = getService(APP_SELF);
362 
363         mService.closeSystemDialogsViaWindowManager(REASON);
364 
365         assertThat(mFakeView.getNextCloseSystemDialogsCallReason(TIMEOUT_MS)).isEqualTo(REASON);
366     }
367 
368     @Test
testCloseSystemDialogsViaWindowManager_whenRunningAsShell_isSent()369     public void testCloseSystemDialogsViaWindowManager_whenRunningAsShell_isSent()
370             throws Exception {
371         mService = getService(APP_SELF);
372 
373         SystemUtil.runWithShellPermissionIdentity(
374                 () -> mService.closeSystemDialogsViaWindowManager(REASON));
375 
376         assertThat(mFakeView.getNextCloseSystemDialogsCallReason(TIMEOUT_MS)).isEqualTo(REASON);
377     }
378 
379     @Test
testCloseSystemDialogsViaWindowManager_whenTargetSdkCurrent_isBlockedAndThrows()380     public void testCloseSystemDialogsViaWindowManager_whenTargetSdkCurrent_isBlockedAndThrows()
381             throws Exception {
382         setTargetCurrent();
383         mService = getService(APP_HELPER);
384 
385         assertThrows(SecurityException.class,
386                 () -> mService.closeSystemDialogsViaWindowManager(REASON));
387 
388         assertThat(mFakeView.getNextCloseSystemDialogsCallReason(TIMEOUT_MS)).isEqualTo(null);
389     }
390 
391 
392     @Test
testCloseSystemDialogsViaWindowManager_whenTargetSdk30_isBlockedButDoesNotThrow()393     public void testCloseSystemDialogsViaWindowManager_whenTargetSdk30_isBlockedButDoesNotThrow()
394             throws Exception {
395         mService = getService(APP_HELPER);
396 
397         mService.closeSystemDialogsViaWindowManager(REASON);
398 
399         assertThat(mFakeView.getNextCloseSystemDialogsCallReason(TIMEOUT_MS)).isEqualTo(null);
400     }
401 
402     /** IActivityManager.closeSystemDialogs() */
403 
404     @Test
testCloseSystemDialogsViaActivityManager_whenTestInstrumentedViaShell_isSent()405     public void testCloseSystemDialogsViaActivityManager_whenTestInstrumentedViaShell_isSent()
406             throws Exception {
407         mService = getService(APP_SELF);
408 
409         mService.closeSystemDialogsViaActivityManager(REASON);
410 
411         assertThat(mFakeView.getNextCloseSystemDialogsCallReason(TIMEOUT_MS)).isEqualTo(REASON);
412         assertCloseSystemDialogsReceived();
413     }
414 
415     @Test
testCloseSystemDialogsViaActivityManager_whenRunningAsShell_isSent()416     public void testCloseSystemDialogsViaActivityManager_whenRunningAsShell_isSent()
417             throws Exception {
418         mService = getService(APP_SELF);
419 
420         SystemUtil.runWithShellPermissionIdentity(
421                 () -> mService.closeSystemDialogsViaActivityManager(REASON));
422 
423         assertThat(mFakeView.getNextCloseSystemDialogsCallReason(TIMEOUT_MS)).isEqualTo(REASON);
424         assertCloseSystemDialogsReceived();
425     }
426 
427     @Test
testCloseSystemDialogsViaActivityManager_whenTargetSdkCurrent_isBlockedAndThrows()428     public void testCloseSystemDialogsViaActivityManager_whenTargetSdkCurrent_isBlockedAndThrows()
429             throws Exception {
430         setTargetCurrent();
431         mService = getService(APP_HELPER);
432 
433         assertThrows(SecurityException.class,
434                 () -> mService.closeSystemDialogsViaActivityManager(REASON));
435 
436         assertThat(mFakeView.getNextCloseSystemDialogsCallReason(TIMEOUT_MS)).isEqualTo(null);
437         assertCloseSystemDialogsNotReceived();
438     }
439 
440     @Test
testCloseSystemDialogsViaActivityManager_whenTargetSdk30_isBlockedButDoesNotThrow()441     public void testCloseSystemDialogsViaActivityManager_whenTargetSdk30_isBlockedButDoesNotThrow()
442             throws Exception {
443         mService = getService(APP_HELPER);
444 
445         mService.closeSystemDialogsViaActivityManager(REASON);
446 
447         assertThat(mFakeView.getNextCloseSystemDialogsCallReason(TIMEOUT_MS)).isEqualTo(null);
448         assertCloseSystemDialogsNotReceived();
449     }
450 
setTargetCurrent()451     private void setTargetCurrent() {
452         // The helper app has targetSdk=30, opting-in to changes emulates targeting latest sdk.
453         compat(APP_COMPAT_ENABLE, ActivityManager.LOCK_DOWN_CLOSE_SYSTEM_DIALOGS, APP_HELPER);
454         compat(APP_COMPAT_ENABLE, "NOTIFICATION_TRAMPOLINE_BLOCK", APP_HELPER);
455     }
456 
assertCloseSystemDialogsNotReceived()457     private void assertCloseSystemDialogsNotReceived() {
458         // If both broadcasts are sent, they will be received in order here since they are both
459         // registered receivers in the "bg" queue in system_server and belong to the same app.
460         // This is guaranteed by a series of handlers that are the same in both cases and due to the
461         // fact that the binder that system_server uses to call into the app is the same (since the
462         // app is the same) and one-way calls on the same binder object are ordered.
463         mSentinelReceived = new ConditionVariable(false);
464         Intent intent = new Intent(ACTION_SENTINEL);
465         intent.setPackage(mContext.getPackageName());
466         mContext.sendBroadcast(intent);
467         mSentinelReceived.block();
468         assertThat(mCloseSystemDialogsReceived.isDone()).isFalse();
469     }
470 
assertCloseSystemDialogsReceived()471     private void assertCloseSystemDialogsReceived() throws Exception {
472         mCloseSystemDialogsReceived.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
473         // No TimeoutException thrown
474     }
475 
getService(String packageName)476     private ICloseSystemDialogsTestsService getService(String packageName) throws Exception {
477         ICloseSystemDialogsTestsService service =
478                 ICloseSystemDialogsTestsService.Stub.asInterface(
479                         connect(packageName).get(TIMEOUT_MS));
480         assertTrue("Can't call @hide methods", service.waitUntilReady(TIMEOUT_MS));
481         return service;
482     }
483 
connect(String packageName)484     private FutureServiceConnection connect(String packageName) {
485         if (mConnection != null) {
486             return mConnection;
487         }
488         mConnection = new FutureServiceConnection();
489         Intent intent = new Intent();
490         intent.setComponent(ComponentName.createRelative(packageName, TEST_SERVICE));
491         assertTrue(mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE));
492         return mConnection;
493     }
494 
setHiddenApiPolicy(String policy)495     private String setHiddenApiPolicy(String policy) throws Exception {
496         return SystemUtil.callWithShellPermissionIdentity(() -> {
497             String previous = Settings.Global.getString(mResolver,
498                     Settings.Global.HIDDEN_API_POLICY);
499             Settings.Global.putString(mResolver, Settings.Global.HIDDEN_API_POLICY, policy);
500             return previous;
501         });
502     }
503 
setAccessibilityService(String packageName, String service)504     private void setAccessibilityService(String packageName, String service) throws Exception {
505         setAccessibilityState("1", packageName + "/" + service);
506     }
507 
setAccessibilityState(String enabled, String services)508     private void setAccessibilityState(String enabled, String services) {
509         mResetAccessibility = true;
510         UiAutomation uiAutomation = mInstrumentation.getUiAutomation(
511                 FLAG_DONT_SUPPRESS_ACCESSIBILITY_SERVICES);
512         SystemUtil.runWithShellPermissionIdentity(uiAutomation, () -> {
513             mPreviousAccessibilityServices = Settings.Secure.getString(mResolver,
514                     Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
515             mPreviousAccessibilityEnabled = Settings.Secure.getString(mResolver,
516                     Settings.Secure.ACCESSIBILITY_ENABLED);
517             Settings.Secure.putString(mResolver, Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
518                     services);
519             Settings.Secure.putString(mResolver, Settings.Secure.ACCESSIBILITY_ENABLED, enabled);
520         });
521     }
522 
enableUserFinal()523     private static void enableUserFinal() {
524         SystemUtil.runShellCommand(
525                 "settings put global force_non_debuggable_final_build_for_compat 1");
526     }
527 
resetUserFinal()528     private static void resetUserFinal() {
529         SystemUtil.runShellCommand(
530                 "settings put global force_non_debuggable_final_build_for_compat 0");
531     }
532 
compat(String command, String changeId, String packageName)533     private static void compat(String command, String changeId, String packageName) {
534         SystemUtil.runShellCommand(
535                 String.format("am compat %s %s %s", command, changeId, packageName));
536     }
537 
compat(String command, long changeId, String packageName)538     private static void compat(String command, long changeId, String packageName) {
539         compat(command, Long.toString(changeId), packageName);
540     }
541 
getContextForSaw(Context context)542     private static Context getContextForSaw(Context context) {
543         DisplayManager displayManager = context.getSystemService(DisplayManager.class);
544         Display display = displayManager.getDisplay(USER_HELPER.getMainDisplayId());
545         Context displayContext = context.createDisplayContext(display);
546         return displayContext.createWindowContext(TYPE_APPLICATION_OVERLAY, null);
547     }
548 
549     private class IntentReceiver extends BroadcastReceiver {
550         @Override
onReceive(Context context, Intent intent)551         public void onReceive(Context context, Intent intent) {
552             switch (intent.getAction()) {
553                 case Intent.ACTION_CLOSE_SYSTEM_DIALOGS:
554                     mCloseSystemDialogsReceived.complete(null);
555                     break;
556                 case ACTION_SENTINEL:
557                     mSentinelReceived.open();
558                     break;
559             }
560         }
561     }
562 
563     private class FutureReceiver extends ResultReceiver {
564         private final CompletableFuture<Integer> mFuture;
565 
FutureReceiver(CompletableFuture<Integer> future)566         FutureReceiver(CompletableFuture<Integer> future) {
567             super(mMainHandler);
568             mFuture = future;
569         }
570 
571         @Override
onReceiveResult(int resultCode, Bundle resultData)572         protected void onReceiveResult(int resultCode, Bundle resultData) {
573             mFuture.complete(resultCode);
574         }
575     }
576 }
577