• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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