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