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.packages; 18 19 import static android.Manifest.permission.FORCE_STOP_PACKAGES; 20 import static android.Manifest.permission.QUERY_ALL_PACKAGES; 21 import static android.content.pm.ApplicationInfo.FLAG_STOPPED; 22 import static android.content.pm.ApplicationInfo.FLAG_SYSTEM; 23 import static android.content.pm.PackageManager.GET_PERMISSIONS; 24 import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES; 25 import static android.content.pm.PackageManager.PERMISSION_GRANTED; 26 import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS; 27 import static android.content.pm.PermissionInfo.PROTECTION_FLAG_DEVELOPMENT; 28 import static android.os.Build.VERSION_CODES.P; 29 import static android.os.Build.VERSION_CODES.S; 30 import static android.os.Process.myUid; 31 32 import static com.android.bedstead.nene.permissions.CommonPermissions.CHANGE_COMPONENT_ENABLED_STATE; 33 import static com.android.bedstead.nene.permissions.CommonPermissions.INTERACT_ACROSS_USERS_FULL; 34 import static com.android.bedstead.nene.permissions.CommonPermissions.MANAGE_ROLE_HOLDERS; 35 36 import static com.google.common.truth.Truth.assertWithMessage; 37 38 import static org.junit.Assert.fail; 39 40 import android.annotation.TargetApi; 41 import android.app.ActivityManager; 42 import android.app.role.RoleManager; 43 import android.content.Intent; 44 import android.content.IntentFilter; 45 import android.content.pm.ApplicationInfo; 46 import android.content.pm.CrossProfileApps; 47 import android.content.pm.PackageInfo; 48 import android.content.pm.PackageManager; 49 import android.content.pm.PermissionInfo; 50 import android.os.Build; 51 import android.os.UserHandle; 52 import android.util.Log; 53 54 import androidx.annotation.Nullable; 55 56 import com.android.bedstead.nene.TestApis; 57 import com.android.bedstead.nene.annotations.Experimental; 58 import com.android.bedstead.nene.appops.AppOps; 59 import com.android.bedstead.nene.devicepolicy.DeviceOwner; 60 import com.android.bedstead.nene.devicepolicy.ProfileOwner; 61 import com.android.bedstead.nene.exceptions.AdbException; 62 import com.android.bedstead.nene.exceptions.AdbParseException; 63 import com.android.bedstead.nene.exceptions.NeneException; 64 import com.android.bedstead.nene.permissions.PermissionContext; 65 import com.android.bedstead.nene.permissions.Permissions; 66 import com.android.bedstead.nene.roles.RoleContext; 67 import com.android.bedstead.nene.users.UserReference; 68 import com.android.bedstead.nene.utils.Poll; 69 import com.android.bedstead.nene.utils.Retry; 70 import com.android.bedstead.nene.utils.ShellCommand; 71 import com.android.bedstead.nene.utils.ShellCommandUtils; 72 import com.android.bedstead.nene.utils.Versions; 73 import com.android.compatibility.common.util.BlockingBroadcastReceiver; 74 import com.android.compatibility.common.util.BlockingCallback.DefaultBlockingCallback; 75 76 import java.io.File; 77 import java.util.Arrays; 78 import java.util.HashSet; 79 import java.util.Objects; 80 import java.util.Set; 81 import java.util.function.Supplier; 82 import java.util.stream.Collectors; 83 84 /** 85 * A representation of a package on device which may or may not exist. 86 */ 87 public final class Package { 88 private static final String LOG_TAG = "PackageReference"; 89 private static final int PIDS_PER_USER_ID = 100000; 90 private static final PackageManager sPackageManager = 91 TestApis.context().instrumentedContext().getPackageManager(); 92 private static final RoleManager sRoleManager = TestApis.context().instrumentedContext() 93 .getSystemService(RoleManager.class); 94 95 private final String mPackageName; 96 97 /** 98 * Constructs a new {@link Package} from the provided {@code packageName}. 99 */ of(String packageName)100 public static Package of(String packageName) { 101 return new Package(packageName); 102 } 103 Package(String packageName)104 Package(String packageName) { 105 mPackageName = packageName; 106 } 107 108 /** Return the package's name. */ packageName()109 public String packageName() { 110 return mPackageName; 111 } 112 113 /** 114 * Install the package on the given user. 115 * 116 * <p>If you wish to install a package which is not already installed on another user, see 117 * {@link Packages#install(UserReference, File)}. 118 */ installExisting(UserReference user)119 public Package installExisting(UserReference user) { 120 if (user == null) { 121 throw new NullPointerException(); 122 } 123 124 try { 125 // Expected output "Package X installed for user: Y" 126 ShellCommand.builderForUser(user, "cmd package install-existing") 127 .addOperand(mPackageName) 128 .validate( 129 (output) -> output.contains("installed for user")) 130 .execute(); 131 132 return this; 133 } catch (AdbException e) { 134 throw new NeneException("Could not install-existing package " + this, e); 135 } 136 } 137 138 /** 139 * Install this package on the given user, using {@link #installExisting(UserReference)} if 140 * possible, otherwise installing fresh. 141 */ install(UserReference user, File apkFile)142 public Package install(UserReference user, File apkFile) { 143 if (exists()) { 144 return installExisting(user); 145 } 146 147 return TestApis.packages().install(user, apkFile); 148 } 149 150 /** 151 * Install this package on the given user, using {@link #installExisting(UserReference)} if 152 * possible, otherwise installing fresh. 153 */ install(UserReference user, Supplier<File> apkFile)154 public Package install(UserReference user, Supplier<File> apkFile) { 155 if (exists()) { 156 return installExisting(user); 157 } 158 159 return TestApis.packages().install(user, apkFile.get()); 160 } 161 162 /** 163 * Install this package on the given user, using {@link #installExisting(UserReference)} if 164 * possible, otherwise installing fresh. 165 */ installBytes(UserReference user, byte[] apkFile)166 public Package installBytes(UserReference user, byte[] apkFile) { 167 if (exists()) { 168 return installExisting(user); 169 } 170 171 return TestApis.packages().install(user, apkFile); 172 } 173 174 /** 175 * Install this package on the given user, using {@link #installExisting(UserReference)} if 176 * possible, otherwise installing fresh. 177 */ installBytes(UserReference user, Supplier<byte[]> apkFile)178 public Package installBytes(UserReference user, Supplier<byte[]> apkFile) { 179 if (exists()) { 180 return installExisting(user); 181 } 182 183 return TestApis.packages().install(user, apkFile.get()); 184 } 185 186 /** 187 * Uninstall the package for all users. 188 */ uninstallFromAllUsers()189 public Package uninstallFromAllUsers() { 190 for (UserReference user : installedOnUsers()) { 191 uninstall(user); 192 } 193 194 return this; 195 } 196 197 /** 198 * Uninstall the package for the given user. 199 * 200 * <p>If the package is not installed for the given user, nothing will happen. 201 */ uninstall(UserReference user)202 public Package uninstall(UserReference user) { 203 if (user == null) { 204 throw new NullPointerException(); 205 } 206 207 IntentFilter packageRemovedIntentFilter = 208 new IntentFilter(Intent.ACTION_PACKAGE_REMOVED); 209 packageRemovedIntentFilter.addDataScheme("package"); 210 211 // This is outside of the try because we don't want to await if the package isn't installed 212 BlockingBroadcastReceiver broadcastReceiver = BlockingBroadcastReceiver.create( 213 TestApis.context().androidContextAsUser(user), 214 packageRemovedIntentFilter); 215 216 try { 217 218 boolean canWaitForBroadcast = false; 219 if (Versions.meetsMinimumSdkVersionRequirement(Build.VERSION_CODES.R)) { 220 try (PermissionContext p = TestApis.permissions().withPermission( 221 INTERACT_ACROSS_USERS_FULL)) { 222 broadcastReceiver.register(); 223 } 224 canWaitForBroadcast = true; 225 } else if (user.equals(TestApis.users().instrumented())) { 226 broadcastReceiver.register(); 227 canWaitForBroadcast = true; 228 } 229 230 String commandOutput = Poll.forValue(() -> { 231 // Expected output "Success" 232 return ShellCommand.builderForUser(user, "pm uninstall") 233 .addOperand(mPackageName) 234 .execute(); 235 }).toMeet(output -> output.toUpperCase().startsWith("SUCCESS") 236 || output.toUpperCase().contains("NOT INSTALLED FOR")) 237 .terminalValue((output) -> { 238 if (output.contains("DELETE_FAILED_DEVICE_POLICY_MANAGER")) { 239 // A recently-removed device policy manager can't be removed - but won't 240 // show as DPC 241 242 DeviceOwner deviceOwner = TestApis.devicePolicy().getDeviceOwner(); 243 if (deviceOwner != null && deviceOwner.pkg().equals(this)) { 244 // Terminal, can't remove actual DO 245 return true; 246 } 247 ProfileOwner profileOwner = 248 TestApis.devicePolicy().getProfileOwner(user); 249 // Terminal, can't remove actual PO 250 return profileOwner != null && profileOwner.pkg().equals(this); 251 252 // Not PO or DO, likely temporary failure 253 } 254 255 return true; 256 }) 257 .errorOnFail() 258 .await(); 259 260 if (commandOutput.toUpperCase().startsWith("SUCCESS")) { 261 if (canWaitForBroadcast) { 262 broadcastReceiver.awaitForBroadcastOrFail(); 263 } else { 264 try { 265 // On versions prior to R - cross user installs can't block for broadcasts 266 // so we have an arbitrary sleep 267 Thread.sleep(10000); 268 } catch (InterruptedException e) { 269 Log.i(LOG_TAG, "Interrupted waiting for package uninstallation", e); 270 } 271 } 272 } 273 return this; 274 } catch (NeneException e) { 275 throw new NeneException("Could not uninstall package " + this, e); 276 } finally { 277 broadcastReceiver.unregisterQuietly(); 278 } 279 } 280 281 /** 282 * Enable this package for the given {@link UserReference}. 283 */ 284 @Experimental enable(UserReference user)285 public Package enable(UserReference user) { 286 try { 287 ShellCommand.builderForUser(user, "pm enable") 288 .addOperand(mPackageName) 289 .validate(o -> o.contains("new state")) 290 .execute(); 291 } catch (AdbException e) { 292 throw new NeneException("Error enabling package " + this + " for user " + user, e); 293 } 294 return this; 295 } 296 297 /** 298 * Enable this package on the instrumented user. 299 */ 300 @Experimental enable()301 public Package enable() { 302 return enable(TestApis.users().instrumented()); 303 } 304 305 /** 306 * Disable this package for the given {@link UserReference}. 307 */ 308 @Experimental disable(UserReference user)309 public Package disable(UserReference user) { 310 try { 311 // TODO(279387509): "pm disable" is currently broken for packages - restore to normal 312 // disable when fixed 313 ShellCommand.builderForUser(user, "pm disable-user") 314 .addOperand(mPackageName) 315 .validate(o -> o.contains("new state")) 316 .execute(); 317 } catch (AdbException e) { 318 throw new NeneException("Error disabling package " + this + " for user " + user, e); 319 } 320 return this; 321 } 322 323 /** 324 * Disable this package on the instrumented user. 325 */ 326 @Experimental disable()327 public Package disable() { 328 return disable(TestApis.users().instrumented()); 329 } 330 331 /** 332 * Get a reference to the given {@code componentName} within this package. 333 * 334 * <p>This does not guarantee that the component exists. 335 */ 336 @Experimental component(String componentName)337 public ComponentReference component(String componentName) { 338 return new ComponentReference(this, componentName); 339 } 340 341 /** 342 * Grant a permission for the package on the given user. 343 * 344 * <p>The package must be installed on the user, must request the given permission, and the 345 * permission must be a runtime permission. 346 */ grantPermission(UserReference user, String permission)347 public Package grantPermission(UserReference user, String permission) { 348 // There is no readable output upon failure so we need to check ourselves 349 checkCanGrantOrRevokePermission(user, permission); 350 351 try { 352 ShellCommand.builderForUser(user, "pm grant") 353 .addOperand(packageName()) 354 .addOperand(permission) 355 .allowEmptyOutput(true) 356 .validate(String::isEmpty) 357 .execute(); 358 359 assertWithMessage("Error granting permission " + permission 360 + " to package " + this + " on user " + user 361 + ". Command appeared successful but not set.") 362 .that(hasPermission(user, permission)).isTrue(); 363 364 return this; 365 } catch (AdbException e) { 366 throw new NeneException("Error granting permission " + permission + " to package " 367 + this + " on user " + user, e); 368 } 369 } 370 371 /** Grant the {@code permission} on the instrumented user. */ grantPermission(String permission)372 public Package grantPermission(String permission) { 373 return grantPermission(TestApis.users().instrumented(), permission); 374 } 375 376 /** Deny the {@code permission} on the instrumented user. */ denyPermission(String permission)377 public Package denyPermission(String permission) { 378 return denyPermission(TestApis.users().instrumented(), permission); 379 } 380 381 /** 382 * Deny a permission for the package on the given user. 383 * 384 * <p>The package must be installed on the user, must request the given permission, and the 385 * permission must be a runtime permission. 386 * 387 * <p>You can not deny permissions for the current package on the current user. 388 */ denyPermission(UserReference user, String permission)389 public Package denyPermission(UserReference user, String permission) { 390 if (!hasPermission(user, permission)) { 391 return this; // Already denied 392 } 393 394 // There is no readable output upon failure so we need to check ourselves 395 checkCanGrantOrRevokePermission(user, permission); 396 397 if (packageName().equals(TestApis.context().instrumentedContext().getPackageName()) 398 && user.equals(TestApis.users().instrumented())) { 399 throw new NeneException("Cannot deny permission from current package"); 400 } 401 402 try { 403 ShellCommand.builderForUser(user, "pm revoke") 404 .addOperand(packageName()) 405 .addOperand(permission) 406 .allowEmptyOutput(true) 407 .validate(String::isEmpty) 408 .execute(); 409 410 assertWithMessage("Error denying permission " + permission 411 + " to package " + this + " on user " + user 412 + ". Command appeared successful but not revoked.") 413 .that(hasPermission(user, permission)).isFalse(); 414 415 return this; 416 } catch (AdbException e) { 417 throw new NeneException("Error denying permission " + permission + " to package " 418 + this + " on user " + user, e); 419 } 420 } 421 checkCanGrantOrRevokePermission(UserReference user, String permission)422 void checkCanGrantOrRevokePermission(UserReference user, String permission) { 423 if (!installedOnUser(user)) { 424 throw new NeneException("Attempting to grant " + permission + " to " + this 425 + " on user " + user + ". But it is not installed"); 426 } 427 428 try { 429 PermissionInfo permissionInfo = 430 sPackageManager.getPermissionInfo(permission, /* flags= */ 0); 431 432 if (!protectionIsDangerous(permissionInfo.protectionLevel) 433 && !protectionIsDevelopment(permissionInfo.protectionLevel)) { 434 throw new NeneException("Cannot grant non-runtime permission " 435 + permission + ", protection level is " + permissionInfo.protectionLevel); 436 } 437 438 if (!requestedPermissions().contains(permission)) { 439 throw new NeneException("Cannot grant permission " 440 + permission + " which was not requested by package " + packageName()); 441 } 442 } catch (PackageManager.NameNotFoundException e) { 443 throw new NeneException("Permission does not exist: " + permission); 444 } 445 } 446 protectionIsDangerous(int protectionLevel)447 private boolean protectionIsDangerous(int protectionLevel) { 448 return (protectionLevel & PROTECTION_DANGEROUS) != 0; 449 } 450 protectionIsDevelopment(int protectionLevel)451 private boolean protectionIsDevelopment(int protectionLevel) { 452 return (protectionLevel & PROTECTION_FLAG_DEVELOPMENT) != 0; 453 } 454 455 /** Get running {@link ProcessReference} for this package on all users. */ 456 @Experimental runningProcesses()457 public Set<ProcessReference> runningProcesses() { 458 // TODO(scottjonathan): See if this can be remade using 459 // ActivityManager#getRunningappProcesses 460 try { 461 return ShellCommand.builder("ps") 462 .addOperand("-A") 463 .addOperand("-n") 464 .executeAndParseOutput(o -> parsePsOutput(o).stream() 465 .filter(p -> p.mPackageName.equals(mPackageName)) 466 .map(p -> new ProcessReference(this, p.mPid, p.mUid, 467 TestApis.users().find(p.mUserId)))) 468 .collect(Collectors.toSet()); 469 } catch (AdbException e) { 470 throw new NeneException("Error getting running processes ", e); 471 } 472 } 473 parsePsOutput(String psOutput)474 private Set<ProcessInfo> parsePsOutput(String psOutput) { 475 return Arrays.stream(psOutput.split("\n")) 476 .skip(1) // Skip the title line 477 .map(s -> s.split("\\s+")) 478 .map(m -> new ProcessInfo( 479 m[8], Integer.parseInt(m[1]), 480 Integer.parseInt(m[0]), 481 Integer.parseInt(m[0]) / PIDS_PER_USER_ID)) 482 .collect(Collectors.toSet()); 483 } 484 485 /** Get the running {@link ProcessReference} for this package on the given user. */ 486 @Experimental 487 @Nullable runningProcess(UserReference user)488 public ProcessReference runningProcess(UserReference user) { 489 ProcessReference p = runningProcesses().stream().filter( 490 i -> i.user().equals(user)) 491 .findAny() 492 .orElse(null); 493 return p; 494 } 495 496 /** Get the running {@link ProcessReference} for this package on the given user. */ 497 @Experimental 498 @Nullable runningProcess(UserHandle user)499 public ProcessReference runningProcess(UserHandle user) { 500 return runningProcess(TestApis.users().find(user)); 501 } 502 503 /** Get the running {@link ProcessReference} for this package on the instrumented user. */ 504 @Experimental 505 @Nullable runningProcess()506 public ProcessReference runningProcess() { 507 return runningProcess(TestApis.users().instrumented()); 508 } 509 510 /** {@code true} if the package is installed on the given user. */ installedOnUser(UserHandle userHandle)511 public boolean installedOnUser(UserHandle userHandle) { 512 return installedOnUser(TestApis.users().find(userHandle)); 513 } 514 515 /** {@code true} if the package is installed on the given user. */ installedOnUser(UserReference user)516 public boolean installedOnUser(UserReference user) { 517 return packageInfoForUser(user, /* flags= */ 0) != null; 518 } 519 520 /** {@code true} if the package is installed on the instrumented user. */ installedOnUser()521 public boolean installedOnUser() { 522 return installedOnUser(TestApis.users().instrumented()); 523 } 524 525 /** {@code true} if the package on the given user has the given permission. */ hasPermission(UserReference user, String permission)526 public boolean hasPermission(UserReference user, String permission) { 527 return TestApis.context().androidContextAsUser(user).getPackageManager() 528 .checkPermission(permission, mPackageName) == PERMISSION_GRANTED; 529 } 530 531 /** {@code true} if the package on the given user has the given permission. */ hasPermission(UserHandle user, String permission)532 public boolean hasPermission(UserHandle user, String permission) { 533 return hasPermission(TestApis.users().find(user), permission); 534 } 535 536 /** {@code true} if the package on the instrumented user has the given permission. */ hasPermission(String permission)537 public boolean hasPermission(String permission) { 538 return hasPermission(TestApis.users().instrumented(), permission); 539 } 540 541 /** Get the permissions requested in the package's manifest. */ requestedPermissions()542 public Set<String> requestedPermissions() { 543 PackageInfo packageInfo = packageInfoFromAnyUser(GET_PERMISSIONS); 544 545 if (packageInfo == null) { 546 if (TestApis.packages().instrumented().isInstantApp()) { 547 Log.i(LOG_TAG, "Tried to get requestedPermissions for " 548 + mPackageName + " but can't on instant apps"); 549 return new HashSet<>(); 550 } 551 throw new NeneException("Error getting requestedPermissions, does not exist"); 552 } 553 554 if (packageInfo.requestedPermissions == null) { 555 return new HashSet<>(); 556 } 557 558 return new HashSet<>(Arrays.asList(packageInfo.requestedPermissions)); 559 } 560 561 @Nullable packageInfoFromAnyUser(int flags)562 private PackageInfo packageInfoFromAnyUser(int flags) { 563 return TestApis.users().all().stream() 564 .map(i -> packageInfoForUser(i, flags)) 565 .filter(Objects::nonNull) 566 .findFirst() 567 .orElse(null); 568 } 569 570 @Nullable packageInfoForUser(UserReference user, int flags)571 private PackageInfo packageInfoForUser(UserReference user, int flags) { 572 if (TestApis.packages().instrumented().isInstantApp() 573 || !Versions.meetsMinimumSdkVersionRequirement(S)) { 574 // Can't call API's directly 575 return packageInfoForUserPreS(user, flags); 576 } 577 578 if (user.equals(TestApis.users().instrumented())) { 579 try { 580 return TestApis.context().instrumentedContext() 581 .getPackageManager() 582 .getPackageInfo(mPackageName, /* flags= */ flags); 583 } catch (PackageManager.NameNotFoundException e) { 584 Log.e(LOG_TAG, "Could not find package " + this + " on user " + user, e); 585 return null; 586 } 587 } 588 589 if (Permissions.sIgnorePermissions.get()) { 590 try { 591 return TestApis.context().androidContextAsUser(user) 592 .getPackageManager() 593 .getPackageInfo(mPackageName, /* flags= */ flags); 594 } catch (PackageManager.NameNotFoundException e) { 595 return null; 596 } 597 } else { 598 try (PermissionContext p = TestApis.permissions().withPermission( 599 INTERACT_ACROSS_USERS_FULL)) { 600 return TestApis.context().androidContextAsUser(user) 601 .getPackageManager() 602 .getPackageInfo(mPackageName, /* flags= */ flags); 603 } catch (PackageManager.NameNotFoundException e) { 604 return null; 605 } 606 } 607 } 608 packageInfoForUserPreS(UserReference user, int flags)609 private PackageInfo packageInfoForUserPreS(UserReference user, int flags) { 610 AdbPackage pkg = Packages.parseDumpsys().mPackages.get(mPackageName); 611 612 if (pkg == null) { 613 return null; 614 } 615 616 if (!pkg.installedOnUsers().contains(user)) { 617 return null; 618 } 619 620 PackageInfo packageInfo = new PackageInfo(); 621 packageInfo.packageName = mPackageName; 622 packageInfo.requestedPermissions = pkg.requestedPermissions().toArray(new String[]{}); 623 624 return packageInfo; 625 } 626 627 @Nullable applicationInfoFromAnyUser(int flags)628 private ApplicationInfo applicationInfoFromAnyUser(int flags) { 629 return TestApis.users().all().stream() 630 .map(i -> applicationInfoForUser(i, flags)) 631 .filter(Objects::nonNull) 632 .findFirst() 633 .orElse(null); 634 } 635 636 @Nullable applicationInfoForUser(UserReference user, int flags)637 private ApplicationInfo applicationInfoForUser(UserReference user, int flags) { 638 if (user.equals(TestApis.users().instrumented())) { 639 try { 640 return TestApis.context().instrumentedContext() 641 .getPackageManager() 642 .getApplicationInfo(mPackageName, /* flags= */ flags); 643 } catch (PackageManager.NameNotFoundException e) { 644 return null; 645 } 646 } 647 648 if (!Versions.meetsMinimumSdkVersionRequirement(Build.VERSION_CODES.Q)) { 649 return applicationInfoForUserPreQ(user, flags); 650 } 651 652 if (Permissions.sIgnorePermissions.get()) { 653 try { 654 return TestApis.context().androidContextAsUser(user) 655 .getPackageManager() 656 .getApplicationInfo(mPackageName, /* flags= */ flags); 657 } catch (PackageManager.NameNotFoundException e) { 658 return null; 659 } 660 } else { 661 try (PermissionContext p = TestApis.permissions().withPermission( 662 INTERACT_ACROSS_USERS_FULL)) { 663 return TestApis.context().androidContextAsUser(user) 664 .getPackageManager() 665 .getApplicationInfo(mPackageName, /* flags= */ flags); 666 } catch (PackageManager.NameNotFoundException e) { 667 return null; 668 } 669 } 670 } 671 applicationInfoForUserPreQ(UserReference user, int flags)672 private ApplicationInfo applicationInfoForUserPreQ(UserReference user, int flags) { 673 try { 674 String dumpsysOutput = ShellCommand.builder("dumpsys package").execute(); 675 676 AdbPackageParser.ParseResult r = Packages.sParser.parse(dumpsysOutput); 677 AdbPackage pkg = r.mPackages.get(mPackageName); 678 679 if (pkg == null) { 680 return null; 681 } 682 683 ApplicationInfo applicationInfo = new ApplicationInfo(); 684 applicationInfo.packageName = mPackageName; 685 applicationInfo.uid = -1; // TODO: Get the actual uid... 686 687 return applicationInfo; 688 } catch (AdbException | AdbParseException e) { 689 throw new NeneException("Error getting package info pre Q", e); 690 } 691 } 692 693 /** 694 * Get all users this package is installed on. 695 * 696 * <p>Note that this is an expensive operation - favor {@link #installedOnUser(UserReference)} 697 * when possible. 698 */ installedOnUsers()699 public Set<UserReference> installedOnUsers() { 700 return TestApis.users().all().stream() 701 .filter(this::installedOnUser) 702 .collect(Collectors.toSet()); 703 } 704 705 /** 706 * Force the running instance of the package to stop on the given user. 707 * 708 * <p>See {@link ActivityManager#forceStopPackage(String)}. 709 */ 710 @Experimental forceStop(UserReference user)711 public void forceStop(UserReference user) { 712 try (PermissionContext p = TestApis.permissions().withPermission(FORCE_STOP_PACKAGES)) { 713 ActivityManager userActivityManager = 714 TestApis.context().androidContextAsUser(user) 715 .getSystemService(ActivityManager.class); 716 717 PackageManager userPackageManager = 718 TestApis.context().androidContextAsUser(user).getPackageManager(); 719 boolean shouldCheckPreviousProcess = runningProcess() != null; 720 // In most cases this should work first time, however if a user restriction has been 721 // recently removed we may need to retry 722 723 int previousPid = shouldCheckPreviousProcess ? runningProcess().pid() : -1; 724 725 Poll.forValue("Application flag", () -> { 726 userActivityManager.forceStopPackage(mPackageName); 727 728 return userPackageManager.getPackageInfo(mPackageName, 729 PackageManager.GET_META_DATA) 730 .applicationInfo.flags; 731 }).toMeet(flag -> !shouldCheckPreviousProcess || (flag & FLAG_STOPPED) == FLAG_STOPPED 732 || previousPid != runningProcess().pid()) 733 .errorOnFail("Expected application flags to contain FLAG_STOPPED (" 734 + FLAG_STOPPED + ")") 735 .await(); 736 } 737 } 738 739 /** 740 * Force the running instance of the package to stop on the instrumented user. 741 * 742 * <p>See {@link ActivityManager#forceStopPackage(String)}. 743 */ 744 @Experimental forceStop()745 public void forceStop() { 746 forceStop(TestApis.users().instrumented()); 747 } 748 749 /** 750 * Interact with AppOps on the instrumented user for the given package. 751 */ 752 @Experimental appOps()753 public AppOps appOps() { 754 return appOps(TestApis.users().instrumented()); 755 } 756 757 /** 758 * Interact with AppOps on the given user for the given package. 759 */ 760 @Experimental appOps(UserReference user)761 public AppOps appOps(UserReference user) { 762 return new AppOps(this, user); 763 } 764 765 /** 766 * Get the UID of the package on the instrumented user. 767 */ 768 @Experimental uid()769 public int uid() { 770 return uid(TestApis.users().instrumented()); 771 } 772 773 /** 774 * Get the UID of the package on the given {@code user}. 775 */ 776 @Experimental uid(UserReference user)777 public int uid(UserReference user) { 778 if (user.equals(TestApis.users().instrumented()) 779 && this.equals(TestApis.packages().instrumented())) { 780 return myUid(); 781 } 782 783 ApplicationInfo applicationInfo = applicationInfoForUser(user, /* flags= */ 0); 784 if (applicationInfo == null) { 785 throw new IllegalStateException( 786 "Trying to get uid for not installed package " + this + " on user " + user); 787 } 788 789 return applicationInfo.uid; 790 } 791 792 @Override hashCode()793 public int hashCode() { 794 return mPackageName.hashCode(); 795 } 796 797 @Override equals(Object obj)798 public boolean equals(Object obj) { 799 if (!(obj instanceof Package)) { 800 return false; 801 } 802 803 Package other = (Package) obj; 804 return other.mPackageName.equals(mPackageName); 805 } 806 807 @Override toString()808 public String toString() { 809 StringBuilder stringBuilder = new StringBuilder("PackageReference{"); 810 stringBuilder.append("packageName=" + mPackageName); 811 stringBuilder.append("}"); 812 return stringBuilder.toString(); 813 } 814 815 /** {@code true} if the package exists on any user on the device as is not uninstalled. */ 816 @TargetApi(S) isInstalled()817 public boolean isInstalled() { 818 Versions.requireMinimumVersion(S); 819 820 try (PermissionContext p = TestApis.permissions().withPermission(QUERY_ALL_PACKAGES)) { 821 return packageInfoFromAnyUser(0) != null; 822 } 823 } 824 825 /** {@code true} if the package exists on the device. */ exists()826 public boolean exists() { 827 if (Versions.meetsMinimumSdkVersionRequirement(S)) { 828 try (PermissionContext p = TestApis.permissions().withPermission(QUERY_ALL_PACKAGES)) { 829 return packageInfoFromAnyUser(MATCH_UNINSTALLED_PACKAGES) != null; 830 } 831 } 832 833 return Packages.parseDumpsys().mPackages.containsKey(mPackageName); 834 } 835 836 /** Get the targetSdkVersion for the package. */ 837 @Experimental targetSdkVersion()838 public int targetSdkVersion() { 839 return applicationInfoFromAnyUserOrError(/* flags= */ 0).targetSdkVersion; 840 } 841 842 /** 843 * {@code true} if the package is installed in the device's system image. 844 */ 845 @Experimental hasSystemFlag()846 public boolean hasSystemFlag() { 847 return (applicationInfoFromAnyUserOrError(/* flags= */ 0).flags & FLAG_SYSTEM) > 0; 848 } 849 850 @Experimental isInstantApp()851 public boolean isInstantApp() { 852 return sPackageManager.isInstantApp(mPackageName); 853 } 854 855 /** Get the AppComponentFactory for the package. */ 856 @Experimental 857 @Nullable 858 @TargetApi(P) appComponentFactory()859 public String appComponentFactory() { 860 return applicationInfoFromAnyUserOrError(/* flags= */ 0).appComponentFactory; 861 } 862 applicationInfoFromAnyUserOrError(int flags)863 private ApplicationInfo applicationInfoFromAnyUserOrError(int flags) { 864 ApplicationInfo appInfo = applicationInfoFromAnyUser(flags); 865 if (appInfo == null) { 866 throw new NeneException("Package not installed: " + this); 867 } 868 return appInfo; 869 } 870 871 /** 872 * Gets the shared user id of the package. 873 */ 874 @Experimental sharedUserId()875 public String sharedUserId() { 876 PackageInfo packageInfo = packageInfoFromAnyUser(/* flags= */ 0); 877 878 if (packageInfo == null) { 879 throw new NeneException("Error getting sharedUserId, does not exist"); 880 } 881 882 return packageInfo.sharedUserId; 883 } 884 885 /** 886 * See {@link PackageManager#setSyntheticAppDetailsActivityEnabled(String, boolean)}. 887 */ 888 @Experimental setSyntheticAppDetailsActivityEnabled(UserReference user, boolean enabled)889 public void setSyntheticAppDetailsActivityEnabled(UserReference user, boolean enabled) { 890 try (PermissionContext p = TestApis.permissions() 891 .withPermission(CHANGE_COMPONENT_ENABLED_STATE)) { 892 TestApis.context().androidContextAsUser(user).getPackageManager() 893 .setSyntheticAppDetailsActivityEnabled(packageName(), enabled); 894 } 895 } 896 897 /** 898 * See {@link PackageManager#setSyntheticAppDetailsActivityEnabled(String, boolean)}. 899 */ 900 @Experimental setSyntheticAppDetailsActivityEnabled(boolean enabled)901 public void setSyntheticAppDetailsActivityEnabled(boolean enabled) { 902 setSyntheticAppDetailsActivityEnabled(TestApis.users().instrumented(), enabled); 903 } 904 905 /** 906 * See {@link PackageManager#getSyntheticAppDetailsActivityEnabled(String)}. 907 */ 908 @Experimental syntheticAppDetailsActivityEnabled(UserReference user)909 public boolean syntheticAppDetailsActivityEnabled(UserReference user) { 910 return TestApis.context().androidContextAsUser(user).getPackageManager() 911 .getSyntheticAppDetailsActivityEnabled(packageName()); 912 } 913 914 /** 915 * See {@link PackageManager#getSyntheticAppDetailsActivityEnabled(String)}. 916 */ 917 @Experimental syntheticAppDetailsActivityEnabled()918 public boolean syntheticAppDetailsActivityEnabled() { 919 return syntheticAppDetailsActivityEnabled(TestApis.users().instrumented()); 920 } 921 922 private static final class ProcessInfo { 923 final String mPackageName; 924 final int mPid; 925 final int mUid; 926 final int mUserId; 927 ProcessInfo(String packageName, int pid, int uid, int userId)928 ProcessInfo(String packageName, int pid, int uid, int userId) { 929 if (packageName == null) { 930 throw new NullPointerException(); 931 } 932 mPackageName = packageName; 933 mPid = pid; 934 mUid = uid; 935 mUserId = userId; 936 } 937 938 @Override toString()939 public String toString() { 940 return "ProcessInfo{packageName=" + mPackageName + ", pid=" 941 + mPid + ", uid=" + mUid + ", userId=" + mUserId + "}"; 942 } 943 } 944 945 /** 946 * Set this package as filling the given role on the instrumented user. 947 */ 948 @Experimental setAsRoleHolder(String role)949 public RoleContext setAsRoleHolder(String role) { 950 return setAsRoleHolder(role, TestApis.users().instrumented()); 951 } 952 953 /** 954 * Set this package as filling the given role. 955 */ 956 @Experimental setAsRoleHolder(String role, UserReference user)957 public RoleContext setAsRoleHolder(String role, UserReference user) { 958 try (PermissionContext p = TestApis.permissions().withPermission( 959 MANAGE_ROLE_HOLDERS, INTERACT_ACROSS_USERS_FULL)) { 960 961 Retry.logic(() -> { 962 TestApis.logcat().clear(); 963 DefaultBlockingCallback<Boolean> blockingCallback = new DefaultBlockingCallback<>(); 964 965 sRoleManager.addRoleHolderAsUser( 966 role, 967 mPackageName, 968 /* flags= */ 0, 969 user.userHandle(), 970 TestApis.context().instrumentedContext().getMainExecutor(), 971 blockingCallback::triggerCallback); 972 973 boolean success = blockingCallback.await(); 974 if (!success) { 975 fail("Could not set role holder of " + role + "." + " Relevant logcat: " 976 + TestApis.logcat().dump((line) -> line.contains(role))); 977 } 978 if (!TestApis.roles().getRoleHoldersAsUser(role, user).contains(packageName())) { 979 fail("addRoleHolderAsUser returned true but did not add role holder. " 980 + "Relevant logcat: " + TestApis.logcat().dump( 981 (line) -> line.contains(role))); 982 } 983 }).terminalException(e -> { 984 // Terminal unless we see logcat output indicating it might be temporary 985 var logcat = TestApis.logcat() 986 .dump(l -> l.contains("Error calling onAddRoleHolder()")); 987 if (!logcat.isEmpty()) { 988 // On low end devices - this can happen when the broadcast queue is full 989 try { 990 Thread.sleep(10_000); 991 } catch (InterruptedException ex) { 992 return true; 993 } 994 995 return false; 996 } 997 998 return true; 999 }).runAndWrapException(); 1000 1001 return new RoleContext(role, this, user); 1002 } 1003 } 1004 1005 /** 1006 * Remove this package from the given role on the instrumented user. 1007 */ 1008 @Experimental removeAsRoleHolder(String role)1009 public void removeAsRoleHolder(String role) { 1010 removeAsRoleHolder(role, TestApis.users().instrumented()); 1011 } 1012 1013 /** 1014 * Remove this package from the given role. 1015 */ 1016 @Experimental removeAsRoleHolder(String role, UserReference user)1017 public void removeAsRoleHolder(String role, UserReference user) { 1018 try (PermissionContext p = TestApis.permissions().withPermission( 1019 MANAGE_ROLE_HOLDERS)) { 1020 Retry.logic(() -> { 1021 TestApis.logcat().clear(); 1022 DefaultBlockingCallback<Boolean> blockingCallback = new DefaultBlockingCallback<>(); 1023 sRoleManager.removeRoleHolderAsUser( 1024 role, 1025 mPackageName, 1026 /* flags= */ 0, 1027 user.userHandle(), 1028 TestApis.context().instrumentedContext().getMainExecutor(), 1029 blockingCallback::triggerCallback); 1030 TestApis.roles().setBypassingRoleQualification(false); 1031 1032 boolean success = blockingCallback.await(); 1033 if (!success) { 1034 fail("Failed to clear the role holder of " 1035 + role + "."); 1036 } 1037 if (TestApis.roles().getRoleHoldersAsUser(role, user).contains(packageName())) { 1038 fail("removeRoleHolderAsUser returned true but did not remove role holder. " 1039 + "Relevant logcat: " + TestApis.logcat().dump( 1040 (line) -> line.contains(role))); 1041 } 1042 }).terminalException(e -> { 1043 // Terminal unless we see logcat output indicating it might be temporary 1044 var logcat = TestApis.logcat() 1045 .dump(l -> l.contains("Error calling onRemoveRoleHolder()")); 1046 if (!logcat.isEmpty()) { 1047 // On low end devices - this can happen when the broadcast queue is full 1048 try { 1049 Thread.sleep(10_000); 1050 } catch (InterruptedException ex) { 1051 return true; 1052 } 1053 1054 return false; 1055 } 1056 1057 return true; 1058 }).runAndWrapException(); 1059 } 1060 } 1061 1062 /** 1063 * True if the given package on the instrumented user can have its ability to interact across 1064 * profiles configured by the user. 1065 */ 1066 @Experimental canConfigureInteractAcrossProfiles()1067 public boolean canConfigureInteractAcrossProfiles() { 1068 return canConfigureInteractAcrossProfiles(TestApis.users().instrumented()); 1069 } 1070 1071 /** 1072 * True if the given package can have its ability to interact across profiles configured 1073 * by the user. 1074 */ 1075 @Experimental canConfigureInteractAcrossProfiles(UserReference user)1076 public boolean canConfigureInteractAcrossProfiles(UserReference user) { 1077 return TestApis.context().androidContextAsUser(user) 1078 .getSystemService(CrossProfileApps.class) 1079 .canConfigureInteractAcrossProfiles(packageName()); 1080 } 1081 1082 /** 1083 * Enable or disable this package from using @TestApis. 1084 */ 1085 @Experimental setAllowTestApiAccess(boolean allowed)1086 public void setAllowTestApiAccess(boolean allowed) { 1087 ShellCommand.builder("am compat") 1088 .addOperand(allowed ? "enable" : "disable") 1089 .addOperand("ALLOW_TEST_API_ACCESS") 1090 .addOperand(packageName()) 1091 .validate(s -> s.startsWith(allowed ? "Enabled change" : "Disabled change")) 1092 .executeOrThrowNeneException( 1093 "Error allowing/disallowing test api access for " + this); 1094 } 1095 1096 /** 1097 * True if the given package is suspended in the given user. 1098 */ 1099 @Experimental isSuspended(UserReference user)1100 public boolean isSuspended(UserReference user) { 1101 try (PermissionContext p = 1102 TestApis.permissions().withPermission(INTERACT_ACROSS_USERS_FULL)) { 1103 return TestApis.context().androidContextAsUser(user).getPackageManager() 1104 .isPackageSuspended(mPackageName); 1105 } catch (PackageManager.NameNotFoundException e) { 1106 throw new NeneException("Package " + mPackageName + " not found for user " + user); 1107 } 1108 } 1109 1110 /** 1111 * Get the app standby bucket of the package. 1112 */ 1113 @Experimental getAppStandbyBucket()1114 public int getAppStandbyBucket() { 1115 return getAppStandbyBucket(TestApis.users().instrumented()); 1116 } 1117 1118 /** 1119 * Get the app standby bucket of the package. 1120 */ 1121 @Experimental getAppStandbyBucket(UserReference user)1122 public int getAppStandbyBucket(UserReference user) { 1123 try { 1124 return ShellCommand.builderForUser(user, "am get-standby-bucket") 1125 .addOperand(mPackageName) 1126 .executeAndParseOutput(o -> Integer.parseInt(o.trim())); 1127 } catch (AdbException e) { 1128 throw new NeneException("Could not get app standby bucket " + this, e); 1129 } 1130 } 1131 1132 /** Approves all links for an auto verifiable app */ 1133 @Experimental setAppLinksToAllApproved()1134 public void setAppLinksToAllApproved() { 1135 try { 1136 ShellCommand.builder("pm set-app-links") 1137 .addOption("--package", this.mPackageName) 1138 .addOperand(2) // 2 = STATE_APPROVED 1139 .addOperand("all") 1140 .execute(); 1141 } catch (AdbException e) { 1142 throw new NeneException("Error verifying links ", e); 1143 } 1144 } 1145 1146 /** Checks if the current package is a role holder for the given role*/ 1147 @Experimental isRoleHolder(String role)1148 public boolean isRoleHolder(String role) { 1149 return TestApis.roles().getRoleHolders(role).contains(this.mPackageName); 1150 } 1151 1152 @Experimental clearStorage()1153 public void clearStorage() { 1154 ShellCommand.builder("pm clear") 1155 .addOperand(mPackageName) 1156 .validate(ShellCommandUtils::startsWithSuccess) 1157 .executeOrThrowNeneException("Error clearing storage for " + this); 1158 } 1159 } 1160