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