• 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.FORCE_STOP_PACKAGES;
20 import static android.Manifest.permission.QUERY_ALL_PACKAGES;
21 import static android.content.pm.ApplicationInfo.FLAG_STOPPED;
22 import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
23 import static android.content.pm.PackageManager.GET_PERMISSIONS;
24 import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
25 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
26 import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS;
27 import static android.content.pm.PermissionInfo.PROTECTION_FLAG_DEVELOPMENT;
28 import static android.os.Build.VERSION_CODES.P;
29 import static android.os.Build.VERSION_CODES.S;
30 import static android.os.Process.myUid;
31 
32 import static com.android.bedstead.nene.permissions.CommonPermissions.CHANGE_COMPONENT_ENABLED_STATE;
33 import static com.android.bedstead.nene.permissions.CommonPermissions.INTERACT_ACROSS_USERS_FULL;
34 import static com.android.bedstead.nene.permissions.CommonPermissions.MANAGE_ROLE_HOLDERS;
35 
36 import static com.google.common.truth.Truth.assertWithMessage;
37 
38 import static org.junit.Assert.fail;
39 
40 import android.annotation.TargetApi;
41 import android.app.ActivityManager;
42 import android.app.role.RoleManager;
43 import android.content.Intent;
44 import android.content.IntentFilter;
45 import android.content.pm.ApplicationInfo;
46 import android.content.pm.CrossProfileApps;
47 import android.content.pm.PackageInfo;
48 import android.content.pm.PackageManager;
49 import android.content.pm.PermissionInfo;
50 import android.os.Build;
51 import android.os.UserHandle;
52 import android.util.Log;
53 
54 import androidx.annotation.Nullable;
55 
56 import com.android.bedstead.nene.TestApis;
57 import com.android.bedstead.nene.annotations.Experimental;
58 import com.android.bedstead.nene.appops.AppOps;
59 import com.android.bedstead.nene.devicepolicy.DeviceOwner;
60 import com.android.bedstead.nene.devicepolicy.ProfileOwner;
61 import com.android.bedstead.nene.exceptions.AdbException;
62 import com.android.bedstead.nene.exceptions.AdbParseException;
63 import com.android.bedstead.nene.exceptions.NeneException;
64 import com.android.bedstead.nene.permissions.PermissionContext;
65 import com.android.bedstead.nene.permissions.Permissions;
66 import com.android.bedstead.nene.roles.RoleContext;
67 import com.android.bedstead.nene.users.UserReference;
68 import com.android.bedstead.nene.utils.Poll;
69 import com.android.bedstead.nene.utils.Retry;
70 import com.android.bedstead.nene.utils.ShellCommand;
71 import com.android.bedstead.nene.utils.ShellCommandUtils;
72 import com.android.bedstead.nene.utils.Versions;
73 import com.android.compatibility.common.util.BlockingBroadcastReceiver;
74 import com.android.compatibility.common.util.BlockingCallback.DefaultBlockingCallback;
75 
76 import java.io.File;
77 import java.util.Arrays;
78 import java.util.HashSet;
79 import java.util.Objects;
80 import java.util.Set;
81 import java.util.function.Supplier;
82 import java.util.stream.Collectors;
83 
84 /**
85  * A representation of a package on device which may or may not exist.
86  */
87 public final class Package {
88     private static final String LOG_TAG = "PackageReference";
89     private static final int PIDS_PER_USER_ID = 100000;
90     private static final PackageManager sPackageManager =
91             TestApis.context().instrumentedContext().getPackageManager();
92     private static final RoleManager sRoleManager = TestApis.context().instrumentedContext()
93             .getSystemService(RoleManager.class);
94 
95     private final String mPackageName;
96 
97     /**
98      * Constructs a new {@link Package} from the provided {@code packageName}.
99      */
of(String packageName)100     public static Package of(String packageName) {
101         return new Package(packageName);
102     }
103 
Package(String packageName)104     Package(String packageName) {
105         mPackageName = packageName;
106     }
107 
108     /** Return the package's name. */
packageName()109     public String packageName() {
110         return mPackageName;
111     }
112 
113     /**
114      * Install the package on the given user.
115      *
116      * <p>If you wish to install a package which is not already installed on another user, see
117      * {@link Packages#install(UserReference, File)}.
118      */
installExisting(UserReference user)119     public Package installExisting(UserReference user) {
120         if (user == null) {
121             throw new NullPointerException();
122         }
123 
124         try {
125             // Expected output "Package X installed for user: Y"
126             ShellCommand.builderForUser(user, "cmd package install-existing")
127                     .addOperand(mPackageName)
128                     .validate(
129                             (output) -> output.contains("installed for user"))
130                     .execute();
131 
132             return this;
133         } catch (AdbException e) {
134             throw new NeneException("Could not install-existing package " + this, e);
135         }
136     }
137 
138     /**
139      * Install this package on the given user, using {@link #installExisting(UserReference)} if
140      * possible, otherwise installing fresh.
141      */
install(UserReference user, File apkFile)142     public Package install(UserReference user, File apkFile) {
143         if (exists()) {
144             return installExisting(user);
145         }
146 
147         return TestApis.packages().install(user, apkFile);
148     }
149 
150     /**
151      * Install this package on the given user, using {@link #installExisting(UserReference)} if
152      * possible, otherwise installing fresh.
153      */
install(UserReference user, Supplier<File> apkFile)154     public Package install(UserReference user, Supplier<File> apkFile) {
155         if (exists()) {
156             return installExisting(user);
157         }
158 
159         return TestApis.packages().install(user, apkFile.get());
160     }
161 
162     /**
163      * Install this package on the given user, using {@link #installExisting(UserReference)} if
164      * possible, otherwise installing fresh.
165      */
installBytes(UserReference user, byte[] apkFile)166     public Package installBytes(UserReference user, byte[] apkFile) {
167         if (exists()) {
168             return installExisting(user);
169         }
170 
171         return TestApis.packages().install(user, apkFile);
172     }
173 
174     /**
175      * Install this package on the given user, using {@link #installExisting(UserReference)} if
176      * possible, otherwise installing fresh.
177      */
installBytes(UserReference user, Supplier<byte[]> apkFile)178     public Package installBytes(UserReference user, Supplier<byte[]> apkFile) {
179         if (exists()) {
180             return installExisting(user);
181         }
182 
183         return TestApis.packages().install(user, apkFile.get());
184     }
185 
186     /**
187      * Uninstall the package for all users.
188      */
uninstallFromAllUsers()189     public Package uninstallFromAllUsers() {
190         for (UserReference user : installedOnUsers()) {
191             uninstall(user);
192         }
193 
194         return this;
195     }
196 
197     /**
198      * Uninstall the package for the given user.
199      *
200      * <p>If the package is not installed for the given user, nothing will happen.
201      */
uninstall(UserReference user)202     public Package uninstall(UserReference user) {
203         if (user == null) {
204             throw new NullPointerException();
205         }
206 
207         IntentFilter packageRemovedIntentFilter =
208                 new IntentFilter(Intent.ACTION_PACKAGE_REMOVED);
209         packageRemovedIntentFilter.addDataScheme("package");
210 
211         // This is outside of the try because we don't want to await if the package isn't installed
212         BlockingBroadcastReceiver broadcastReceiver = BlockingBroadcastReceiver.create(
213                 TestApis.context().androidContextAsUser(user),
214                 packageRemovedIntentFilter);
215 
216         try {
217 
218             boolean canWaitForBroadcast = false;
219             if (Versions.meetsMinimumSdkVersionRequirement(Build.VERSION_CODES.R)) {
220                 try (PermissionContext p = TestApis.permissions().withPermission(
221                         INTERACT_ACROSS_USERS_FULL)) {
222                     broadcastReceiver.register();
223                 }
224                 canWaitForBroadcast = true;
225             } else if (user.equals(TestApis.users().instrumented())) {
226                 broadcastReceiver.register();
227                 canWaitForBroadcast = true;
228             }
229 
230             String commandOutput = Poll.forValue(() -> {
231                 // Expected output "Success"
232                 return ShellCommand.builderForUser(user, "pm uninstall")
233                         .addOperand(mPackageName)
234                         .execute();
235             }).toMeet(output -> output.toUpperCase().startsWith("SUCCESS")
236                     || output.toUpperCase().contains("NOT INSTALLED FOR"))
237                     .terminalValue((output) -> {
238                         if (output.contains("DELETE_FAILED_DEVICE_POLICY_MANAGER")) {
239                             // A recently-removed device policy manager can't be removed - but won't
240                             // show as DPC
241 
242                             DeviceOwner deviceOwner = TestApis.devicePolicy().getDeviceOwner();
243                             if (deviceOwner != null && deviceOwner.pkg().equals(this)) {
244                                 // Terminal, can't remove actual DO
245                                 return true;
246                             }
247                             ProfileOwner profileOwner =
248                                     TestApis.devicePolicy().getProfileOwner(user);
249                             // Terminal, can't remove actual PO
250                             return profileOwner != null && profileOwner.pkg().equals(this);
251 
252                             // Not PO or DO, likely temporary failure
253                         }
254 
255                         return true;
256                     })
257                     .errorOnFail()
258                     .await();
259 
260             if (commandOutput.toUpperCase().startsWith("SUCCESS")) {
261                 if (canWaitForBroadcast) {
262                     broadcastReceiver.awaitForBroadcastOrFail();
263                 } else {
264                     try {
265                         // On versions prior to R - cross user installs can't block for broadcasts
266                         // so we have an arbitrary sleep
267                         Thread.sleep(10000);
268                     } catch (InterruptedException e) {
269                         Log.i(LOG_TAG, "Interrupted waiting for package uninstallation", e);
270                     }
271                 }
272             }
273             return this;
274         } catch (NeneException e) {
275             throw new NeneException("Could not uninstall package " + this, e);
276         } finally {
277             broadcastReceiver.unregisterQuietly();
278         }
279     }
280 
281     /**
282      * Enable this package for the given {@link UserReference}.
283      */
284     @Experimental
enable(UserReference user)285     public Package enable(UserReference user) {
286         try {
287             ShellCommand.builderForUser(user, "pm enable")
288                     .addOperand(mPackageName)
289                     .validate(o -> o.contains("new state"))
290                     .execute();
291         } catch (AdbException e) {
292             throw new NeneException("Error enabling package " + this + " for user " + user, e);
293         }
294         return this;
295     }
296 
297     /**
298      * Enable this package on the instrumented user.
299      */
300     @Experimental
enable()301     public Package enable() {
302         return enable(TestApis.users().instrumented());
303     }
304 
305     /**
306      * Disable this package for the given {@link UserReference}.
307      */
308     @Experimental
disable(UserReference user)309     public Package disable(UserReference user) {
310         try {
311             // TODO(279387509): "pm disable" is currently broken for packages - restore to normal
312             //  disable when fixed
313             ShellCommand.builderForUser(user, "pm disable-user")
314                     .addOperand(mPackageName)
315                     .validate(o -> o.contains("new state"))
316                     .execute();
317         } catch (AdbException e) {
318             throw new NeneException("Error disabling package " + this + " for user " + user, e);
319         }
320         return this;
321     }
322 
323     /**
324      * Disable this package on the instrumented user.
325      */
326     @Experimental
disable()327     public Package disable() {
328         return disable(TestApis.users().instrumented());
329     }
330 
331     /**
332      * Get a reference to the given {@code componentName} within this package.
333      *
334      * <p>This does not guarantee that the component exists.
335      */
336     @Experimental
component(String componentName)337     public ComponentReference component(String componentName) {
338         return new ComponentReference(this, componentName);
339     }
340 
341     /**
342      * Grant a permission for the package on the given user.
343      *
344      * <p>The package must be installed on the user, must request the given permission, and the
345      * permission must be a runtime permission.
346      */
grantPermission(UserReference user, String permission)347     public Package grantPermission(UserReference user, String permission) {
348         // There is no readable output upon failure so we need to check ourselves
349         checkCanGrantOrRevokePermission(user, permission);
350 
351         try {
352             ShellCommand.builderForUser(user, "pm grant")
353                     .addOperand(packageName())
354                     .addOperand(permission)
355                     .allowEmptyOutput(true)
356                     .validate(String::isEmpty)
357                     .execute();
358 
359             assertWithMessage("Error granting permission " + permission
360                     + " to package " + this + " on user " + user
361                     + ". Command appeared successful but not set.")
362                     .that(hasPermission(user, permission)).isTrue();
363 
364             return this;
365         } catch (AdbException e) {
366             throw new NeneException("Error granting permission " + permission + " to package "
367                     + this + " on user " + user, e);
368         }
369     }
370 
371     /** Grant the {@code permission} on the instrumented user. */
grantPermission(String permission)372     public Package grantPermission(String permission) {
373         return grantPermission(TestApis.users().instrumented(), permission);
374     }
375 
376     /** Deny the {@code permission} on the instrumented user. */
denyPermission(String permission)377     public Package denyPermission(String permission) {
378         return denyPermission(TestApis.users().instrumented(), permission);
379     }
380 
381     /**
382      * Deny a permission for the package on the given user.
383      *
384      * <p>The package must be installed on the user, must request the given permission, and the
385      * permission must be a runtime permission.
386      *
387      * <p>You can not deny permissions for the current package on the current user.
388      */
denyPermission(UserReference user, String permission)389     public Package denyPermission(UserReference user, String permission) {
390         if (!hasPermission(user, permission)) {
391             return this; // Already denied
392         }
393 
394         // There is no readable output upon failure so we need to check ourselves
395         checkCanGrantOrRevokePermission(user, permission);
396 
397         if (packageName().equals(TestApis.context().instrumentedContext().getPackageName())
398                 && user.equals(TestApis.users().instrumented())) {
399             throw new NeneException("Cannot deny permission from current package");
400         }
401 
402         try {
403             ShellCommand.builderForUser(user, "pm revoke")
404                     .addOperand(packageName())
405                     .addOperand(permission)
406                     .allowEmptyOutput(true)
407                     .validate(String::isEmpty)
408                     .execute();
409 
410             assertWithMessage("Error denying permission " + permission
411                     + " to package " + this + " on user " + user
412                     + ". Command appeared successful but not revoked.")
413                     .that(hasPermission(user, permission)).isFalse();
414 
415             return this;
416         } catch (AdbException e) {
417             throw new NeneException("Error denying permission " + permission + " to package "
418                     + this + " on user " + user, e);
419         }
420     }
421 
checkCanGrantOrRevokePermission(UserReference user, String permission)422     void checkCanGrantOrRevokePermission(UserReference user, String permission) {
423         if (!installedOnUser(user)) {
424             throw new NeneException("Attempting to grant " + permission + " to " + this
425                     + " on user " + user + ". But it is not installed");
426         }
427 
428         try {
429             PermissionInfo permissionInfo =
430                     sPackageManager.getPermissionInfo(permission, /* flags= */ 0);
431 
432             if (!protectionIsDangerous(permissionInfo.protectionLevel)
433                     && !protectionIsDevelopment(permissionInfo.protectionLevel)) {
434                 throw new NeneException("Cannot grant non-runtime permission "
435                         + permission + ", protection level is " + permissionInfo.protectionLevel);
436             }
437 
438             if (!requestedPermissions().contains(permission)) {
439                 throw new NeneException("Cannot grant permission "
440                         + permission + " which was not requested by package " + packageName());
441             }
442         } catch (PackageManager.NameNotFoundException e) {
443             throw new NeneException("Permission does not exist: " + permission);
444         }
445     }
446 
protectionIsDangerous(int protectionLevel)447     private boolean protectionIsDangerous(int protectionLevel) {
448         return (protectionLevel & PROTECTION_DANGEROUS) != 0;
449     }
450 
protectionIsDevelopment(int protectionLevel)451     private boolean protectionIsDevelopment(int protectionLevel) {
452         return (protectionLevel & PROTECTION_FLAG_DEVELOPMENT) != 0;
453     }
454 
455     /** Get running {@link ProcessReference} for this package on all users. */
456     @Experimental
runningProcesses()457     public Set<ProcessReference> runningProcesses() {
458         // TODO(scottjonathan): See if this can be remade using
459         //  ActivityManager#getRunningappProcesses
460         try {
461             return ShellCommand.builder("ps")
462                     .addOperand("-A")
463                     .addOperand("-n")
464                     .executeAndParseOutput(o -> parsePsOutput(o).stream()
465                             .filter(p -> p.mPackageName.equals(mPackageName))
466                             .map(p -> new ProcessReference(this, p.mPid, p.mUid,
467                                     TestApis.users().find(p.mUserId))))
468                     .collect(Collectors.toSet());
469         } catch (AdbException e) {
470             throw new NeneException("Error getting running processes ", e);
471         }
472     }
473 
parsePsOutput(String psOutput)474     private Set<ProcessInfo> parsePsOutput(String psOutput) {
475         return Arrays.stream(psOutput.split("\n"))
476                 .skip(1) // Skip the title line
477                 .map(s -> s.split("\\s+"))
478                 .map(m -> new ProcessInfo(
479                         m[8], Integer.parseInt(m[1]),
480                         Integer.parseInt(m[0]),
481                         Integer.parseInt(m[0]) / PIDS_PER_USER_ID))
482                 .collect(Collectors.toSet());
483     }
484 
485     /** Get the running {@link ProcessReference} for this package on the given user. */
486     @Experimental
487     @Nullable
runningProcess(UserReference user)488     public ProcessReference runningProcess(UserReference user) {
489         ProcessReference p = runningProcesses().stream().filter(
490                 i -> i.user().equals(user))
491                 .findAny()
492                 .orElse(null);
493         return p;
494     }
495 
496     /** Get the running {@link ProcessReference} for this package on the given user. */
497     @Experimental
498     @Nullable
runningProcess(UserHandle user)499     public ProcessReference runningProcess(UserHandle user) {
500         return runningProcess(TestApis.users().find(user));
501     }
502 
503     /** Get the running {@link ProcessReference} for this package on the instrumented user. */
504     @Experimental
505     @Nullable
runningProcess()506     public ProcessReference runningProcess() {
507         return runningProcess(TestApis.users().instrumented());
508     }
509 
510     /** {@code true} if the package is installed on the given user. */
installedOnUser(UserHandle userHandle)511     public boolean installedOnUser(UserHandle userHandle) {
512         return installedOnUser(TestApis.users().find(userHandle));
513     }
514 
515     /** {@code true} if the package is installed on the given user. */
installedOnUser(UserReference user)516     public boolean installedOnUser(UserReference user) {
517         return packageInfoForUser(user, /* flags= */ 0) != null;
518     }
519 
520     /** {@code true} if the package is installed on the instrumented user. */
installedOnUser()521     public boolean installedOnUser() {
522         return installedOnUser(TestApis.users().instrumented());
523     }
524 
525     /** {@code true} if the package on the given user has the given permission. */
hasPermission(UserReference user, String permission)526     public boolean hasPermission(UserReference user, String permission) {
527         return TestApis.context().androidContextAsUser(user).getPackageManager()
528                 .checkPermission(permission, mPackageName) == PERMISSION_GRANTED;
529     }
530 
531     /** {@code true} if the package on the given user has the given permission. */
hasPermission(UserHandle user, String permission)532     public boolean hasPermission(UserHandle user, String permission) {
533         return hasPermission(TestApis.users().find(user), permission);
534     }
535 
536     /** {@code true} if the package on the instrumented user has the given permission. */
hasPermission(String permission)537     public boolean hasPermission(String permission) {
538         return hasPermission(TestApis.users().instrumented(), permission);
539     }
540 
541     /** Get the permissions requested in the package's manifest. */
requestedPermissions()542     public Set<String> requestedPermissions() {
543         PackageInfo packageInfo = packageInfoFromAnyUser(GET_PERMISSIONS);
544 
545         if (packageInfo == null) {
546             if (TestApis.packages().instrumented().isInstantApp()) {
547                 Log.i(LOG_TAG, "Tried to get requestedPermissions for "
548                         + mPackageName + " but can't on instant apps");
549                 return new HashSet<>();
550             }
551             throw new NeneException("Error getting requestedPermissions, does not exist");
552         }
553 
554         if (packageInfo.requestedPermissions == null) {
555             return new HashSet<>();
556         }
557 
558         return new HashSet<>(Arrays.asList(packageInfo.requestedPermissions));
559     }
560 
561     @Nullable
packageInfoFromAnyUser(int flags)562     private PackageInfo packageInfoFromAnyUser(int flags) {
563         return TestApis.users().all().stream()
564                 .map(i -> packageInfoForUser(i, flags))
565                 .filter(Objects::nonNull)
566                 .findFirst()
567                 .orElse(null);
568     }
569 
570     @Nullable
packageInfoForUser(UserReference user, int flags)571     private PackageInfo packageInfoForUser(UserReference user, int flags) {
572         if (TestApis.packages().instrumented().isInstantApp()
573                 || !Versions.meetsMinimumSdkVersionRequirement(S)) {
574             // Can't call API's directly
575             return packageInfoForUserPreS(user, flags);
576         }
577 
578         if (user.equals(TestApis.users().instrumented())) {
579             try {
580                 return TestApis.context().instrumentedContext()
581                         .getPackageManager()
582                         .getPackageInfo(mPackageName, /* flags= */ flags);
583             } catch (PackageManager.NameNotFoundException e) {
584                 Log.e(LOG_TAG, "Could not find package " + this + " on user " + user, e);
585                 return null;
586             }
587         }
588 
589         if (Permissions.sIgnorePermissions.get()) {
590             try {
591                 return TestApis.context().androidContextAsUser(user)
592                         .getPackageManager()
593                         .getPackageInfo(mPackageName, /* flags= */ flags);
594             } catch (PackageManager.NameNotFoundException e) {
595                 return null;
596             }
597         } else {
598             try (PermissionContext p = TestApis.permissions().withPermission(
599                     INTERACT_ACROSS_USERS_FULL)) {
600                 return TestApis.context().androidContextAsUser(user)
601                         .getPackageManager()
602                         .getPackageInfo(mPackageName, /* flags= */ flags);
603             } catch (PackageManager.NameNotFoundException e) {
604                 return null;
605             }
606         }
607     }
608 
packageInfoForUserPreS(UserReference user, int flags)609     private PackageInfo packageInfoForUserPreS(UserReference user, int flags) {
610         AdbPackage pkg = Packages.parseDumpsys().mPackages.get(mPackageName);
611 
612         if (pkg == null) {
613             return null;
614         }
615 
616         if (!pkg.installedOnUsers().contains(user)) {
617             return null;
618         }
619 
620         PackageInfo packageInfo = new PackageInfo();
621         packageInfo.packageName = mPackageName;
622         packageInfo.requestedPermissions = pkg.requestedPermissions().toArray(new String[]{});
623 
624         return packageInfo;
625     }
626 
627     @Nullable
applicationInfoFromAnyUser(int flags)628     private ApplicationInfo applicationInfoFromAnyUser(int flags) {
629         return TestApis.users().all().stream()
630                 .map(i -> applicationInfoForUser(i, flags))
631                 .filter(Objects::nonNull)
632                 .findFirst()
633                 .orElse(null);
634     }
635 
636     @Nullable
applicationInfoForUser(UserReference user, int flags)637     private ApplicationInfo applicationInfoForUser(UserReference user, int flags) {
638         if (user.equals(TestApis.users().instrumented())) {
639             try {
640                 return TestApis.context().instrumentedContext()
641                         .getPackageManager()
642                         .getApplicationInfo(mPackageName, /* flags= */ flags);
643             } catch (PackageManager.NameNotFoundException e) {
644                 return null;
645             }
646         }
647 
648         if (!Versions.meetsMinimumSdkVersionRequirement(Build.VERSION_CODES.Q)) {
649             return applicationInfoForUserPreQ(user, flags);
650         }
651 
652         if (Permissions.sIgnorePermissions.get()) {
653             try {
654                 return TestApis.context().androidContextAsUser(user)
655                         .getPackageManager()
656                         .getApplicationInfo(mPackageName, /* flags= */ flags);
657             } catch (PackageManager.NameNotFoundException e) {
658                 return null;
659             }
660         } else {
661             try (PermissionContext p = TestApis.permissions().withPermission(
662                     INTERACT_ACROSS_USERS_FULL)) {
663                 return TestApis.context().androidContextAsUser(user)
664                         .getPackageManager()
665                         .getApplicationInfo(mPackageName, /* flags= */ flags);
666             } catch (PackageManager.NameNotFoundException e) {
667                 return null;
668             }
669         }
670     }
671 
applicationInfoForUserPreQ(UserReference user, int flags)672     private ApplicationInfo applicationInfoForUserPreQ(UserReference user, int flags) {
673         try {
674             String dumpsysOutput = ShellCommand.builder("dumpsys package").execute();
675 
676             AdbPackageParser.ParseResult r = Packages.sParser.parse(dumpsysOutput);
677             AdbPackage pkg = r.mPackages.get(mPackageName);
678 
679             if (pkg == null) {
680                 return null;
681             }
682 
683             ApplicationInfo applicationInfo = new ApplicationInfo();
684             applicationInfo.packageName = mPackageName;
685             applicationInfo.uid = -1; // TODO: Get the actual uid...
686 
687             return applicationInfo;
688         } catch (AdbException | AdbParseException e) {
689             throw new NeneException("Error getting package info pre Q", e);
690         }
691     }
692 
693     /**
694      * Get all users this package is installed on.
695      *
696      * <p>Note that this is an expensive operation - favor {@link #installedOnUser(UserReference)}
697      * when possible.
698      */
installedOnUsers()699     public Set<UserReference> installedOnUsers() {
700         return TestApis.users().all().stream()
701                 .filter(this::installedOnUser)
702                 .collect(Collectors.toSet());
703     }
704 
705     /**
706      * Force the running instance of the package to stop on the given user.
707      *
708      * <p>See {@link ActivityManager#forceStopPackage(String)}.
709      */
710     @Experimental
forceStop(UserReference user)711     public void forceStop(UserReference user) {
712         try (PermissionContext p = TestApis.permissions().withPermission(FORCE_STOP_PACKAGES)) {
713             ActivityManager userActivityManager =
714                     TestApis.context().androidContextAsUser(user)
715                             .getSystemService(ActivityManager.class);
716 
717             PackageManager userPackageManager =
718                     TestApis.context().androidContextAsUser(user).getPackageManager();
719             boolean shouldCheckPreviousProcess = runningProcess() != null;
720             // In most cases this should work first time, however if a user restriction has been
721             // recently removed we may need to retry
722 
723             int previousPid = shouldCheckPreviousProcess ? runningProcess().pid() : -1;
724 
725             Poll.forValue("Application flag", () -> {
726                 userActivityManager.forceStopPackage(mPackageName);
727 
728                 return userPackageManager.getPackageInfo(mPackageName,
729                             PackageManager.GET_META_DATA)
730                             .applicationInfo.flags;
731             }).toMeet(flag -> !shouldCheckPreviousProcess || (flag & FLAG_STOPPED) == FLAG_STOPPED
732                             ||  previousPid != runningProcess().pid())
733                     .errorOnFail("Expected application flags to contain FLAG_STOPPED ("
734                             + FLAG_STOPPED + ")")
735                     .await();
736         }
737     }
738 
739     /**
740      * Force the running instance of the package to stop on the instrumented user.
741      *
742      * <p>See {@link ActivityManager#forceStopPackage(String)}.
743      */
744     @Experimental
forceStop()745     public void forceStop() {
746         forceStop(TestApis.users().instrumented());
747     }
748 
749     /**
750      * Interact with AppOps on the instrumented user for the given package.
751      */
752     @Experimental
appOps()753     public AppOps appOps() {
754         return appOps(TestApis.users().instrumented());
755     }
756 
757     /**
758      * Interact with AppOps on the given user for the given package.
759      */
760     @Experimental
appOps(UserReference user)761     public AppOps appOps(UserReference user) {
762         return new AppOps(this, user);
763     }
764 
765     /**
766      * Get the UID of the package on the instrumented user.
767      */
768     @Experimental
uid()769     public int uid() {
770         return uid(TestApis.users().instrumented());
771     }
772 
773     /**
774      * Get the UID of the package on the given {@code user}.
775      */
776     @Experimental
uid(UserReference user)777     public int uid(UserReference user) {
778         if (user.equals(TestApis.users().instrumented())
779                 && this.equals(TestApis.packages().instrumented())) {
780             return myUid();
781         }
782 
783         ApplicationInfo applicationInfo = applicationInfoForUser(user, /* flags= */ 0);
784         if (applicationInfo == null) {
785             throw new IllegalStateException(
786                     "Trying to get uid for not installed package " + this + " on user " + user);
787         }
788 
789         return applicationInfo.uid;
790     }
791 
792     @Override
hashCode()793     public int hashCode() {
794         return mPackageName.hashCode();
795     }
796 
797     @Override
equals(Object obj)798     public boolean equals(Object obj) {
799         if (!(obj instanceof Package)) {
800             return false;
801         }
802 
803         Package other = (Package) obj;
804         return other.mPackageName.equals(mPackageName);
805     }
806 
807     @Override
toString()808     public String toString() {
809         StringBuilder stringBuilder = new StringBuilder("PackageReference{");
810         stringBuilder.append("packageName=" + mPackageName);
811         stringBuilder.append("}");
812         return stringBuilder.toString();
813     }
814 
815     /** {@code true} if the package exists on any user on the device as is not uninstalled. */
816     @TargetApi(S)
isInstalled()817     public boolean isInstalled() {
818         Versions.requireMinimumVersion(S);
819 
820         try (PermissionContext p = TestApis.permissions().withPermission(QUERY_ALL_PACKAGES)) {
821             return packageInfoFromAnyUser(0) != null;
822         }
823     }
824 
825     /** {@code true} if the package exists on the device. */
exists()826     public boolean exists() {
827         if (Versions.meetsMinimumSdkVersionRequirement(S)) {
828             try (PermissionContext p = TestApis.permissions().withPermission(QUERY_ALL_PACKAGES)) {
829                 return packageInfoFromAnyUser(MATCH_UNINSTALLED_PACKAGES) != null;
830             }
831         }
832 
833         return Packages.parseDumpsys().mPackages.containsKey(mPackageName);
834     }
835 
836     /** Get the targetSdkVersion for the package. */
837     @Experimental
targetSdkVersion()838     public int targetSdkVersion() {
839         return applicationInfoFromAnyUserOrError(/* flags= */ 0).targetSdkVersion;
840     }
841 
842     /**
843      * {@code true} if the package is installed in the device's system image.
844      */
845     @Experimental
hasSystemFlag()846     public boolean hasSystemFlag() {
847         return (applicationInfoFromAnyUserOrError(/* flags= */ 0).flags & FLAG_SYSTEM) > 0;
848     }
849 
850     @Experimental
isInstantApp()851     public boolean isInstantApp() {
852         return sPackageManager.isInstantApp(mPackageName);
853     }
854 
855     /** Get the AppComponentFactory for the package. */
856     @Experimental
857     @Nullable
858     @TargetApi(P)
appComponentFactory()859     public String appComponentFactory() {
860         return applicationInfoFromAnyUserOrError(/* flags= */ 0).appComponentFactory;
861     }
862 
applicationInfoFromAnyUserOrError(int flags)863     private ApplicationInfo applicationInfoFromAnyUserOrError(int flags) {
864         ApplicationInfo appInfo = applicationInfoFromAnyUser(flags);
865         if (appInfo == null) {
866             throw new NeneException("Package not installed: " + this);
867         }
868         return appInfo;
869     }
870 
871     /**
872      * Gets the shared user id of the package.
873      */
874     @Experimental
sharedUserId()875     public String sharedUserId() {
876         PackageInfo packageInfo = packageInfoFromAnyUser(/* flags= */ 0);
877 
878         if (packageInfo == null) {
879             throw new NeneException("Error getting sharedUserId, does not exist");
880         }
881 
882         return packageInfo.sharedUserId;
883     }
884 
885     /**
886      * See {@link PackageManager#setSyntheticAppDetailsActivityEnabled(String, boolean)}.
887      */
888     @Experimental
setSyntheticAppDetailsActivityEnabled(UserReference user, boolean enabled)889     public void setSyntheticAppDetailsActivityEnabled(UserReference user, boolean enabled) {
890         try (PermissionContext p = TestApis.permissions()
891                 .withPermission(CHANGE_COMPONENT_ENABLED_STATE)) {
892             TestApis.context().androidContextAsUser(user).getPackageManager()
893                     .setSyntheticAppDetailsActivityEnabled(packageName(), enabled);
894         }
895     }
896 
897     /**
898      * See {@link PackageManager#setSyntheticAppDetailsActivityEnabled(String, boolean)}.
899      */
900     @Experimental
setSyntheticAppDetailsActivityEnabled(boolean enabled)901     public void setSyntheticAppDetailsActivityEnabled(boolean enabled) {
902         setSyntheticAppDetailsActivityEnabled(TestApis.users().instrumented(), enabled);
903     }
904 
905     /**
906      * See {@link PackageManager#getSyntheticAppDetailsActivityEnabled(String)}.
907      */
908     @Experimental
syntheticAppDetailsActivityEnabled(UserReference user)909     public boolean syntheticAppDetailsActivityEnabled(UserReference user) {
910         return TestApis.context().androidContextAsUser(user).getPackageManager()
911                 .getSyntheticAppDetailsActivityEnabled(packageName());
912     }
913 
914     /**
915      * See {@link PackageManager#getSyntheticAppDetailsActivityEnabled(String)}.
916      */
917     @Experimental
syntheticAppDetailsActivityEnabled()918     public boolean syntheticAppDetailsActivityEnabled() {
919         return syntheticAppDetailsActivityEnabled(TestApis.users().instrumented());
920     }
921 
922     private static final class ProcessInfo {
923         final String mPackageName;
924         final int mPid;
925         final int mUid;
926         final int mUserId;
927 
ProcessInfo(String packageName, int pid, int uid, int userId)928         ProcessInfo(String packageName, int pid, int uid, int userId) {
929             if (packageName == null) {
930                 throw new NullPointerException();
931             }
932             mPackageName = packageName;
933             mPid = pid;
934             mUid = uid;
935             mUserId = userId;
936         }
937 
938         @Override
toString()939         public String toString() {
940             return "ProcessInfo{packageName=" + mPackageName + ", pid="
941                     + mPid + ", uid=" + mUid + ", userId=" + mUserId + "}";
942         }
943     }
944 
945     /**
946      * Set this package as filling the given role on the instrumented user.
947      */
948     @Experimental
setAsRoleHolder(String role)949     public RoleContext setAsRoleHolder(String role) {
950         return setAsRoleHolder(role, TestApis.users().instrumented());
951     }
952 
953     /**
954      * Set this package as filling the given role.
955      */
956     @Experimental
setAsRoleHolder(String role, UserReference user)957     public RoleContext setAsRoleHolder(String role, UserReference user) {
958         try (PermissionContext p = TestApis.permissions().withPermission(
959                 MANAGE_ROLE_HOLDERS, INTERACT_ACROSS_USERS_FULL)) {
960 
961             Retry.logic(() -> {
962                 TestApis.logcat().clear();
963                 DefaultBlockingCallback<Boolean> blockingCallback = new DefaultBlockingCallback<>();
964 
965                 sRoleManager.addRoleHolderAsUser(
966                         role,
967                         mPackageName,
968                         /* flags= */ 0,
969                         user.userHandle(),
970                         TestApis.context().instrumentedContext().getMainExecutor(),
971                         blockingCallback::triggerCallback);
972 
973                 boolean success = blockingCallback.await();
974                 if (!success) {
975                     fail("Could not set role holder of " + role + "." + " Relevant logcat: "
976                             + TestApis.logcat().dump((line) -> line.contains(role)));
977                 }
978                 if (!TestApis.roles().getRoleHoldersAsUser(role, user).contains(packageName())) {
979                     fail("addRoleHolderAsUser returned true but did not add role holder. "
980                             + "Relevant logcat: " + TestApis.logcat().dump(
981                                     (line) -> line.contains(role)));
982                 }
983             }).terminalException(e -> {
984                 // Terminal unless we see logcat output indicating it might be temporary
985                 var logcat = TestApis.logcat()
986                         .dump(l -> l.contains("Error calling onAddRoleHolder()"));
987                 if (!logcat.isEmpty()) {
988                     // On low end devices - this can happen when the broadcast queue is full
989                     try {
990                         Thread.sleep(10_000);
991                     } catch (InterruptedException ex) {
992                         return true;
993                     }
994 
995                     return false;
996                 }
997 
998                 return true;
999             }).runAndWrapException();
1000 
1001             return new RoleContext(role, this, user);
1002         }
1003     }
1004 
1005     /**
1006      * Remove this package from the given role on the instrumented user.
1007      */
1008     @Experimental
removeAsRoleHolder(String role)1009     public void removeAsRoleHolder(String role) {
1010         removeAsRoleHolder(role, TestApis.users().instrumented());
1011     }
1012 
1013     /**
1014      * Remove this package from the given role.
1015      */
1016     @Experimental
removeAsRoleHolder(String role, UserReference user)1017     public void removeAsRoleHolder(String role, UserReference user) {
1018         try (PermissionContext p = TestApis.permissions().withPermission(
1019                 MANAGE_ROLE_HOLDERS)) {
1020             Retry.logic(() -> {
1021                 TestApis.logcat().clear();
1022                 DefaultBlockingCallback<Boolean> blockingCallback = new DefaultBlockingCallback<>();
1023                 sRoleManager.removeRoleHolderAsUser(
1024                         role,
1025                         mPackageName,
1026                         /* flags= */ 0,
1027                         user.userHandle(),
1028                         TestApis.context().instrumentedContext().getMainExecutor(),
1029                         blockingCallback::triggerCallback);
1030                 TestApis.roles().setBypassingRoleQualification(false);
1031 
1032                 boolean success = blockingCallback.await();
1033                 if (!success) {
1034                     fail("Failed to clear the role holder of "
1035                             + role + ".");
1036                 }
1037                 if (TestApis.roles().getRoleHoldersAsUser(role, user).contains(packageName())) {
1038                     fail("removeRoleHolderAsUser returned true but did not remove role holder. "
1039                             + "Relevant logcat: " + TestApis.logcat().dump(
1040                                     (line) -> line.contains(role)));
1041                 }
1042             }).terminalException(e -> {
1043                 // Terminal unless we see logcat output indicating it might be temporary
1044                 var logcat = TestApis.logcat()
1045                         .dump(l -> l.contains("Error calling onRemoveRoleHolder()"));
1046                 if (!logcat.isEmpty()) {
1047                     // On low end devices - this can happen when the broadcast queue is full
1048                     try {
1049                         Thread.sleep(10_000);
1050                     } catch (InterruptedException ex) {
1051                         return true;
1052                     }
1053 
1054                     return false;
1055                 }
1056 
1057                 return true;
1058             }).runAndWrapException();
1059         }
1060     }
1061 
1062     /**
1063      * True if the given package on the instrumented user can have its ability to interact across
1064      * profiles configured by the user.
1065      */
1066     @Experimental
canConfigureInteractAcrossProfiles()1067     public boolean canConfigureInteractAcrossProfiles() {
1068         return canConfigureInteractAcrossProfiles(TestApis.users().instrumented());
1069     }
1070 
1071     /**
1072      * True if the given package can have its ability to interact across profiles configured
1073      * by the user.
1074      */
1075     @Experimental
canConfigureInteractAcrossProfiles(UserReference user)1076     public boolean canConfigureInteractAcrossProfiles(UserReference user) {
1077         return TestApis.context().androidContextAsUser(user)
1078                 .getSystemService(CrossProfileApps.class)
1079                 .canConfigureInteractAcrossProfiles(packageName());
1080     }
1081 
1082     /**
1083      * Enable or disable this package from using @TestApis.
1084      */
1085     @Experimental
setAllowTestApiAccess(boolean allowed)1086     public void setAllowTestApiAccess(boolean allowed) {
1087         ShellCommand.builder("am compat")
1088                 .addOperand(allowed ? "enable" : "disable")
1089                 .addOperand("ALLOW_TEST_API_ACCESS")
1090                 .addOperand(packageName())
1091                 .validate(s -> s.startsWith(allowed ? "Enabled change" : "Disabled change"))
1092                 .executeOrThrowNeneException(
1093                         "Error allowing/disallowing test api access for " + this);
1094     }
1095 
1096     /**
1097      * True if the given package is suspended in the given user.
1098      */
1099     @Experimental
isSuspended(UserReference user)1100     public boolean isSuspended(UserReference user) {
1101         try (PermissionContext p =
1102                      TestApis.permissions().withPermission(INTERACT_ACROSS_USERS_FULL)) {
1103             return TestApis.context().androidContextAsUser(user).getPackageManager()
1104                     .isPackageSuspended(mPackageName);
1105         } catch (PackageManager.NameNotFoundException e) {
1106             throw new NeneException("Package " + mPackageName + " not found for user " + user);
1107         }
1108     }
1109 
1110     /**
1111      * Get the app standby bucket of the package.
1112      */
1113     @Experimental
getAppStandbyBucket()1114     public int getAppStandbyBucket() {
1115         return getAppStandbyBucket(TestApis.users().instrumented());
1116     }
1117 
1118     /**
1119      * Get the app standby bucket of the package.
1120      */
1121     @Experimental
getAppStandbyBucket(UserReference user)1122     public int getAppStandbyBucket(UserReference user) {
1123         try {
1124             return ShellCommand.builderForUser(user, "am get-standby-bucket")
1125                 .addOperand(mPackageName)
1126                 .executeAndParseOutput(o -> Integer.parseInt(o.trim()));
1127         } catch (AdbException e) {
1128             throw new NeneException("Could not get app standby bucket " + this, e);
1129         }
1130     }
1131 
1132     /** Approves all links for an auto verifiable app */
1133     @Experimental
setAppLinksToAllApproved()1134     public void setAppLinksToAllApproved() {
1135         try {
1136             ShellCommand.builder("pm set-app-links")
1137                     .addOption("--package", this.mPackageName)
1138                     .addOperand(2) // 2 = STATE_APPROVED
1139                     .addOperand("all")
1140                     .execute();
1141         } catch (AdbException e) {
1142             throw new NeneException("Error verifying links ", e);
1143         }
1144     }
1145 
1146     /** Checks if the current package is a role holder for the given role*/
1147     @Experimental
isRoleHolder(String role)1148     public boolean isRoleHolder(String role) {
1149         return TestApis.roles().getRoleHolders(role).contains(this.mPackageName);
1150     }
1151 
1152     @Experimental
clearStorage()1153     public void clearStorage() {
1154         ShellCommand.builder("pm clear")
1155                 .addOperand(mPackageName)
1156                 .validate(ShellCommandUtils::startsWithSuccess)
1157                 .executeOrThrowNeneException("Error clearing storage for " + this);
1158     }
1159 }
1160