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