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