1 /* 2 * Copyright (C) 2021 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 com.android.bedstead.nene.permissions; 18 19 import static android.content.pm.PackageManager.PERMISSION_GRANTED; 20 21 import static com.android.bedstead.nene.permissions.CommonPermissions.MANAGE_APP_OPS_MODES; 22 23 import static com.google.common.truth.Truth.assertWithMessage; 24 25 import android.app.AppOpsManager; 26 import android.app.UiAutomation; 27 import android.content.Context; 28 import android.content.pm.PackageManager; 29 import android.content.pm.PermissionInfo; 30 import android.os.Build; 31 import android.util.Log; 32 33 import com.android.bedstead.nene.TestApis; 34 import com.android.bedstead.nene.appops.AppOpsMode; 35 import com.android.bedstead.nene.exceptions.NeneException; 36 import com.android.bedstead.nene.packages.Package; 37 import com.android.bedstead.nene.users.UserReference; 38 import com.android.bedstead.nene.utils.ShellCommandUtils; 39 import com.android.bedstead.nene.utils.UndoableContext; 40 import com.android.bedstead.nene.utils.Versions; 41 42 import com.google.common.collect.ImmutableSet; 43 44 import java.util.ArrayList; 45 import java.util.Collections; 46 import java.util.HashSet; 47 import java.util.List; 48 import java.util.Set; 49 import java.util.concurrent.atomic.AtomicBoolean; 50 import java.util.stream.Collectors; 51 52 /** Permission manager for tests. */ 53 public final class Permissions { 54 55 public static final AtomicBoolean sIgnorePermissions = new AtomicBoolean(false); 56 private static final String LOG_TAG = "Permissions"; 57 private static final Context sContext = TestApis.context().instrumentedContext(); 58 private static final PackageManager sPackageManager = sContext.getPackageManager(); 59 private static final AppOpsManager sAppOpsManager = 60 TestApis.context().instrumentedContext().getSystemService(AppOpsManager.class); 61 private static final Package sInstrumentedPackage = 62 TestApis.packages().find(sContext.getPackageName()); 63 private static final UserReference sUser = TestApis.users().instrumented(); 64 private static final Package sShellPackage = 65 TestApis.packages().find("com.android.shell"); 66 private static final Set<String> sCheckedGrantPermissions = new HashSet<>(); 67 private static final Set<String> sCheckedDenyPermissions = new HashSet<>(); 68 private static final boolean SUPPORTS_ADOPT_SHELL_PERMISSIONS = 69 Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q; 70 71 /** 72 * Permissions which cannot be given to shell. 73 * 74 * <p>Each entry must include a comment with the reason it cannot be added. 75 */ 76 private static final ImmutableSet EXEMPT_SHELL_PERMISSIONS = ImmutableSet.of( 77 78 ); 79 80 public static final Permissions sInstance = new Permissions(); 81 82 private final List<PermissionContextImpl> mPermissionContexts = 83 Collections.synchronizedList(new ArrayList<>()); 84 private final Set<String> mShellPermissions; 85 private final Set<String> mInstrumentedRequestedPermissions; 86 private Set<String> mExistingPermissions; 87 ignoringPermissions()88 public static UndoableContext ignoringPermissions() { 89 boolean original = Permissions.sIgnorePermissions.get(); 90 Permissions.sIgnorePermissions.set(true); 91 92 if (SUPPORTS_ADOPT_SHELL_PERMISSIONS) { 93 ShellCommandUtils.uiAutomation() 94 .adoptShellPermissionIdentity(); 95 } 96 97 return new UndoableContext(() -> { 98 if (SUPPORTS_ADOPT_SHELL_PERMISSIONS) { 99 ShellCommandUtils.uiAutomation().dropShellPermissionIdentity(); 100 } 101 Permissions.sIgnorePermissions.set(original); 102 }); 103 } 104 105 private Permissions() { 106 // Packages requires using INTERACT_ACROSS_USERS_FULL but we don't want it to rely on 107 // Permissions or it'll recurse forever - so we disable permission checks and just use 108 // shell permission adoption directly while initialising 109 try (UndoableContext c = ignoringPermissions()) { 110 if (SUPPORTS_ADOPT_SHELL_PERMISSIONS) { 111 mShellPermissions = sShellPackage.requestedPermissions(); 112 } else { 113 mShellPermissions = new HashSet<>(); 114 } 115 mInstrumentedRequestedPermissions = sInstrumentedPackage.requestedPermissions(); 116 } 117 } 118 119 /** 120 * Enter a {@link PermissionContext} where the given permissions are granted. 121 * 122 * <p>If the permissions cannot be granted, and are not already granted, an exception will be 123 * thrown. 124 * 125 * <p>Recommended usage: 126 * {@code 127 * 128 * try (PermissionContext p = mTestApis.permissions().withPermission(PERMISSION1, PERMISSION2) { 129 * // Code which needs the permissions goes here 130 * } 131 * } 132 */ 133 public PermissionContextImpl withPermission(String... permissions) { 134 if (mPermissionContexts.isEmpty()) { 135 recordExistingPermissions(); 136 } 137 138 PermissionContextImpl permissionContext = new PermissionContextImpl(this); 139 mPermissionContexts.add(permissionContext); 140 141 permissionContext.withPermission(permissions); 142 143 return permissionContext; 144 } 145 146 /** 147 * Enter a {@link PermissionContext} where the given permissions are granted only when running 148 * on the given version or above. 149 * 150 * <p>If the permissions cannot be granted, and are not already granted, an exception will be 151 * thrown. 152 * 153 * <p>If the version does not match, the permission context will not change. 154 */ 155 public PermissionContextImpl withPermissionOnVersionAtLeast( 156 int minSdkVersion, String... permissions) { 157 if (mPermissionContexts.isEmpty()) { 158 recordExistingPermissions(); 159 } 160 161 PermissionContextImpl permissionContext = new PermissionContextImpl(this); 162 mPermissionContexts.add(permissionContext); 163 164 permissionContext.withPermissionOnVersionAtLeast(minSdkVersion, permissions); 165 166 return permissionContext; 167 } 168 169 /** 170 * Enter a {@link PermissionContext} where the given permissions are granted only when running 171 * on the given version or below. 172 * 173 * <p>If the permissions cannot be granted, and are not already granted, an exception will be 174 * thrown. 175 * 176 * <p>If the version does not match, the permission context will not change. 177 */ 178 public PermissionContextImpl withPermissionOnVersionAtMost( 179 int maxSdkVersion, String... permissions) { 180 if (mPermissionContexts.isEmpty()) { 181 recordExistingPermissions(); 182 } 183 184 PermissionContextImpl permissionContext = new PermissionContextImpl(this); 185 mPermissionContexts.add(permissionContext); 186 187 permissionContext.withPermissionOnVersionAtMost(maxSdkVersion, permissions); 188 189 return permissionContext; 190 } 191 192 /** 193 * Enter a {@link PermissionContext} where the given permissions are granted only when running 194 * on the range of versions given (inclusive). 195 * 196 * <p>If the permissions cannot be granted, and are not already granted, an exception will be 197 * thrown. 198 * 199 * <p>If the version does not match, the permission context will not change. 200 */ 201 public PermissionContextImpl withPermissionOnVersionBetween( 202 int minSdkVersion, int maxSdkVersion, String... permissions) { 203 if (mPermissionContexts.isEmpty()) { 204 recordExistingPermissions(); 205 } 206 207 PermissionContextImpl permissionContext = new PermissionContextImpl(this); 208 mPermissionContexts.add(permissionContext); 209 210 permissionContext.withPermissionOnVersionBetween(minSdkVersion, maxSdkVersion, permissions); 211 212 return permissionContext; 213 } 214 215 /** 216 * Enter a {@link PermissionContext} where the given permissions are granted only when running 217 * on the given version. 218 * 219 * <p>If the permissions cannot be granted, and are not already granted, an exception will be 220 * thrown. 221 * 222 * <p>If the version does not match, the permission context will not change. 223 */ 224 public PermissionContextImpl withPermissionOnVersion(int sdkVersion, String... permissions) { 225 if (mPermissionContexts.isEmpty()) { 226 recordExistingPermissions(); 227 } 228 229 PermissionContextImpl permissionContext = new PermissionContextImpl(this); 230 mPermissionContexts.add(permissionContext); 231 232 permissionContext.withPermissionOnVersion(sdkVersion, permissions); 233 234 return permissionContext; 235 } 236 237 /** 238 * Enter a {@link PermissionContext} where the given appOps are granted. 239 * 240 * <p>If the appOps cannot be granted, and are not already granted, an exception will be 241 * thrown. 242 * 243 * <p>Recommended usage: 244 * {@code 245 * 246 * try (PermissionContext p = mTestApis.permissions().withAppOps(APP_OP1, APP_OP2) { 247 * // Code which needs the app ops goes here 248 * } 249 * } 250 */ 251 public PermissionContextImpl withAppOp(String... appOps) { 252 if (mPermissionContexts.isEmpty()) { 253 recordExistingPermissions(); 254 } 255 256 PermissionContextImpl permissionContext = new PermissionContextImpl(this); 257 mPermissionContexts.add(permissionContext); 258 259 permissionContext.withAppOp(appOps); 260 261 return permissionContext; 262 } 263 264 /** 265 * Enter a {@link PermissionContext} where the given appOps are granted. 266 * 267 * <p>If the appOps cannot be granted, and are not already granted, an exception will be 268 * thrown. 269 * 270 * <p>Recommended usage: 271 * {@code 272 * 273 * try (PermissionContext p = mTestApis.permissions().withAppOps(APP_OP1, APP_OP2) { 274 * // Code which needs the app ops goes here 275 * } 276 * } 277 * 278 * <p>If the version does not match the appOp will not be granted. 279 */ 280 public PermissionContextImpl withAppOpOnVersion(int sdkVersion, String... appOps) { 281 if (mPermissionContexts.isEmpty()) { 282 recordExistingPermissions(); 283 } 284 285 PermissionContextImpl permissionContext = new PermissionContextImpl(this); 286 mPermissionContexts.add(permissionContext); 287 288 permissionContext.withAppOpOnVersion(sdkVersion, appOps); 289 290 return permissionContext; 291 } 292 293 /** 294 * Enter a {@link PermissionContext} where the given appOps are granted. 295 * 296 * <p>If the appOps cannot be granted, and are not already granted, an exception will be 297 * thrown. 298 * 299 * <p>Recommended usage: 300 * {@code 301 * 302 * try (PermissionContext p = mTestApis.permissions().withAppOps(APP_OP1, APP_OP2) { 303 * // Code which needs the app ops goes here 304 * } 305 * } 306 * 307 * <p>If the version does not match the appOp will not be granted. 308 */ 309 public PermissionContextImpl withAppOpOnVersionAtLeast(int sdkVersion, String... appOps) { 310 if (mPermissionContexts.isEmpty()) { 311 recordExistingPermissions(); 312 } 313 314 PermissionContextImpl permissionContext = new PermissionContextImpl(this); 315 mPermissionContexts.add(permissionContext); 316 317 permissionContext.withAppOpOnVersionAtLeast(sdkVersion, appOps); 318 319 return permissionContext; 320 } 321 322 /** 323 * Enter a {@link PermissionContext} where the given appOps are granted. 324 * 325 * <p>If the appOps cannot be granted, and are not already granted, an exception will be 326 * thrown. 327 * 328 * <p>Recommended usage: 329 * {@code 330 * 331 * try (PermissionContext p = mTestApis.permissions().withAppOps(APP_OP1, APP_OP2) { 332 * // Code which needs the app ops goes here 333 * } 334 * } 335 * 336 * <p>If the version does not match the appOp will not be granted. 337 */ 338 public PermissionContextImpl withAppOpOnVersionAtMost(int sdkVersion, String... appOps) { 339 if (mPermissionContexts.isEmpty()) { 340 recordExistingPermissions(); 341 } 342 343 PermissionContextImpl permissionContext = new PermissionContextImpl(this); 344 mPermissionContexts.add(permissionContext); 345 346 permissionContext.withAppOpOnVersionAtMost(sdkVersion, appOps); 347 348 return permissionContext; 349 } 350 351 /** 352 * Enter a {@link PermissionContext} where the given appOps are granted. 353 * 354 * <p>If the appOps cannot be granted, and are not already granted, an exception will be 355 * thrown. 356 * 357 * <p>Recommended usage: 358 * {@code 359 * 360 * try (PermissionContext p = mTestApis.permissions().withAppOps(APP_OP1, APP_OP2) { 361 * // Code which needs the app ops goes here 362 * } 363 * } 364 * 365 * <p>If the version does not match the appOp will not be granted. 366 */ 367 public PermissionContextImpl withAppOpOnVersionBetween( 368 int minSdkVersion, int maxSdkVersion, String... appOps) { 369 if (mPermissionContexts.isEmpty()) { 370 recordExistingPermissions(); 371 } 372 373 PermissionContextImpl permissionContext = new PermissionContextImpl(this); 374 mPermissionContexts.add(permissionContext); 375 376 permissionContext.withAppOpOnVersionBetween(minSdkVersion, maxSdkVersion, appOps); 377 378 return permissionContext; 379 } 380 381 /** 382 * Enter a {@link PermissionContext} where the given permissions are not granted. 383 * 384 * <p>If the permissions cannot be denied, and are not already denied, an exception will be 385 * thrown. 386 * 387 * <p>Recommended usage: 388 * {@code 389 * 390 * try (PermissionContext p = 391 * mTestApis.permissions().withoutPermission(PERMISSION1, PERMISSION2) { 392 * // Code which needs the permissions to be denied goes here 393 * } 394 */ 395 public PermissionContextImpl withoutPermission(String... permissions) { 396 if (mPermissionContexts.isEmpty()) { 397 recordExistingPermissions(); 398 } 399 400 PermissionContextImpl permissionContext = new PermissionContextImpl(this); 401 mPermissionContexts.add(permissionContext); 402 403 permissionContext.withoutPermission(permissions); 404 405 return permissionContext; 406 } 407 408 /** 409 * Enter a {@link PermissionContext} where the given appOps are not granted. 410 * 411 * <p>If the appOps cannot be denied, and are not already denied, an exception will be 412 * thrown. 413 * 414 * <p>Recommended usage: 415 * {@code 416 * 417 * try (PermissionContext p = 418 * mTestApis.permissions().withoutappOp(APP_OP1, APP_OP2) { 419 * // Code which needs the appOp to be denied goes here 420 * } 421 * } 422 */ 423 public PermissionContextImpl withoutAppOp(String... appOps) { 424 if (mPermissionContexts.isEmpty()) { 425 recordExistingPermissions(); 426 } 427 428 PermissionContextImpl permissionContext = new PermissionContextImpl(this); 429 mPermissionContexts.add(permissionContext); 430 431 permissionContext.withoutAppOp(appOps); 432 433 return permissionContext; 434 } 435 436 void undoPermission(PermissionContext permissionContext) { 437 mPermissionContexts.remove(permissionContext); 438 applyPermissions(); 439 } 440 441 void applyPermissions() { 442 if (sIgnorePermissions.get()) { 443 return; 444 } 445 446 if (mPermissionContexts.isEmpty()) { 447 restoreExistingPermissions(); 448 return; 449 } 450 451 if (TestApis.packages().instrumented().isInstantApp()) { 452 // Instant Apps aren't able to know the permissions of shell so we can't know if we can 453 // adopt it - we'll assume we can adopt and log 454 Log.i(LOG_TAG, 455 "Adopting all shell permissions as can't check shell: " + mPermissionContexts); 456 ShellCommandUtils.uiAutomation().adoptShellPermissionIdentity(); 457 return; 458 } 459 460 if (SUPPORTS_ADOPT_SHELL_PERMISSIONS) { 461 ShellCommandUtils.uiAutomation().dropShellPermissionIdentity(); 462 } 463 Set<String> grantedPermissions = new HashSet<>(); 464 Set<String> deniedPermissions = new HashSet<>(); 465 Set<String> grantedAppOps = new HashSet<>(); 466 Set<String> deniedAppOps = new HashSet<>(); 467 468 synchronized (mPermissionContexts) { 469 for (PermissionContextImpl permissionContext : mPermissionContexts) { 470 for (String permission : permissionContext.grantedPermissions()) { 471 grantedPermissions.add(permission); 472 deniedPermissions.remove(permission); 473 } 474 475 for (String permission : permissionContext.deniedPermissions()) { 476 grantedPermissions.remove(permission); 477 deniedPermissions.add(permission); 478 } 479 480 for (String appOp : permissionContext.grantedAppOps()) { 481 grantedAppOps.add(appOp); 482 deniedAppOps.remove(appOp); 483 } 484 485 for (String appOp : permissionContext.deniedAppOps()) { 486 grantedAppOps.remove(appOp); 487 deniedAppOps.add(appOp); 488 } 489 } 490 } 491 492 Log.d(LOG_TAG, "Applying permissions granting: " 493 + grantedPermissions + " denying: " + deniedPermissions); 494 495 // We first try to use shell permissions, because they can be revoked/etc. much more easily 496 497 Set<String> adoptedShellPermissions = new HashSet<>(); 498 for (String permission : grantedPermissions) { 499 checkCanGrantOnAllSupportedVersions(permission); 500 501 Log.d(LOG_TAG, "Trying to grant " + permission); 502 if (sInstrumentedPackage.hasPermission(sUser, permission)) { 503 // Already granted, can skip 504 Log.d(LOG_TAG, permission + " already granted at runtime"); 505 } else if (mInstrumentedRequestedPermissions.contains(permission) 506 && sContext.checkSelfPermission(permission) == PERMISSION_GRANTED) { 507 // Already granted, can skip 508 Log.d(LOG_TAG, permission + " already granted from manifest"); 509 } else if (SUPPORTS_ADOPT_SHELL_PERMISSIONS 510 && mShellPermissions.contains(permission)) { 511 adoptedShellPermissions.add(permission); 512 } else if (canGrantPermission(permission)) { 513 sInstrumentedPackage.grantPermission(sUser, permission); 514 } else { 515 removePermissionContextsUntilCanApply(); 516 517 throwPermissionException("PermissionContext requires granting " 518 + permission + " but cannot.", permission); 519 } 520 } 521 522 for (String permission : deniedPermissions) { 523 checkCanDenyOnAllSupportedVersions(permission, sUser); 524 525 if (!sInstrumentedPackage.hasPermission(sUser, permission)) { 526 // Already denied, can skip 527 } else if (SUPPORTS_ADOPT_SHELL_PERMISSIONS 528 && !mShellPermissions.contains(permission)) { 529 adoptedShellPermissions.add(permission); 530 } else { // We can't deny a permission to ourselves 531 removePermissionContextsUntilCanApply(); 532 throwPermissionException("PermissionContext requires denying " 533 + permission + " but cannot.", permission); 534 } 535 } 536 537 Package appOpPackage = adoptedShellPermissions.isEmpty() 538 ? sInstrumentedPackage : sShellPackage; 539 540 // Filter so we get just the appOps which require a state that they are not currently in 541 Set<String> filteredGrantedAppOps = grantedAppOps.stream() 542 .filter(o -> appOpPackage.appOps().get(o) != AppOpsMode.ALLOWED) 543 .collect(Collectors.toSet()); 544 Set<String> filteredDeniedAppOps = deniedAppOps.stream() 545 .filter(o -> appOpPackage.appOps().get(o) != AppOpsMode.IGNORED) 546 .collect(Collectors.toSet()); 547 548 if (!filteredGrantedAppOps.isEmpty() || !filteredDeniedAppOps.isEmpty()) { 549 // We need MANAGE_APP_OPS_MODES to change app op permissions - but don't want to 550 // infinite loop so won't use .appOps().set() 551 ShellCommandUtils.uiAutomation().adoptShellPermissionIdentity(MANAGE_APP_OPS_MODES); 552 for (String appOp : filteredGrantedAppOps) { 553 sAppOpsManager.setMode(appOp, appOpPackage.uid(sUser), 554 appOpPackage.packageName(), AppOpsMode.ALLOWED.value()); 555 } 556 for (String appOp : filteredDeniedAppOps) { 557 sAppOpsManager.setMode(appOp, appOpPackage.uid(sUser), 558 appOpPackage.packageName(), AppOpsMode.IGNORED.value()); 559 } 560 ShellCommandUtils.uiAutomation().dropShellPermissionIdentity(); 561 } 562 563 if (!adoptedShellPermissions.isEmpty()) { 564 Log.d(LOG_TAG, "Adopting " + adoptedShellPermissions); 565 ShellCommandUtils.uiAutomation().adoptShellPermissionIdentity( 566 adoptedShellPermissions.toArray(new String[0])); 567 } 568 } 569 570 private void checkCanGrantOnAllSupportedVersions( 571 String permission) { 572 if (sCheckedGrantPermissions.contains(permission)) { 573 return; 574 } 575 576 if (Versions.isDevelopmentVersion() 577 && !mShellPermissions.contains(permission) 578 && !EXEMPT_SHELL_PERMISSIONS.contains(permission)) { 579 throwPermissionException(permission + " is not granted to shell on latest development" 580 + "version. You must add it to the com.android.shell manifest. If " 581 + "that is not" 582 + "possible add it to" 583 + "com.android.bedstead.nene.permissions" 584 + ".Permissions#EXEMPT_SHELL_PERMISSIONS", 585 permission); 586 } 587 588 sCheckedGrantPermissions.add(permission); 589 } 590 591 private void checkCanDenyOnAllSupportedVersions( 592 String permission, UserReference user) { 593 if (sCheckedDenyPermissions.contains(permission)) { 594 return; 595 } 596 597 sCheckedDenyPermissions.add(permission); 598 } 599 600 /** 601 * Throw an exception including permission contextual information. 602 */ 603 public void throwPermissionException( 604 String message, String permission) { 605 String protectionLevel = "Permission not found"; 606 try { 607 protectionLevel = Integer.toString(sPackageManager.getPermissionInfo( 608 permission, /* flags= */ 0).protectionLevel); 609 } catch (PackageManager.NameNotFoundException e) { 610 Log.e(LOG_TAG, "Permission not found", e); 611 } 612 613 614 615 try (UndoableContext c = ignoringPermissions()){ 616 throw new NeneException(message + "\n\nRunning On User: " + sUser 617 + "\nPermission: " + permission 618 + "\nPermission protection level: " + protectionLevel 619 + "\nPermission state: " + sContext.checkSelfPermission(permission) 620 + "\nInstrumented Package: " + sInstrumentedPackage.packageName() 621 + "\n\nRequested Permissions:\n" 622 + sInstrumentedPackage.requestedPermissions() 623 + "\n\nCan adopt shell permissions: " + SUPPORTS_ADOPT_SHELL_PERMISSIONS 624 + "\nShell permissions:" 625 + mShellPermissions 626 + "\nExempt Shell permissions: " + EXEMPT_SHELL_PERMISSIONS); 627 } 628 } 629 630 void clearPermissions() { 631 mPermissionContexts.clear(); 632 applyPermissions(); 633 } 634 635 /** 636 * Returns all of the permissions which can be adopted. 637 */ 638 public Set<String> adoptablePermissions() { 639 return mShellPermissions; 640 } 641 642 /** 643 * Returns all of the permissions which are currently able to be used. 644 */ 645 public Set<String> usablePermissions() { 646 Set<String> usablePermissions = new HashSet<>(); 647 usablePermissions.addAll(mShellPermissions); 648 usablePermissions.addAll(mInstrumentedRequestedPermissions); 649 return usablePermissions; 650 } 651 652 private void removePermissionContextsUntilCanApply() { 653 try { 654 mPermissionContexts.remove(mPermissionContexts.size() - 1); 655 applyPermissions(); 656 } catch (NeneException e) { 657 // Suppress NeneException here as we may get a few as we pop through the stack 658 } 659 } 660 661 private boolean canGrantPermission(String permission) { 662 try { 663 PermissionInfo p = sPackageManager.getPermissionInfo(permission, /* flags= */ 0); 664 if ((p.protectionLevel & PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) > 0) { 665 return true; 666 } 667 return (p.protectionLevel & PermissionInfo.PROTECTION_DANGEROUS) > 0; 668 } catch (PackageManager.NameNotFoundException e) { 669 return false; 670 } 671 } 672 673 private void recordExistingPermissions() { 674 if (!Versions.meetsMinimumSdkVersionRequirement(Build.VERSION_CODES.S)) { 675 return; 676 } 677 678 mExistingPermissions = ShellCommandUtils.uiAutomation().getAdoptedShellPermissions(); 679 } 680 681 @SuppressWarnings("NewApi") 682 private void restoreExistingPermissions() { 683 if (!Versions.meetsMinimumSdkVersionRequirement(Build.VERSION_CODES.S)) { 684 return; 685 } 686 687 if (mExistingPermissions == null) { 688 return; // We haven't recorded previous permissions 689 } else if (mExistingPermissions.isEmpty()) { 690 ShellCommandUtils.uiAutomation().dropShellPermissionIdentity(); 691 } else if (mExistingPermissions == UiAutomation.ALL_PERMISSIONS) { 692 ShellCommandUtils.uiAutomation().adoptShellPermissionIdentity(); 693 } else { 694 ShellCommandUtils.uiAutomation().adoptShellPermissionIdentity( 695 mExistingPermissions.toArray(new String[0])); 696 } 697 698 mExistingPermissions = null; 699 } 700 701 /** True if the current process has the given permission. */ 702 public boolean hasPermission(String permission) { 703 return sContext.checkSelfPermission(permission) == PERMISSION_GRANTED; 704 } 705 706 /** True if the current process has the given appOp set to ALLOWED. */ 707 public boolean hasAppOpAllowed(String appOp) { 708 Package appOpPackage = sInstrumentedPackage; 709 if (!ShellCommandUtils.uiAutomation().getAdoptedShellPermissions().isEmpty()) { 710 // We care about the shell package 711 appOpPackage = sShellPackage; 712 } 713 714 return appOpPackage.appOps().get(appOp) == AppOpsMode.ALLOWED; 715 } 716 } 717