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