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