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