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.INTERACT_ACROSS_USERS_FULL; 21 import static android.Manifest.permission.QUERY_ALL_PACKAGES; 22 import static android.content.pm.ApplicationInfo.FLAG_STOPPED; 23 import static android.content.pm.ApplicationInfo.FLAG_SYSTEM; 24 import static android.content.pm.PackageManager.GET_PERMISSIONS; 25 import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES; 26 import static android.content.pm.PackageManager.PERMISSION_GRANTED; 27 import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS; 28 import static android.content.pm.PermissionInfo.PROTECTION_FLAG_DEVELOPMENT; 29 import static android.os.Build.VERSION_CODES.S; 30 import static android.os.Process.myUid; 31 32 import static com.google.common.truth.Truth.assertWithMessage; 33 34 import android.app.ActivityManager; 35 import android.content.Intent; 36 import android.content.IntentFilter; 37 import android.content.pm.ApplicationInfo; 38 import android.content.pm.PackageInfo; 39 import android.content.pm.PackageManager; 40 import android.content.pm.PermissionInfo; 41 import android.os.Build; 42 import android.os.UserHandle; 43 import android.util.Log; 44 45 import androidx.annotation.Nullable; 46 47 import com.android.bedstead.nene.TestApis; 48 import com.android.bedstead.nene.annotations.Experimental; 49 import com.android.bedstead.nene.appops.AppOps; 50 import com.android.bedstead.nene.devicepolicy.DeviceOwner; 51 import com.android.bedstead.nene.devicepolicy.ProfileOwner; 52 import com.android.bedstead.nene.exceptions.AdbException; 53 import com.android.bedstead.nene.exceptions.AdbParseException; 54 import com.android.bedstead.nene.exceptions.NeneException; 55 import com.android.bedstead.nene.permissions.PermissionContext; 56 import com.android.bedstead.nene.permissions.Permissions; 57 import com.android.bedstead.nene.users.UserReference; 58 import com.android.bedstead.nene.utils.Poll; 59 import com.android.bedstead.nene.utils.ShellCommand; 60 import com.android.bedstead.nene.utils.Versions; 61 import com.android.compatibility.common.util.BlockingBroadcastReceiver; 62 63 import java.io.File; 64 import java.util.Arrays; 65 import java.util.HashSet; 66 import java.util.Objects; 67 import java.util.Set; 68 import java.util.function.Supplier; 69 import java.util.stream.Collectors; 70 71 /** 72 * A representation of a package on device which may or may not exist. 73 */ 74 public final class Package { 75 private static final String LOG_TAG = "PackageReference"; 76 private static final int PIDS_PER_USER_ID = 100000; 77 private static final PackageManager sPackageManager = 78 TestApis.context().instrumentedContext().getPackageManager(); 79 80 private final String mPackageName; 81 Package(String packageName)82 Package(String packageName) { 83 mPackageName = packageName; 84 } 85 86 /** Return the package's name. */ packageName()87 public String packageName() { 88 return mPackageName; 89 } 90 91 /** 92 * Install the package on the given user. 93 * 94 * <p>If you wish to install a package which is not already installed on another user, see 95 * {@link Packages#install(UserReference, File)}. 96 */ installExisting(UserReference user)97 public Package installExisting(UserReference user) { 98 if (user == null) { 99 throw new NullPointerException(); 100 } 101 102 try { 103 // Expected output "Package X installed for user: Y" 104 ShellCommand.builderForUser(user, "cmd package install-existing") 105 .addOperand(mPackageName) 106 .validate( 107 (output) -> output.contains("installed for user")) 108 .execute(); 109 return this; 110 } catch (AdbException e) { 111 throw new NeneException("Could not install-existing package " + this, e); 112 } 113 } 114 115 /** 116 * Install this package on the given user, using {@link #installExisting(UserReference)} if 117 * possible, otherwise installing fresh. 118 */ install(UserReference user, File apkFile)119 public Package install(UserReference user, File apkFile) { 120 if (exists()) { 121 return installExisting(user); 122 } 123 124 return TestApis.packages().install(user, apkFile); 125 } 126 127 /** 128 * Install this package on the given user, using {@link #installExisting(UserReference)} if 129 * possible, otherwise installing fresh. 130 */ install(UserReference user, Supplier<File> apkFile)131 public Package install(UserReference user, Supplier<File> apkFile) { 132 if (exists()) { 133 return installExisting(user); 134 } 135 136 return TestApis.packages().install(user, apkFile.get()); 137 } 138 139 /** 140 * Install this package on the given user, using {@link #installExisting(UserReference)} if 141 * possible, otherwise installing fresh. 142 */ installBytes(UserReference user, byte[] apkFile)143 public Package installBytes(UserReference user, byte[] apkFile) { 144 if (exists()) { 145 return installExisting(user); 146 } 147 148 return TestApis.packages().install(user, apkFile); 149 } 150 151 /** 152 * Install this package on the given user, using {@link #installExisting(UserReference)} if 153 * possible, otherwise installing fresh. 154 */ installBytes(UserReference user, Supplier<byte[]> apkFile)155 public Package installBytes(UserReference user, Supplier<byte[]> apkFile) { 156 if (exists()) { 157 return installExisting(user); 158 } 159 160 return TestApis.packages().install(user, apkFile.get()); 161 } 162 163 /** 164 * Uninstall the package for all users. 165 */ uninstallFromAllUsers()166 public Package uninstallFromAllUsers() { 167 for (UserReference user : installedOnUsers()) { 168 uninstall(user); 169 } 170 171 return this; 172 } 173 174 /** 175 * Uninstall the package for the given user. 176 * 177 * <p>If the package is not installed for the given user, nothing will happen. 178 */ uninstall(UserReference user)179 public Package uninstall(UserReference user) { 180 if (user == null) { 181 throw new NullPointerException(); 182 } 183 184 IntentFilter packageRemovedIntentFilter = 185 new IntentFilter(Intent.ACTION_PACKAGE_REMOVED); 186 packageRemovedIntentFilter.addDataScheme("package"); 187 188 // This is outside of the try because we don't want to await if the package isn't installed 189 BlockingBroadcastReceiver broadcastReceiver = BlockingBroadcastReceiver.create( 190 TestApis.context().androidContextAsUser(user), 191 packageRemovedIntentFilter); 192 193 try { 194 195 boolean canWaitForBroadcast = false; 196 if (Versions.meetsMinimumSdkVersionRequirement(Build.VERSION_CODES.R)) { 197 try (PermissionContext p = TestApis.permissions().withPermission( 198 INTERACT_ACROSS_USERS_FULL)) { 199 broadcastReceiver.register(); 200 } 201 canWaitForBroadcast = true; 202 } else if (user.equals(TestApis.users().instrumented())) { 203 broadcastReceiver.register(); 204 canWaitForBroadcast = true; 205 } 206 207 String commandOutput = Poll.forValue(() -> { 208 // Expected output "Success" 209 return ShellCommand.builderForUser(user, "pm uninstall") 210 .addOperand(mPackageName) 211 .execute(); 212 }).toMeet(output -> output.toUpperCase().startsWith("SUCCESS") 213 || output.toUpperCase().contains("NOT INSTALLED FOR")) 214 .terminalValue((output) -> { 215 if (output.contains("DELETE_FAILED_DEVICE_POLICY_MANAGER")) { 216 // A recently-removed device policy manager can't be removed - but won't 217 // show as DPC 218 219 DeviceOwner deviceOwner = TestApis.devicePolicy().getDeviceOwner(); 220 if (deviceOwner != null && deviceOwner.pkg().equals(this)) { 221 // Terminal, can't remove actual DO 222 return true; 223 } 224 ProfileOwner profileOwner = 225 TestApis.devicePolicy().getProfileOwner(user); 226 // Terminal, can't remove actual PO 227 return profileOwner != null && profileOwner.pkg().equals(this); 228 229 // Not PO or DO, likely temporary failure 230 } 231 232 return true; 233 }) 234 .errorOnFail() 235 .await(); 236 237 if (commandOutput.toUpperCase().startsWith("SUCCESS")) { 238 if (canWaitForBroadcast) { 239 broadcastReceiver.awaitForBroadcastOrFail(); 240 } else { 241 try { 242 // On versions prior to R - cross user installs can't block for broadcasts 243 // so we have an arbitrary sleep 244 Thread.sleep(10000); 245 } catch (InterruptedException e) { 246 Log.i(LOG_TAG, "Interrupted waiting for package uninstallation", e); 247 } 248 } 249 } 250 return this; 251 } catch (NeneException e) { 252 throw new NeneException("Could not uninstall package " + this, e); 253 } finally { 254 broadcastReceiver.unregisterQuietly(); 255 } 256 } 257 258 /** 259 * Enable this package for the given {@link UserReference}. 260 */ 261 @Experimental enable(UserReference user)262 public Package enable(UserReference user) { 263 try { 264 ShellCommand.builderForUser(user, "pm enable") 265 .addOperand(mPackageName) 266 .validate(o -> o.contains("new state")) 267 .execute(); 268 } catch (AdbException e) { 269 throw new NeneException("Error enabling package " + this + " for user " + user, e); 270 } 271 return this; 272 } 273 274 /** 275 * Enable this package on the instrumented user. 276 */ 277 @Experimental enable()278 public Package enable() { 279 return enable(TestApis.users().instrumented()); 280 } 281 282 /** 283 * Disable this package for the given {@link UserReference}. 284 */ 285 @Experimental disable(UserReference user)286 public Package disable(UserReference user) { 287 try { 288 ShellCommand.builderForUser(user, "pm disable") 289 .addOperand(mPackageName) 290 .validate(o -> o.contains("new state")) 291 .execute(); 292 } catch (AdbException e) { 293 throw new NeneException("Error disabling package " + this + " for user " + user, e); 294 } 295 return this; 296 } 297 298 /** 299 * Disable this package on the instrumented user. 300 */ 301 @Experimental disable()302 public Package disable() { 303 return disable(TestApis.users().instrumented()); 304 } 305 306 /** 307 * Get a reference to the given {@code componentName} within this package. 308 * 309 * <p>This does not guarantee that the component exists. 310 */ 311 @Experimental component(String componentName)312 public ComponentReference component(String componentName) { 313 return new ComponentReference(this, componentName); 314 } 315 316 /** 317 * Grant a permission for the package on the given user. 318 * 319 * <p>The package must be installed on the user, must request the given permission, and the 320 * permission must be a runtime permission. 321 */ grantPermission(UserReference user, String permission)322 public Package grantPermission(UserReference user, String permission) { 323 // There is no readable output upon failure so we need to check ourselves 324 checkCanGrantOrRevokePermission(user, permission); 325 326 try { 327 ShellCommand.builderForUser(user, "pm grant") 328 .addOperand(packageName()) 329 .addOperand(permission) 330 .allowEmptyOutput(true) 331 .validate(String::isEmpty) 332 .execute(); 333 334 assertWithMessage("Error granting permission " + permission 335 + " to package " + this + " on user " + user 336 + ". Command appeared successful but not set.") 337 .that(hasPermission(user, permission)).isTrue(); 338 339 return this; 340 } catch (AdbException e) { 341 throw new NeneException("Error granting permission " + permission + " to package " 342 + this + " on user " + user, e); 343 } 344 } 345 346 /** Grant the {@code permission} on the instrumented user. */ grantPermission(String permission)347 public Package grantPermission(String permission) { 348 return grantPermission(TestApis.users().instrumented(), permission); 349 } 350 351 /** Deny the {@code permission} on the instrumented user. */ denyPermission(String permission)352 public Package denyPermission(String permission) { 353 return denyPermission(TestApis.users().instrumented(), permission); 354 } 355 356 /** 357 * Deny a permission for the package on the given user. 358 * 359 * <p>The package must be installed on the user, must request the given permission, and the 360 * permission must be a runtime permission. 361 * 362 * <p>You can not deny permissions for the current package on the current user. 363 */ denyPermission(UserReference user, String permission)364 public Package denyPermission(UserReference user, String permission) { 365 // There is no readable output upon failure so we need to check ourselves 366 checkCanGrantOrRevokePermission(user, permission); 367 368 if (packageName().equals(TestApis.context().instrumentedContext().getPackageName()) 369 && user.equals(TestApis.users().instrumented())) { 370 if (!hasPermission(user, permission)) { 371 return this; // Already denied 372 } 373 throw new NeneException("Cannot deny permission from current package"); 374 } 375 376 try { 377 ShellCommand.builderForUser(user, "pm revoke") 378 .addOperand(packageName()) 379 .addOperand(permission) 380 .allowEmptyOutput(true) 381 .validate(String::isEmpty) 382 .execute(); 383 384 assertWithMessage("Error denying permission " + permission 385 + " to package " + this + " on user " + user 386 + ". Command appeared successful but not set.") 387 .that(hasPermission(user, permission)).isFalse(); 388 389 return this; 390 } catch (AdbException e) { 391 throw new NeneException("Error denying permission " + permission + " to package " 392 + this + " on user " + user, e); 393 } 394 } 395 checkCanGrantOrRevokePermission(UserReference user, String permission)396 private void checkCanGrantOrRevokePermission(UserReference user, String permission) { 397 if (!installedOnUser(user)) { 398 throw new NeneException("Attempting to grant " + permission + " to " + this 399 + " on user " + user + ". But it is not installed"); 400 } 401 402 try { 403 PermissionInfo permissionInfo = 404 sPackageManager.getPermissionInfo(permission, /* flags= */ 0); 405 406 if (!protectionIsDangerous(permissionInfo.protectionLevel) 407 && !protectionIsDevelopment(permissionInfo.protectionLevel)) { 408 throw new NeneException("Cannot grant non-runtime permission " 409 + permission + ", protection level is " + permissionInfo.protectionLevel); 410 } 411 412 if (!requestedPermissions().contains(permission)) { 413 throw new NeneException("Cannot grant permission " 414 + permission + " which was not requested by package " + packageName()); 415 } 416 } catch (PackageManager.NameNotFoundException e) { 417 throw new NeneException("Permission does not exist: " + permission); 418 } 419 } 420 protectionIsDangerous(int protectionLevel)421 private boolean protectionIsDangerous(int protectionLevel) { 422 return (protectionLevel & PROTECTION_DANGEROUS) != 0; 423 } 424 protectionIsDevelopment(int protectionLevel)425 private boolean protectionIsDevelopment(int protectionLevel) { 426 return (protectionLevel & PROTECTION_FLAG_DEVELOPMENT) != 0; 427 } 428 429 /** Get running {@link ProcessReference} for this package on all users. */ 430 @Experimental runningProcesses()431 public Set<ProcessReference> runningProcesses() { 432 // TODO(scottjonathan): See if this can be remade using 433 // ActivityManager#getRunningappProcesses 434 try { 435 return ShellCommand.builder("ps") 436 .addOperand("-A") 437 .addOperand("-n") 438 .executeAndParseOutput(o -> parsePsOutput(o).stream() 439 .filter(p -> p.mPackageName.equals(mPackageName)) 440 .map(p -> new ProcessReference(this, p.mPid, p.mUid, 441 TestApis.users().find(p.mUserId)))) 442 .collect(Collectors.toSet()); 443 } catch (AdbException e) { 444 throw new NeneException("Error getting running processes ", e); 445 } 446 } 447 parsePsOutput(String psOutput)448 private Set<ProcessInfo> parsePsOutput(String psOutput) { 449 return Arrays.stream(psOutput.split("\n")) 450 .skip(1) // Skip the title line 451 .map(s -> s.split("\\s+")) 452 .map(m -> new ProcessInfo( 453 m[8], Integer.parseInt(m[1]), 454 Integer.parseInt(m[0]), 455 Integer.parseInt(m[0]) / PIDS_PER_USER_ID)) 456 .collect(Collectors.toSet()); 457 } 458 459 /** Get the running {@link ProcessReference} for this package on the given user. */ 460 @Experimental 461 @Nullable runningProcess(UserReference user)462 public ProcessReference runningProcess(UserReference user) { 463 return runningProcesses().stream().filter( 464 i -> i.user().equals(user)) 465 .findAny() 466 .orElse(null); 467 } 468 469 /** Get the running {@link ProcessReference} for this package on the given user. */ 470 @Experimental 471 @Nullable runningProcess(UserHandle user)472 public ProcessReference runningProcess(UserHandle user) { 473 return runningProcess(TestApis.users().find(user)); 474 } 475 476 /** Get the running {@link ProcessReference} for this package on the instrumented user. */ 477 @Experimental 478 @Nullable runningProcess()479 public ProcessReference runningProcess() { 480 return runningProcess(TestApis.users().instrumented()); 481 } 482 483 /** {@code true} if the package is installed on the given user. */ installedOnUser(UserHandle userHandle)484 public boolean installedOnUser(UserHandle userHandle) { 485 return installedOnUser(TestApis.users().find(userHandle)); 486 } 487 488 /** {@code true} if the package is installed on the given user. */ installedOnUser(UserReference user)489 public boolean installedOnUser(UserReference user) { 490 return packageInfoForUser(user, /* flags= */ 0) != null; 491 } 492 493 /** {@code true} if the package is installed on the instrumented user. */ installedOnUser()494 public boolean installedOnUser() { 495 return installedOnUser(TestApis.users().instrumented()); 496 } 497 498 /** {@code true} if the package on the given user has the given permission. */ hasPermission(UserReference user, String permission)499 public boolean hasPermission(UserReference user, String permission) { 500 return TestApis.context().androidContextAsUser(user).getPackageManager() 501 .checkPermission(permission, mPackageName) == PERMISSION_GRANTED; 502 } 503 504 /** {@code true} if the package on the given user has the given permission. */ hasPermission(UserHandle user, String permission)505 public boolean hasPermission(UserHandle user, String permission) { 506 return hasPermission(TestApis.users().find(user), permission); 507 } 508 509 /** {@code true} if the package on the instrumented user has the given permission. */ hasPermission(String permission)510 public boolean hasPermission(String permission) { 511 return hasPermission(TestApis.users().instrumented(), permission); 512 } 513 514 /** Get the permissions requested in the package's manifest. */ requestedPermissions()515 public Set<String> requestedPermissions() { 516 PackageInfo packageInfo = packageInfoFromAnyUser(GET_PERMISSIONS); 517 518 if (packageInfo == null) { 519 if (TestApis.packages().instrumented().isInstantApp()) { 520 Log.i(LOG_TAG, "Tried to get requestedPermissions for " 521 + mPackageName + " but can't on instant apps"); 522 return new HashSet<>(); 523 } 524 throw new NeneException("Error getting requestedPermissions, does not exist"); 525 } 526 527 if (packageInfo.requestedPermissions == null) { 528 return new HashSet<>(); 529 } 530 531 return new HashSet<>(Arrays.asList(packageInfo.requestedPermissions)); 532 } 533 534 @Nullable packageInfoFromAnyUser(int flags)535 private PackageInfo packageInfoFromAnyUser(int flags) { 536 return TestApis.users().all().stream() 537 .map(i -> packageInfoForUser(i, flags)) 538 .filter(Objects::nonNull) 539 .findFirst() 540 .orElse(null); 541 } 542 543 @Nullable packageInfoForUser(UserReference user, int flags)544 private PackageInfo packageInfoForUser(UserReference user, int flags) { 545 if (user.equals(TestApis.users().instrumented())) { 546 try { 547 return TestApis.context().instrumentedContext() 548 .getPackageManager() 549 .getPackageInfo(mPackageName, /* flags= */ flags); 550 } catch (PackageManager.NameNotFoundException e) { 551 return null; 552 } 553 } 554 555 if (!Versions.meetsMinimumSdkVersionRequirement(S)) { 556 return packageInfoForUserPreS(user, flags); 557 } 558 559 if (Permissions.sIgnorePermissions.get()) { 560 try { 561 return TestApis.context().androidContextAsUser(user) 562 .getPackageManager() 563 .getPackageInfo(mPackageName, /* flags= */ flags); 564 } catch (PackageManager.NameNotFoundException e) { 565 return null; 566 } 567 } else { 568 try (PermissionContext p = TestApis.permissions().withPermission( 569 INTERACT_ACROSS_USERS_FULL)) { 570 return TestApis.context().androidContextAsUser(user) 571 .getPackageManager() 572 .getPackageInfo(mPackageName, /* flags= */ flags); 573 } catch (PackageManager.NameNotFoundException e) { 574 return null; 575 } 576 } 577 } 578 packageInfoForUserPreS(UserReference user, int flags)579 private PackageInfo packageInfoForUserPreS(UserReference user, int flags) { 580 AdbPackage pkg = Packages.parseDumpsys().mPackages.get(mPackageName); 581 582 if (pkg == null) { 583 return null; 584 } 585 586 if (!pkg.installedOnUsers().contains(user)) { 587 return null; 588 } 589 590 PackageInfo packageInfo = new PackageInfo(); 591 packageInfo.packageName = mPackageName; 592 packageInfo.requestedPermissions = pkg.requestedPermissions().toArray(new String[]{}); 593 594 return packageInfo; 595 } 596 597 @Nullable applicationInfoFromAnyUser(int flags)598 private ApplicationInfo applicationInfoFromAnyUser(int flags) { 599 return TestApis.users().all().stream() 600 .map(i -> applicationInfoForUser(i, flags)) 601 .filter(Objects::nonNull) 602 .findFirst() 603 .orElse(null); 604 } 605 606 @Nullable applicationInfoForUser(UserReference user, int flags)607 private ApplicationInfo applicationInfoForUser(UserReference user, int flags) { 608 if (user.equals(TestApis.users().instrumented())) { 609 try { 610 return TestApis.context().instrumentedContext() 611 .getPackageManager() 612 .getApplicationInfo(mPackageName, /* flags= */ flags); 613 } catch (PackageManager.NameNotFoundException e) { 614 return null; 615 } 616 } 617 618 if (!Versions.meetsMinimumSdkVersionRequirement(Build.VERSION_CODES.Q)) { 619 return applicationInfoForUserPreQ(user, flags); 620 } 621 622 if (Permissions.sIgnorePermissions.get()) { 623 try { 624 return TestApis.context().androidContextAsUser(user) 625 .getPackageManager() 626 .getApplicationInfo(mPackageName, /* flags= */ flags); 627 } catch (PackageManager.NameNotFoundException e) { 628 return null; 629 } 630 } else { 631 try (PermissionContext p = TestApis.permissions().withPermission( 632 INTERACT_ACROSS_USERS_FULL)) { 633 return TestApis.context().androidContextAsUser(user) 634 .getPackageManager() 635 .getApplicationInfo(mPackageName, /* flags= */ flags); 636 } catch (PackageManager.NameNotFoundException e) { 637 return null; 638 } 639 } 640 } 641 applicationInfoForUserPreQ(UserReference user, int flags)642 private ApplicationInfo applicationInfoForUserPreQ(UserReference user, int flags) { 643 try { 644 String dumpsysOutput = ShellCommand.builder("dumpsys package").execute(); 645 646 AdbPackageParser.ParseResult r = Packages.sParser.parse(dumpsysOutput); 647 AdbPackage pkg = r.mPackages.get(mPackageName); 648 649 if (pkg == null) { 650 return null; 651 } 652 653 ApplicationInfo applicationInfo = new ApplicationInfo(); 654 applicationInfo.packageName = mPackageName; 655 applicationInfo.uid = -1; // TODO: Get the actual uid... 656 657 return applicationInfo; 658 } catch (AdbException | AdbParseException e) { 659 throw new NeneException("Error getting package info pre Q", e); 660 } 661 } 662 663 /** 664 * Get all users this package is installed on. 665 * 666 * <p>Note that this is an expensive operation - favor {@link #installedOnUser(UserReference)} 667 * when possible. 668 */ installedOnUsers()669 public Set<UserReference> installedOnUsers() { 670 return TestApis.users().all().stream() 671 .filter(this::installedOnUser) 672 .collect(Collectors.toSet()); 673 } 674 675 /** 676 * Force the running instance of the package to stop on the given user. 677 * 678 * <p>See {@link ActivityManager#forceStopPackage(String)}. 679 */ 680 @Experimental forceStop(UserReference user)681 public void forceStop(UserReference user) { 682 try (PermissionContext p = TestApis.permissions().withPermission(FORCE_STOP_PACKAGES)) { 683 ActivityManager userActivityManager = 684 TestApis.context().androidContextAsUser(user) 685 .getSystemService(ActivityManager.class); 686 687 PackageManager userPackageManager = 688 TestApis.context().androidContextAsUser(user).getPackageManager(); 689 690 // In most cases this should work first time, however if a user restriction has been 691 // recently removed we may need to retry 692 Poll.forValue("Application flag", () -> { 693 userActivityManager.forceStopPackage(mPackageName); 694 695 return userPackageManager.getPackageInfo(mPackageName, PackageManager.GET_META_DATA) 696 .applicationInfo.flags; 697 }) 698 .toMeet(flag -> (flag & FLAG_STOPPED) == FLAG_STOPPED) 699 .errorOnFail("Expected application flags to contain FLAG_STOPPED (" 700 + FLAG_STOPPED + ")") 701 .await(); 702 } 703 } 704 705 /** 706 * Force the running instance of the package to stop on the instrumented user. 707 * 708 * <p>See {@link ActivityManager#forceStopPackage(String)}. 709 */ 710 @Experimental forceStop()711 public void forceStop() { 712 forceStop(TestApis.users().instrumented()); 713 } 714 715 /** 716 * Interact with AppOps for the given package. 717 */ 718 @Experimental appOps()719 public AppOps appOps() { 720 return new AppOps(this); 721 } 722 723 /** 724 * Get the UID of the package on the instrumented user. 725 */ 726 @Experimental uid()727 public int uid() { 728 return uid(TestApis.users().instrumented()); 729 } 730 731 /** 732 * Get the UID of the package on the given {@code user}. 733 */ 734 @Experimental uid(UserReference user)735 public int uid(UserReference user) { 736 if (user.equals(TestApis.users().instrumented()) 737 && this.equals(TestApis.packages().instrumented())) { 738 return myUid(); 739 } 740 741 ApplicationInfo applicationInfo = applicationInfoForUser(user, /* flags= */ 0); 742 if (applicationInfo == null) { 743 throw new IllegalStateException( 744 "Trying to get uid for not installed package " + this + " on user " + user); 745 } 746 747 return applicationInfo.uid; 748 } 749 750 @Override hashCode()751 public int hashCode() { 752 return mPackageName.hashCode(); 753 } 754 755 @Override equals(Object obj)756 public boolean equals(Object obj) { 757 if (!(obj instanceof Package)) { 758 return false; 759 } 760 761 Package other = (Package) obj; 762 return other.mPackageName.equals(mPackageName); 763 } 764 765 @Override toString()766 public String toString() { 767 StringBuilder stringBuilder = new StringBuilder("PackageReference{"); 768 stringBuilder.append("packageName=" + mPackageName); 769 stringBuilder.append("}"); 770 return stringBuilder.toString(); 771 } 772 773 /** {@code true} if the package exists on the device. */ exists()774 public boolean exists() { 775 if (Versions.meetsMinimumSdkVersionRequirement(S)) { 776 try (PermissionContext p = TestApis.permissions().withPermission(QUERY_ALL_PACKAGES)) { 777 return packageInfoFromAnyUser(MATCH_UNINSTALLED_PACKAGES) != null; 778 } 779 } 780 781 return Packages.parseDumpsys().mPackages.containsKey(mPackageName); 782 } 783 784 /** 785 * {@code true} if the package is installed in the device's system image. 786 */ 787 @Experimental hasSystemFlag()788 public boolean hasSystemFlag() { 789 ApplicationInfo appInfo = applicationInfoFromAnyUser(/* flags= */ 0); 790 791 if (appInfo == null) { 792 throw new NeneException("Package not installed: " + this); 793 } 794 795 return (appInfo.flags & FLAG_SYSTEM) > 0; 796 } 797 798 @Experimental isInstantApp()799 public boolean isInstantApp() { 800 return sPackageManager.isInstantApp(mPackageName); 801 } 802 803 /** 804 * Gets the shared user id of the package. 805 */ 806 @Experimental sharedUserId()807 public String sharedUserId() { 808 PackageInfo packageInfo = packageInfoFromAnyUser(/* flags= */ 0); 809 810 if (packageInfo == null) { 811 throw new NeneException("Error getting sharedUserId, does not exist"); 812 } 813 814 return packageInfo.sharedUserId; 815 } 816 817 private static final class ProcessInfo { 818 final String mPackageName; 819 final int mPid; 820 final int mUid; 821 final int mUserId; 822 ProcessInfo(String packageName, int pid, int uid, int userId)823 ProcessInfo(String packageName, int pid, int uid, int userId) { 824 if (packageName == null) { 825 throw new NullPointerException(); 826 } 827 mPackageName = packageName; 828 mPid = pid; 829 mUid = uid; 830 mUserId = userId; 831 } 832 833 @Override toString()834 public String toString() { 835 return "ProcessInfo{packageName=" + mPackageName + ", pid=" 836 + mPid + ", uid=" + mUid + ", userId=" + mUserId + "}"; 837 } 838 } 839 } 840