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.INSTALL_PACKAGES; 20 import static android.Manifest.permission.INTERACT_ACROSS_USERS; 21 import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; 22 import static android.content.pm.PackageInstaller.EXTRA_PACKAGE_NAME; 23 import static android.content.pm.PackageInstaller.EXTRA_STATUS; 24 import static android.content.pm.PackageInstaller.EXTRA_STATUS_MESSAGE; 25 import static android.content.pm.PackageInstaller.STATUS_FAILURE; 26 import static android.content.pm.PackageInstaller.STATUS_SUCCESS; 27 import static android.content.pm.PackageInstaller.SessionParams.MODE_FULL_INSTALL; 28 import static android.os.Build.VERSION.SDK_INT; 29 import static android.os.Build.VERSION_CODES.R; 30 31 import static com.android.bedstead.permissions.CommonPermissions.INSTALL_TEST_ONLY_PACKAGE; 32 import static com.android.bedstead.permissions.CommonPermissions.USE_SYSTEM_DATA_LOADERS; 33 import static com.android.bedstead.nene.utils.FileUtils.readInputStreamFully; 34 35 import android.content.ComponentName; 36 import android.content.Context; 37 import android.content.Intent; 38 import android.content.IntentFilter; 39 import android.content.pm.FeatureInfo; 40 import android.content.pm.PackageInstaller; 41 import android.content.pm.PackageManager; 42 import android.content.pm.ResolveInfo; 43 import android.content.res.Resources; 44 import android.cts.testapisreflection.TestApisReflectionKt; 45 import android.os.Build; 46 import android.util.Log; 47 48 import androidx.annotation.CheckResult; 49 import androidx.annotation.RequiresApi; 50 51 import com.android.bedstead.nene.TestApis; 52 import com.android.bedstead.nene.activities.ActivityReference; 53 import com.android.bedstead.nene.annotations.Experimental; 54 import com.android.bedstead.nene.exceptions.AdbException; 55 import com.android.bedstead.nene.exceptions.AdbParseException; 56 import com.android.bedstead.nene.exceptions.NeneException; 57 import com.android.bedstead.permissions.PermissionContext; 58 import com.android.bedstead.nene.users.UserReference; 59 import com.android.bedstead.nene.utils.BlockingIntentSender; 60 import com.android.bedstead.nene.utils.Poll; 61 import com.android.bedstead.nene.utils.ResolveInfoWrapper; 62 import com.android.bedstead.nene.utils.ShellCommand; 63 import com.android.bedstead.nene.utils.ShellCommandUtils; 64 import com.android.bedstead.nene.utils.UndoableContext; 65 import com.android.bedstead.nene.utils.Versions; 66 import com.android.bedstead.nene.utils.BlockingBroadcastReceiver; 67 68 import java.io.File; 69 import java.io.FileInputStream; 70 import java.io.IOException; 71 import java.io.InputStream; 72 import java.io.OutputStream; 73 import java.time.Duration; 74 import java.util.Arrays; 75 import java.util.Collection; 76 import java.util.HashSet; 77 import java.util.List; 78 import java.util.Objects; 79 import java.util.Set; 80 import java.util.stream.Collectors; 81 82 import javax.annotation.Nullable; 83 84 /** 85 * Test APIs relating to packages. 86 */ 87 public final class Packages { 88 89 private static final String LOG_TAG = "Packages"; 90 91 /** Reference to a Java resource. */ 92 public static final class JavaResource { 93 private final String mName; 94 JavaResource(String name)95 private JavaResource(String name) { 96 mName = name; 97 } 98 99 /** Reference a Java resource by name. */ javaResource(String name)100 public static JavaResource javaResource(String name) { 101 if (name == null) { 102 throw new NullPointerException(); 103 } 104 return new JavaResource(name); 105 } 106 107 @Override toString()108 public String toString() { 109 return "JavaResource{name=" + mName + "}"; 110 } 111 112 @Override equals(Object o)113 public boolean equals(Object o) { 114 if (this == o) return true; 115 if (!(o instanceof JavaResource)) return false; 116 JavaResource that = (JavaResource) o; 117 return mName.equals(that.mName); 118 } 119 120 @Override hashCode()121 public int hashCode() { 122 return Objects.hash(mName); 123 } 124 } 125 126 /** Reference to an Android resource. */ 127 public static final class AndroidResource { 128 private final String mName; 129 AndroidResource(String name)130 private AndroidResource(String name) { 131 if (name == null) { 132 throw new NullPointerException(); 133 } 134 mName = name; 135 } 136 137 /** Reference an Android resource by name. */ androidResource(String name)138 public static AndroidResource androidResource(String name) { 139 return new AndroidResource(name); 140 } 141 142 @Override toString()143 public String toString() { 144 return "AndroidResource{name=" + mName + "}"; 145 } 146 147 @Override equals(Object o)148 public boolean equals(Object o) { 149 if (this == o) return true; 150 if (!(o instanceof AndroidResource)) return false; 151 AndroidResource that = (AndroidResource) o; 152 return mName.equals(that.mName); 153 } 154 155 @Override hashCode()156 public int hashCode() { 157 return Objects.hash(mName); 158 } 159 } 160 161 public static final Packages sInstance = new Packages(); 162 163 private static final String PACKAGE_VERIFIER_INCLUDE_ADB = "verifier_verify_adb_installs"; 164 165 private Set<String> mFeatures = null; 166 private final Context mInstrumentedContext; 167 168 private final IntentFilter mPackageAddedIntentFilter = 169 new IntentFilter(Intent.ACTION_PACKAGE_ADDED); 170 171 private static final PackageManager sPackageManager = 172 TestApis.context().instrumentedContext().getPackageManager(); 173 174 static final AdbPackageParser sParser = AdbPackageParser.get(SDK_INT); 175 176 Packages()177 public Packages() { 178 mPackageAddedIntentFilter.addDataScheme("package"); 179 mInstrumentedContext = TestApis.context().instrumentedContext(); 180 } 181 182 /** Get the features available on the device. */ features()183 public Set<String> features() { 184 if (mFeatures == null) { 185 mFeatures = new HashSet<>(); 186 PackageManager pm = TestApis.context().instrumentedContext().getPackageManager(); 187 FeatureInfo[] features = pm.getSystemAvailableFeatures(); 188 if (features != null) { 189 Arrays.stream(features).map(f -> f.name).forEach(mFeatures::add); 190 } 191 } 192 return mFeatures; 193 } 194 195 /** Get packages installed for the instrumented user. */ installedForUser()196 public Collection<Package> installedForUser() { 197 return installedForUser(TestApis.users().instrumented()); 198 } 199 200 /** 201 * Resolve all packages installed for a given {@link UserReference}. 202 */ installedForUser(UserReference user)203 public Collection<Package> installedForUser(UserReference user) { 204 if (user == null) { 205 throw new NullPointerException(); 206 } 207 208 if (!Versions.meetsMinimumSdkVersionRequirement(R) 209 || TestApis.packages().instrumented().isInstantApp()) { 210 AdbPackageParser.ParseResult packages = parseDumpsys(); 211 return packages.mPackages.values().stream() 212 .filter(p -> p.installedOnUsers().contains(user)) 213 .map(p -> find(p.packageName())) 214 .collect(Collectors.toSet()); 215 } 216 217 if (user.equals(TestApis.users().instrumented())) { 218 return TestApis.context().instrumentedContext().getPackageManager() 219 .getInstalledPackages(/* flags= */ 0) 220 .stream() 221 .map(i -> new Package(i.packageName)) 222 .collect(Collectors.toSet()); 223 } 224 225 try (PermissionContext p = TestApis.permissions() 226 .withPermission(INTERACT_ACROSS_USERS_FULL)) { 227 return TestApis.context().androidContextAsUser(user).getPackageManager() 228 .getInstalledPackages(/* flags= */ 0) 229 .stream() 230 .map(i -> new Package(i.packageName)) 231 .collect(Collectors.toSet()); 232 } 233 } 234 235 /** Install the {@link File} to the instrumented user. */ install(File apkFile)236 public Package install(File apkFile) { 237 return install(TestApis.users().instrumented(), apkFile); 238 } 239 240 /** Install a file as a byte array to the instrumented user. */ install(byte[] apkFile)241 public Package install(byte[] apkFile) { 242 return install(TestApis.users().instrumented(), apkFile); 243 } 244 245 /** 246 * Install an APK file to a given {@link UserReference}. 247 * 248 * <p>The user must be started. 249 * 250 * <p>If the package is already installed, this will replace it. 251 * 252 * <p>If the package is marked testOnly, it will still be installed. 253 * 254 * <p>On versions of Android prior to Q, this will return null. On other versions it will return 255 * the installed package. 256 */ 257 @Nullable install(UserReference user, File apkFile)258 public Package install(UserReference user, File apkFile) { 259 if (user == null || apkFile == null) { 260 throw new NullPointerException(); 261 } 262 263 if (Versions.meetsMinimumSdkVersionRequirement(Build.VERSION_CODES.S)) { 264 return install(user, loadBytes(apkFile)); 265 } 266 267 if (!user.exists()) { 268 throw new NeneException("Packages can not be installed in non-existing users " 269 + "(Trying to install into user " + user + ")"); 270 } 271 272 if (!user.isRunning()) { 273 throw new NeneException("Packages can not be installed in stopped users " 274 + "(Trying to install into user " + user + ")"); 275 } 276 277 if (!user.isUnlocked()) { 278 throw new NeneException("Packages can not be installed in locked users " 279 + "(Trying to install into user " + user + ")"); 280 } 281 282 try (UndoableContext verification = setVerifyAdbInstalls(false)) { 283 // This is not in the try because if the install fails we don't want to await the broadcast 284 BlockingBroadcastReceiver broadcastReceiver = 285 registerPackageInstalledBroadcastReceiver(user); 286 287 try { 288 Collection<Package> beforePackages = TestApis.packages().installedForUser(user); 289 290 // Expected output "Success" 291 String unused = ShellCommand.builderForUser(user, "pm install") 292 .addOperand("-r") // Reinstall automatically 293 .addOperand("-t") // Allow test-only install 294 .addOperand(apkFile.getAbsolutePath()) 295 .validate(ShellCommandUtils::startsWithSuccess) 296 .execute(); 297 298 Package installedPackage = Poll.forValue("newly installed packages", () -> { 299 Set<Package> packages = new HashSet<>( 300 TestApis.packages().installedForUser(user)); 301 packages.removeAll(beforePackages); 302 if (packages.isEmpty()) { 303 return null; 304 } 305 return packages.iterator().next(); 306 }).toNotBeNull() 307 .timeout(Duration.ofSeconds(10)) 308 .await(); 309 if (installedPackage == null) { 310 installedPackage = waitForPackageAddedBroadcast(broadcastReceiver); 311 } 312 return installedPackage; 313 } catch (AdbException e) { 314 throw new NeneException("Could not install " + apkFile + " for user " + user, e); 315 } finally { 316 if (broadcastReceiver != null) { 317 broadcastReceiver.unregisterQuietly(); 318 } 319 } 320 } 321 } 322 323 // TODO: Move this somewhere reusable (in utils) loadBytes(File file)324 private static byte[] loadBytes(File file) { 325 try (FileInputStream fis = new FileInputStream(file)) { 326 return readInputStreamFully(fis); 327 } catch (IOException e) { 328 throw new NeneException("Could not read file bytes for file " + file); 329 } 330 } 331 332 /** 333 * Install an APK from the given byte array to a given {@link UserReference}. 334 * 335 * <p>The user must be started. 336 * 337 * <p>If the package is already installed, this will replace it. 338 * 339 * <p>If the package is marked testOnly, it will still be installed. 340 * 341 * <p>When running as an instant app, this will return null. On other versions it will return 342 * the installed package. 343 */ 344 @Nullable install(UserReference user, byte[] apkFile)345 public Package install(UserReference user, byte[] apkFile) { 346 if (user == null || apkFile == null) { 347 throw new NullPointerException(); 348 } 349 350 if (!user.exists()) { 351 throw new NeneException("Packages can not be installed in non-existing users " 352 + "(Trying to install into user " + user + ")"); 353 } 354 355 if (!user.isRunning()) { 356 throw new NeneException("Packages can not be installed in stopped users " 357 + "(Trying to install into user " + user + ")"); 358 } 359 360 if (!user.isUnlocked()) { 361 throw new NeneException("Packages can not be installed in locked users " 362 + "(Trying to install into user " + user + ")"); 363 } 364 365 try (UndoableContext verification = setVerifyAdbInstalls(false)) { 366 if (TestApis.packages().instrumented().isInstantApp()) { 367 // We should install using stdin with the byte array 368 try { 369 String unused = ShellCommand.builderForUser(user, "pm install") 370 .addOperand("-t") // Allow installing test apks 371 .addOperand("-r") // Replace existing apps 372 .addOption("-S", apkFile.length) // Install from stdin 373 .writeToStdIn(apkFile) 374 .validate(ShellCommandUtils::startsWithSuccess) 375 .execute(); 376 } catch (AdbException e) { 377 throw new NeneException("Error installing from instant app", e); 378 } 379 380 // Arbitrary sleep because the shell command doesn't block and we can't listen for 381 // the broadcast (instant app) 382 try { 383 Thread.sleep(10_000); 384 } catch (InterruptedException e) { 385 throw new NeneException("Interrupted while waiting for install", e); 386 } 387 388 return null; 389 } 390 391 if (true || !Versions.meetsMinimumSdkVersionRequirement(Build.VERSION_CODES.S)) { 392 // We can't make use of -r when using SessionParams 393 return installUsingAdb(user, apkFile); 394 } 395 396 // This is not inside the try because if the install is unsuccessful we don't want to 397 // await the broadcast 398 BlockingBroadcastReceiver broadcastReceiver = 399 registerPackageInstalledBroadcastReceiver(user); 400 401 try { 402 PackageManager packageManager = 403 TestApis.context().androidContextAsUser(user).getPackageManager(); 404 PackageInstaller packageInstaller = packageManager.getPackageInstaller(); 405 406 int sessionId; 407 try (PermissionContext p = TestApis.permissions().withPermission( 408 INTERACT_ACROSS_USERS_FULL, INTERACT_ACROSS_USERS, 409 INSTALL_TEST_ONLY_PACKAGE, USE_SYSTEM_DATA_LOADERS)) { 410 PackageInstaller.SessionParams sessionParams = 411 new PackageInstaller.SessionParams( 412 MODE_FULL_INSTALL); 413 TestApisReflectionKt.setInstallFlagAllowTest(sessionParams); 414 sessionId = packageInstaller.createSession(sessionParams); 415 } 416 417 PackageInstaller.Session session = packageInstaller.openSession(sessionId); 418 try (OutputStream out = 419 session.openWrite("NAME", 0, apkFile.length)) { 420 out.write(apkFile); 421 session.fsync(out); 422 } 423 424 try (BlockingIntentSender intentSender = BlockingIntentSender.create()) { 425 try (PermissionContext p = 426 TestApis.permissions().withPermission( 427 INSTALL_PACKAGES, INSTALL_TEST_ONLY_PACKAGE)) { 428 session.commit(intentSender.intentSender()); 429 session.close(); 430 431 Intent intent = intentSender.await(); 432 433 if (intent == null) { 434 throw new NeneException( 435 "Did not receive intent from package installer session when" 436 + " installing bytes on user " + user 437 + ". Relevant logcat: " 438 + TestApis.logcat().dump( 439 l -> l.contains("PackageInstaller"))); 440 } 441 442 if (intent.getIntExtra(EXTRA_STATUS, /* defaultValue= */ STATUS_FAILURE) 443 != STATUS_SUCCESS) { 444 throw new NeneException("Not successful while installing package. " 445 + "Got status: " 446 + intent.getIntExtra( 447 EXTRA_STATUS, /* defaultValue= */ STATUS_FAILURE) 448 + " extra info: " + intent.getStringExtra( 449 EXTRA_STATUS_MESSAGE)); 450 } 451 452 String installedPackageName = intent.getStringExtra(EXTRA_PACKAGE_NAME); 453 return TestApis.packages().find(installedPackageName); 454 } 455 } 456 } catch (IOException e) { 457 throw new NeneException("Could not install package", e); 458 } finally { 459 if (broadcastReceiver != null) { 460 broadcastReceiver.unregisterQuietly(); 461 } 462 } 463 } 464 } 465 466 @Nullable installUsingAdb(UserReference user, byte[] apkFile)467 private Package installUsingAdb(UserReference user, byte[] apkFile) { 468 // This is not in the try because if the install fails we don't want to await the broadcast 469 BlockingBroadcastReceiver broadcastReceiver = 470 registerPackageInstalledBroadcastReceiver(user); 471 472 // We should install using stdin with the byte array 473 try { 474 Collection<Package> beforePackages = TestApis.packages().installedForUser(user); 475 476 String unused = ShellCommand.builderForUser(user, "pm install") 477 .addOperand("-t") // Allow installing test apks 478 .addOperand("-r") // Replace existing apps 479 .addOption("-S", apkFile.length) // Install from stdin 480 .writeToStdIn(apkFile) 481 .validate(ShellCommandUtils::startsWithSuccess) 482 .execute(); 483 484 Package installedPackage = Poll.forValue("newly installed packages", () -> { 485 Set<Package> packages = new HashSet<>( 486 TestApis.packages().installedForUser(user)); 487 packages.removeAll(beforePackages); 488 if (packages.isEmpty()) { 489 return null; 490 } 491 return packages.iterator().next(); 492 }).toNotBeNull() 493 .timeout(Duration.ofSeconds(10)) 494 .await(); 495 496 if (installedPackage == null) { 497 installedPackage = waitForPackageAddedBroadcast(broadcastReceiver); 498 } 499 return installedPackage; 500 } catch (AdbException e) { 501 throw new NeneException("Error installing package", e); 502 } finally { 503 if (broadcastReceiver != null) { 504 broadcastReceiver.unregisterQuietly(); 505 } 506 } 507 } 508 509 @Nullable waitForPackageAddedBroadcast(BlockingBroadcastReceiver broadcastReceiver)510 private Package waitForPackageAddedBroadcast(BlockingBroadcastReceiver broadcastReceiver) { 511 if (broadcastReceiver == null) { 512 // On Android versions prior to R we can't block on a broadcast for package installation 513 try { 514 Thread.sleep(20000); 515 } catch (InterruptedException e) { 516 Log.e(LOG_TAG, "Interrupted waiting for package installation", e); 517 } 518 519 return null; 520 } 521 522 Intent intent = broadcastReceiver.awaitForBroadcast(); 523 if (intent == null) { 524 throw new NeneException( 525 "Did not receive ACTION_PACKAGE_ADDED broadcast after installing package."); 526 } 527 // TODO(scottjonathan): Could this be flaky? what if something is added elsewhere at 528 // the same time... 529 String installedPackageName = intent.getDataString().split(":", 2)[1]; 530 531 return TestApis.packages().find(installedPackageName); 532 } 533 534 /** 535 * Install an APK stored in Android resources to the given {@link UserReference}. 536 * 537 * <p>The user must be started. 538 * 539 * <p>If the package is already installed, this will replace it. 540 * 541 * <p>If the package is marked testOnly, it will still be installed. 542 */ 543 @Experimental install(UserReference user, AndroidResource resource)544 public Package install(UserReference user, AndroidResource resource) { 545 int indexId = mInstrumentedContext.getResources().getIdentifier( 546 resource.mName, /* defType= */ null, /* defPackage= */ null); 547 548 try (InputStream inputStream = 549 mInstrumentedContext.getResources().openRawResource(indexId)) { 550 return install(user, readInputStreamFully(inputStream)); 551 } catch (IOException e) { 552 throw new NeneException("Error reading resource " + resource, e); 553 } 554 } 555 556 /** 557 * Install an APK stored in Java resources to the given {@link UserReference}. 558 * 559 * <p>The user must be started. 560 * 561 * <p>If the package is already installed, this will replace it. 562 * 563 * <p>If the package is marked testOnly, it will still be installed. 564 */ 565 @Experimental install(UserReference user, JavaResource resource)566 public Package install(UserReference user, JavaResource resource) { 567 try (InputStream inputStream = 568 Packages.class.getClassLoader().getResourceAsStream(resource.mName)) { 569 return install(user, readInputStreamFully(inputStream)); 570 } catch (IOException e) { 571 throw new NeneException("Error reading java resource " + resource, e); 572 } 573 } 574 575 @Nullable registerPackageInstalledBroadcastReceiver( UserReference user)576 private BlockingBroadcastReceiver registerPackageInstalledBroadcastReceiver( 577 UserReference user) { 578 BlockingBroadcastReceiver broadcastReceiver = BlockingBroadcastReceiver.create( 579 TestApis.context().androidContextAsUser(user), 580 mPackageAddedIntentFilter); 581 582 if (user.equals(TestApis.users().instrumented())) { 583 BlockingBroadcastReceiver unused = broadcastReceiver.register(); 584 } else if (Versions.meetsMinimumSdkVersionRequirement(Build.VERSION_CODES.Q)) { 585 try (PermissionContext p = 586 TestApis.permissions().withPermission(INTERACT_ACROSS_USERS_FULL)) { 587 BlockingBroadcastReceiver unused = broadcastReceiver.register(); 588 } 589 } else { 590 return null; 591 } 592 593 return broadcastReceiver; 594 } 595 596 /** 597 * Set packages which will not be cleaned up by the system even if they are not installed on 598 * any user. 599 * 600 * <p>This will ensure they can still be resolved and re-installed without needing the APK 601 */ 602 @RequiresApi(Build.VERSION_CODES.S) 603 @CheckResult keepUninstalledPackages()604 public KeepUninstalledPackagesBuilder keepUninstalledPackages() { 605 Versions.requireMinimumVersion(Build.VERSION_CODES.S); 606 607 return new KeepUninstalledPackagesBuilder(); 608 } 609 610 /** 611 * Get a reference to a package with the given {@code packageName}. 612 * 613 * <p>This does not guarantee that the package exists. Call {@link Package#exists()} 614 * to find if the package exists on the device, or {@link Package#installedOnUsers()} 615 * to find the users it is installed for. 616 */ find(String packageName)617 public Package find(String packageName) { 618 if (packageName == null) { 619 throw new NullPointerException(); 620 } 621 return new Package(packageName); 622 } 623 624 /** 625 * Get a reference to a given {@code componentName} activity. 626 * 627 * <p>This does not guarantee that the component exists - nor that it is actually an activity. 628 */ 629 @Experimental activity(ComponentName componentName)630 public ActivityReference activity(ComponentName componentName) { 631 if (componentName == null) { 632 throw new NullPointerException(); 633 } 634 635 return new ActivityReference( 636 find(componentName.getPackageName()), componentName.getClassName()); 637 } 638 639 /** 640 * Get a reference to a given {@code componentName}. 641 * 642 * <p>This does not guarantee that the component exists. 643 */ 644 @Experimental component(ComponentName componentName)645 public ComponentReference component(ComponentName componentName) { 646 if (componentName == null) { 647 throw new NullPointerException(); 648 } 649 650 return new ComponentReference( 651 find(componentName.getPackageName()), componentName.getClassName()); 652 } 653 654 /** Get a reference to the package being instrumented. */ 655 @Experimental instrumented()656 public Package instrumented() { 657 return find(TestApis.context().instrumentedContext().getPackageName()); 658 } 659 parseDumpsys()660 static AdbPackageParser.ParseResult parseDumpsys() { 661 try { 662 String dumpsysOutput = ShellCommand.builder("dumpsys package").execute(); 663 return Packages.sParser.parse(dumpsysOutput); 664 } catch (AdbException | AdbParseException e) { 665 throw new NeneException("Error parsing package dumpsys", e); 666 } 667 } 668 669 /** 670 * System apps installed on the instrumented user. 671 */ 672 @Experimental systemApps()673 public Set<Package> systemApps() { 674 return systemApps(TestApis.users().instrumented()); 675 } 676 677 /** 678 * System apps installed on the given user. 679 */ 680 @Experimental systemApps(UserReference user)681 public Set<Package> systemApps(UserReference user) { 682 return installedForUser(user).stream() 683 .filter(Package::hasSystemFlag) 684 .collect(Collectors.toSet()); 685 } 686 687 /** 688 * Oem defined default dialer app. 689 */ 690 @Experimental oemDefaultDialerApp()691 public Package oemDefaultDialerApp() { 692 String defaultDialerPackage = TestApis.context().instrumentedContext().getString( 693 Resources.getSystem().getIdentifier("config_defaultDialer", "string", "android")); 694 return TestApis.packages().find(defaultDialerPackage); 695 } 696 697 /** 698 * Oem defined default sms app. 699 */ 700 @Experimental oemDefaultSmsApp()701 public Package oemDefaultSmsApp() { 702 String defaultSmsPackage = TestApis.context().instrumentedContext().getString( 703 Resources.getSystem().getIdentifier("config_defaultSms", "string", "android")); 704 return TestApis.packages().find(defaultSmsPackage); 705 } 706 707 @Experimental setVerifyAdbInstalls(boolean verify)708 public UndoableContext setVerifyAdbInstalls(boolean verify) { 709 boolean originalVerifyAdbInstalls = getVerifyAdbInstalls(); 710 711 if (originalVerifyAdbInstalls == verify) { 712 return UndoableContext.EMPTY; 713 } 714 715 TestApis.settings().global().putInt(PACKAGE_VERIFIER_INCLUDE_ADB, verify ? 1 : 0); 716 717 return new UndoableContext(() -> { 718 UndoableContext unused = setVerifyAdbInstalls(originalVerifyAdbInstalls); 719 }); 720 } 721 722 @Experimental getVerifyAdbInstalls()723 public boolean getVerifyAdbInstalls() { 724 return TestApis.settings().global().getInt(PACKAGE_VERIFIER_INCLUDE_ADB, 1) == 1; 725 } 726 727 /** 728 * Get the Launcher package. 729 */ 730 @Experimental launcher()731 public Package launcher() { 732 return find(TestApis.ui().device().getLauncherPackageName()); 733 } 734 735 /** See {@link PackageManager#queryIntentActivities(Intent, int)}. 736 * 737 * <p> Returns a list of {@link ResolveInfo} wrapped in {@link ResolveInfoWrapper}.*/ 738 @Experimental queryIntentActivities(Intent intent, int flags)739 public List<ResolveInfoWrapper> queryIntentActivities(Intent intent, int flags) { 740 return TestApis.context().instrumentedContext().getPackageManager() 741 .queryIntentActivities(intent, flags) 742 .stream().map(r -> new ResolveInfoWrapper(r.activityInfo, r.match)) 743 .collect(Collectors.toList()); 744 } 745 746 /** See {@link PackageManager#queryIntentActivities(Intent, int)}. 747 * 748 * <p> Returns a list of {@link ResolveInfo} wrapped in {@link ResolveInfoWrapper}.*/ 749 @Experimental queryIntentActivities(UserReference user, Intent intent, int flags)750 public List<ResolveInfoWrapper> queryIntentActivities(UserReference user, Intent intent, int flags) { 751 return TestApis.context().androidContextAsUser(user).getPackageManager() 752 .queryIntentActivities(intent, flags) 753 .stream().map(r -> new ResolveInfoWrapper(r.activityInfo, r.match)) 754 .collect(Collectors.toList()); 755 } 756 757 /** Dump the packages state. */ dump()758 public String dump() { 759 return ShellCommand.builder("dumpsys packages").validate((s) -> !s.isEmpty()) 760 .executeOrThrowNeneException("Error dumping packages state"); 761 } 762 763 } 764