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