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.permissions;
18 
19 import static android.content.pm.PackageManager.PERMISSION_DENIED;
20 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
21 
22 import android.app.AppOpsManager;
23 import android.content.Context;
24 import android.content.pm.PackageManager;
25 import android.content.pm.PermissionInfo;
26 import android.cts.testapisreflection.TestApisReflectionKt;
27 import android.os.Build;
28 import android.util.Log;
29 
30 import com.android.bedstead.nene.TestApis;
31 import com.android.bedstead.nene.annotations.Experimental;
32 import com.android.bedstead.nene.appops.AppOpsMode;
33 import com.android.bedstead.nene.exceptions.NeneException;
34 import com.android.bedstead.nene.packages.Package;
35 import com.android.bedstead.nene.users.UserReference;
36 import com.android.bedstead.nene.utils.FailureDumper;
37 import com.android.bedstead.nene.utils.ShellCommand;
38 import com.android.bedstead.nene.utils.ShellCommandUtils;
39 import com.android.bedstead.nene.utils.Tags;
40 import com.android.bedstead.nene.utils.UndoableContext;
41 import com.android.bedstead.nene.utils.Versions;
42 
43 import com.google.common.collect.ImmutableSet;
44 
45 import java.util.ArrayList;
46 import java.util.Arrays;
47 import java.util.Collection;
48 import java.util.Collections;
49 import java.util.HashSet;
50 import java.util.List;
51 import java.util.Set;
52 import java.util.concurrent.atomic.AtomicBoolean;
53 import java.util.stream.Collectors;
54 
55 /** Permission manager for tests. */
56 public final class Permissions {
57 
58     public static final AtomicBoolean sIgnorePermissions = new AtomicBoolean(false);
59     private static final String LOG_TAG = "Permissions";
60     private static final Context sContext = TestApis.context().instrumentedContext();
61     private static final PackageManager sPackageManager = sContext.getPackageManager();
62     private static final AppOpsManager sAppOpsManager =
63             TestApis.context().instrumentedContext().getSystemService(AppOpsManager.class);
64     private static final Package sInstrumentedPackage =
65             TestApis.packages().instrumented();
66     private static final UserReference sUser = TestApis.users().instrumented();
67     private static final Package sShellPackage =
68             TestApis.packages().find("com.android.shell");
69     private static final boolean SUPPORTS_ADOPT_SHELL_PERMISSIONS =
70             Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q;
71 
72     /**
73      * Permissions which cannot be given to shell.
74      *
75      * <p>Each entry must include a comment with the reason it cannot be added.
76      */
77     private static final ImmutableSet EXEMPT_SHELL_PERMISSIONS = ImmutableSet.of(
78 
79     );
80 
81     public static final Permissions sInstance = new Permissions();
82 
83     private final List<PermissionContextImpl> mPermissionContexts =
84             Collections.synchronizedList(new ArrayList<>());
85     private final Set<String> mShellPermissions;
86     private final Set<String> mInstrumentedRequestedPermissions;
87 
ignoringPermissions()88     public static UndoableContext ignoringPermissions() {
89         boolean original = Permissions.sIgnorePermissions.get();
90         Permissions.sIgnorePermissions.set(true);
91 
92         if (SUPPORTS_ADOPT_SHELL_PERMISSIONS) {
93             adoptShellPermissionIdentity();
94         }
95 
96         return new UndoableContext(() -> {
97             if (SUPPORTS_ADOPT_SHELL_PERMISSIONS) {
98                 dropShellPermissionIdentity();
99             }
100             Permissions.sIgnorePermissions.set(original);
101         });
102     }
103 
104     private Permissions() {
105         // Packages requires using INTERACT_ACROSS_USERS_FULL but we don't want it to rely on
106         // Permissions or it'll recurse forever - so we disable permission checks and just use
107         // shell permission adoption directly while initialising
108         try (UndoableContext c = ignoringPermissions()) {
109             if (SUPPORTS_ADOPT_SHELL_PERMISSIONS) {
110                 mShellPermissions = sShellPackage.requestedPermissions();
111             } else {
112                 mShellPermissions = new HashSet<>();
113             }
114             mInstrumentedRequestedPermissions = sInstrumentedPackage.requestedPermissions();
115         }
116     }
117 
118     /**
119      * Enter a {@link PermissionContext} where the given permissions are granted.
120      *
121      * <p>If the permissions cannot be granted, and are not already granted, an exception will be
122      * thrown.
123      *
124      * <p>Recommended usage:
125      * {@code
126      *
127      * try (PermissionContext p = mTestApis.permissions().withPermission(PERMISSION1, PERMISSION2) {
128      * // Code which needs the permissions goes here
129      * }
130      * }
131      */
132     public PermissionContextImpl withPermission(String... permissions) {
133         PermissionContextImpl permissionContext = new PermissionContextImpl(this);
134         mPermissionContexts.add(permissionContext);
135 
136         PermissionContextImpl unused = permissionContext.withPermission(permissions);
137 
138         return permissionContext;
139     }
140 
141     /**
142      * Enter a {@link PermissionContext} where the given permissions are granted only when running
143      * on the given version or above.
144      *
145      * <p>If the permissions cannot be granted, and are not already granted, an exception will be
146      * thrown.
147      *
148      * <p>If the version does not match, the permission context will not change.
149      */
150     public PermissionContextImpl withPermissionOnVersionAtLeast(
151             int minSdkVersion, String... permissions) {
152         PermissionContextImpl permissionContext = new PermissionContextImpl(this);
153         mPermissionContexts.add(permissionContext);
154 
155         PermissionContextImpl unused =
156                 permissionContext.withPermissionOnVersionAtLeast(minSdkVersion, permissions);
157 
158         return permissionContext;
159     }
160 
161     /**
162      * Enter a {@link PermissionContext} where the given permissions are granted only when running
163      * on the given version or below.
164      *
165      * <p>If the permissions cannot be granted, and are not already granted, an exception will be
166      * thrown.
167      *
168      * <p>If the version does not match, the permission context will not change.
169      */
170     public PermissionContextImpl withPermissionOnVersionAtMost(
171             int maxSdkVersion, String... permissions) {
172         PermissionContextImpl permissionContext = new PermissionContextImpl(this);
173         mPermissionContexts.add(permissionContext);
174 
175         PermissionContextImpl unused =
176                 permissionContext.withPermissionOnVersionAtMost(maxSdkVersion, permissions);
177 
178         return permissionContext;
179     }
180 
181     /**
182      * Enter a {@link PermissionContext} where the given permissions are granted only when running
183      * on the range of versions given (inclusive).
184      *
185      * <p>If the permissions cannot be granted, and are not already granted, an exception will be
186      * thrown.
187      *
188      * <p>If the version does not match, the permission context will not change.
189      */
190     public PermissionContextImpl withPermissionOnVersionBetween(
191             int minSdkVersion, int maxSdkVersion, String... permissions) {
192         PermissionContextImpl permissionContext = new PermissionContextImpl(this);
193         mPermissionContexts.add(permissionContext);
194 
195         PermissionContextImpl unused =
196                 permissionContext.withPermissionOnVersionBetween(minSdkVersion, maxSdkVersion,
197                         permissions);
198 
199         return permissionContext;
200     }
201 
202     /**
203      * Enter a {@link PermissionContext} where the given permissions are granted only when running
204      * on the given version.
205      *
206      * <p>If the permissions cannot be granted, and are not already granted, an exception will be
207      * thrown.
208      *
209      * <p>If the version does not match, the permission context will not change.
210      */
211     public PermissionContextImpl withPermissionOnVersion(int sdkVersion, String... permissions) {
212         PermissionContextImpl permissionContext = new PermissionContextImpl(this);
213         mPermissionContexts.add(permissionContext);
214 
215         PermissionContextImpl unused =
216                 permissionContext.withPermissionOnVersion(sdkVersion, permissions);
217 
218         return permissionContext;
219     }
220 
221     /**
222      * Enter a {@link PermissionContext} where the given appOps are granted.
223      *
224      * <p>If the appOps cannot be granted, and are not already granted, an exception will be
225      * thrown.
226      *
227      * <p>Recommended usage:
228      * {@code
229      *
230      * try (PermissionContext p = mTestApis.permissions().withAppOps(APP_OP1, APP_OP2) {
231      * // Code which needs the app ops goes here
232      * }
233      * }
234      */
235     public PermissionContextImpl withAppOp(String... appOps) {
236         PermissionContextImpl permissionContext = new PermissionContextImpl(this);
237         mPermissionContexts.add(permissionContext);
238 
239         PermissionContextImpl unused = permissionContext.withAppOp(appOps);
240 
241         return permissionContext;
242     }
243 
244     /**
245      * Enter a {@link PermissionContext} where the given appOps are granted.
246      *
247      * <p>If the appOps cannot be granted, and are not already granted, an exception will be
248      * thrown.
249      *
250      * <p>Recommended usage:
251      * {@code
252      *
253      * try (PermissionContext p = mTestApis.permissions().withAppOps(APP_OP1, APP_OP2) {
254      * // Code which needs the app ops goes here
255      * }
256      * }
257      *
258      * <p>If the version does not match the appOp will not be granted.
259      */
260     public PermissionContextImpl withAppOpOnVersion(int sdkVersion, String... appOps) {
261         PermissionContextImpl permissionContext = new PermissionContextImpl(this);
262         mPermissionContexts.add(permissionContext);
263 
264         PermissionContextImpl unused = permissionContext.withAppOpOnVersion(sdkVersion, appOps);
265 
266         return permissionContext;
267     }
268 
269     /**
270      * Enter a {@link PermissionContext} where the given appOps are granted.
271      *
272      * <p>If the appOps cannot be granted, and are not already granted, an exception will be
273      * thrown.
274      *
275      * <p>Recommended usage:
276      * {@code
277      *
278      * try (PermissionContext p = mTestApis.permissions().withAppOps(APP_OP1, APP_OP2) {
279      * // Code which needs the app ops goes here
280      * }
281      * }
282      *
283      * <p>If the version does not match the appOp will not be granted.
284      */
285     public PermissionContextImpl withAppOpOnVersionAtLeast(int sdkVersion, String... appOps) {
286         PermissionContextImpl permissionContext = new PermissionContextImpl(this);
287         mPermissionContexts.add(permissionContext);
288 
289         PermissionContextImpl unused = permissionContext.withAppOpOnVersionAtLeast(sdkVersion,
290                 appOps);
291 
292         return permissionContext;
293     }
294 
295     /**
296      * Enter a {@link PermissionContext} where the given appOps are granted.
297      *
298      * <p>If the appOps cannot be granted, and are not already granted, an exception will be
299      * thrown.
300      *
301      * <p>Recommended usage:
302      * {@code
303      *
304      * try (PermissionContext p = mTestApis.permissions().withAppOps(APP_OP1, APP_OP2) {
305      * // Code which needs the app ops goes here
306      * }
307      * }
308      *
309      * <p>If the version does not match the appOp will not be granted.
310      */
311     public PermissionContextImpl withAppOpOnVersionAtMost(int sdkVersion, String... appOps) {
312         PermissionContextImpl permissionContext = new PermissionContextImpl(this);
313         mPermissionContexts.add(permissionContext);
314 
315         PermissionContextImpl unused = permissionContext.withAppOpOnVersionAtMost(sdkVersion,
316                 appOps);
317 
318         return permissionContext;
319     }
320 
321     /**
322      * Enter a {@link PermissionContext} where the given appOps are granted.
323      *
324      * <p>If the appOps cannot be granted, and are not already granted, an exception will be
325      * thrown.
326      *
327      * <p>Recommended usage:
328      * {@code
329      *
330      * try (PermissionContext p = mTestApis.permissions().withAppOps(APP_OP1, APP_OP2) {
331      * // Code which needs the app ops goes here
332      * }
333      * }
334      *
335      * <p>If the version does not match the appOp will not be granted.
336      */
337     public PermissionContextImpl withAppOpOnVersionBetween(
338             int minSdkVersion, int maxSdkVersion, String... appOps) {
339         PermissionContextImpl permissionContext = new PermissionContextImpl(this);
340         mPermissionContexts.add(permissionContext);
341 
342         PermissionContextImpl unused = permissionContext.withAppOpOnVersionBetween(minSdkVersion,
343                 maxSdkVersion, appOps);
344 
345         return permissionContext;
346     }
347 
348     /**
349      * Enter a {@link PermissionContext} where the given permissions are not granted.
350      *
351      * <p>If the permissions cannot be denied, and are not already denied, an exception will be
352      * thrown.
353      *
354      * <p>Recommended usage:
355      * {@code
356      *
357      * try (PermissionContext p =
358      * mTestApis.permissions().withoutPermission(PERMISSION1, PERMISSION2) {
359      * // Code which needs the permissions to be denied goes here
360      * }
361      */
362     public PermissionContextImpl withoutPermission(String... permissions) {
363         PermissionContextImpl permissionContext = new PermissionContextImpl(this);
364         mPermissionContexts.add(permissionContext);
365 
366         PermissionContextImpl unused = permissionContext.withoutPermission(permissions);
367 
368         return permissionContext;
369     }
370 
371     /**
372      * Enter a {@link PermissionContext} where the given appOps are not granted.
373      *
374      * <p>If the appOps cannot be denied, and are not already denied, an exception will be
375      * thrown.
376      *
377      * <p>Recommended usage:
378      * {@code
379      *
380      * try (PermissionContext p =
381      * mTestApis.permissions().withoutappOp(APP_OP1, APP_OP2) {
382      * // Code which needs the appOp to be denied goes here
383      * }
384      * }
385      */
386     public PermissionContextImpl withoutAppOp(String... appOps) {
387         PermissionContextImpl permissionContext = new PermissionContextImpl(this);
388         mPermissionContexts.add(permissionContext);
389 
390         PermissionContextImpl unused = permissionContext.withoutAppOp(appOps);
391 
392         return permissionContext;
393     }
394 
395     void undoPermission(PermissionContextImpl permissionContext) {
396         boolean unused = mPermissionContexts.remove(permissionContext);
397         applyPermissions(/* removedPermissionContext = */ permissionContext);
398     }
399 
400     void applyPermissions() {
401         applyPermissions(null);
402     }
403 
404     private void applyPermissions(PermissionContextImpl removedPermissionContext) {
405         if (sIgnorePermissions.get()) {
406             return;
407         }
408 
409         Set<String> grantedPermissions = new HashSet<>();
410         Set<String> deniedPermissions = new HashSet<>();
411         Set<String> grantedAppOps = new HashSet<>();
412         Set<String> deniedAppOps = new HashSet<>();
413 
414         synchronized (mPermissionContexts) {
415             for (PermissionContextImpl permissionContext : mPermissionContexts) {
416                 for (String permission : permissionContext.grantedPermissions()) {
417                     grantedPermissions.add(permission);
418                     deniedPermissions.remove(permission);
419                 }
420 
421                 for (String permission : permissionContext.deniedPermissions()) {
422                     grantedPermissions.remove(permission);
423                     deniedPermissions.add(permission);
424                 }
425 
426                 for (String appOp : permissionContext.grantedAppOps()) {
427                     grantedAppOps.add(appOp);
428                     deniedAppOps.remove(appOp);
429                 }
430 
431                 for (String appOp : permissionContext.deniedAppOps()) {
432                     grantedAppOps.remove(appOp);
433                     deniedAppOps.add(appOp);
434                 }
435             }
436         }
437 
438         setPermissionState(
439                 TestApis.packages().instrumented(),
440                 TestApis.users().instrumented(),
441                 grantedPermissions,
442                 deniedPermissions);
443         Package appOpPackage = hasAdoptedShellPermissionIdentity ? sShellPackage : sInstrumentedPackage;
444         setAppOpState(
445                 appOpPackage,
446                 TestApis.users().instrumented(),
447                 grantedAppOps,
448                 deniedAppOps
449         );
450 
451         if (removedPermissionContext != null) {
452             removedPermissionContext.grantedAppOps().stream().filter(
453                     (i) -> !grantedAppOps.contains(i) && !deniedAppOps.contains(i))
454                     .forEach(i -> appOpPackage.appOps().set(i, AppOpsMode.DEFAULT));
455             removedPermissionContext.deniedAppOps().stream().filter(
456                     (i) -> !grantedAppOps.contains(i) && !deniedAppOps.contains(i))
457                     .forEach(i -> appOpPackage.appOps().set(i, AppOpsMode.DEFAULT));
458         }
459 
460     }
461 
462     /**
463      * Throw an exception including permission contextual information.
464      */
465     public void throwPermissionException(
466             String message, String permission) {
467         String protectionLevel = "Permission not found";
468         try {
469             protectionLevel = Integer.toString(sPackageManager.getPermissionInfo(
470                     permission, /* flags= */ 0).protectionLevel);
471         } catch (PackageManager.NameNotFoundException e) {
472             Log.e(LOG_TAG, "Permission not found", e);
473         }
474 
475 
476 
477         try (UndoableContext c = ignoringPermissions()){
478             throw new NeneException(
479                     message
480                             + "\n\n"
481                             + "If this is a new test. Consider moving it to a root-enabled test"
482                             + " suite and adding @RequireRootInstrumentation to the method. This"
483                             + " enables arbitrary use of permissions.\n\n"
484                             + "Running On User: "
485                             + sUser
486                             + "\nPermission: "
487                             + permission
488                             + "\nPermission protection level: "
489                             + protectionLevel
490                             + "\nPermission state: "
491                             + sContext.checkSelfPermission(permission)
492                             + "\nInstrumented Package: "
493                             + sInstrumentedPackage.packageName()
494                             + "\n\nRequested Permissions:\n"
495                             + sInstrumentedPackage.requestedPermissions()
496                             + "\n\nCan adopt shell permissions: "
497                             + SUPPORTS_ADOPT_SHELL_PERMISSIONS
498                             + "\nShell permissions:"
499                             + mShellPermissions
500                             + "\nExempt Shell permissions: "
501                             + EXEMPT_SHELL_PERMISSIONS);
502         }
503     }
504 
505     void clearPermissions() {
506         mPermissionContexts.clear();
507         applyPermissions();
508     }
509 
510     /**
511      * Returns all of the permissions which can be adopted.
512      */
513     public Set<String> adoptablePermissions() {
514         return mShellPermissions;
515     }
516 
517     /**
518      * Returns all of the permissions which are currently able to be used.
519      */
520     public Set<String> usablePermissions() {
521         Set<String> usablePermissions = new HashSet<>();
522         usablePermissions.addAll(mShellPermissions);
523         usablePermissions.addAll(mInstrumentedRequestedPermissions);
524         return usablePermissions;
525     }
526 
527     private void removePermissionContextsUntilCanApplyPermissions() {
528         boolean appliedPermissions = false;
529         while (!mPermissionContexts.isEmpty() && !appliedPermissions) {
530             try {
531                 mPermissionContexts.remove(mPermissionContexts.size() - 1);
532                 applyPermissions();
533                 appliedPermissions = true;
534             } catch (NeneException e) {
535                 // Suppress NeneException here as we may get a few as we pop through the stack
536             }
537         }
538     }
539 
540     private boolean canGrantPermission(String permission) {
541         try {
542             PermissionInfo p = sPackageManager.getPermissionInfo(permission, /* flags= */ 0);
543             if ((p.protectionLevel & PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) > 0) {
544                 return true;
545             }
546             return (p.protectionLevel & PermissionInfo.PROTECTION_DANGEROUS) > 0;
547         } catch (PackageManager.NameNotFoundException e) {
548             return false;
549         }
550     }
551 
552     /** True if the current process has the given permission. */
553     public boolean hasPermission(String permission) {
554         return sContext.checkSelfPermission(permission) == PERMISSION_GRANTED;
555     }
556 
557     /**
558      * True if the current process has the given appOp set to ALLOWED.
559      *
560      * <p>This accounts for shell identity being adopted (in which case it will check the appop
561      * status of the shell identity).
562      */
563     public boolean hasAppOpAllowed(String appOp) {
564         Package appOpPackage = sInstrumentedPackage;
565         if (hasAdoptedShellPermissionIdentity) {
566             // We care about the shell package
567             appOpPackage = sShellPackage;
568         }
569 
570         return appOpPackage.appOps().get(appOp) == AppOpsMode.ALLOWED;
571     }
572 
573     /**
574      * Sets a permission state for a given package on a given user.
575      *
576      * <p>Generally tests should not use this method directly. They should instead used the {@link
577      * #withPermission} and {@link #withoutPermission} methods.
578      *
579      * <p>When this is used while executing a test which uses the RequireRootInstrumentation
580      * annotation, and using Android 15+, it will have access to all permissions for all apps.
581      *
582      * <p>Otherwise, when applying to the instrumented package, shell permission adoption will be
583      * used.
584      *
585      * <p>Otherwise, if the permission is able to be granted/denied by ADB then that will be done.
586      *
587      * <p>Otherwise an error will be thrown.
588      */
589     public void setPermissionState(
590             Package pkg,
591             UserReference user,
592             Collection<String> permissionsToGrant,
593             Collection<String> permissionsToDeny) {
594         FailureDumper.Companion.getFailureDumpers().add(
595                 "com.android.bedstead.permissions.PermissionsAnnotationExecutor");
596         // TODO: replace with dependency on bedstead-root when properly modularised
597         if (Tags.hasTag("root-instrumentation")
598                 && Versions.meetsMinimumSdkVersionRequirement(Versions.V)) {
599              // We must reset as it may have been set previously
600             resetRootPermissionState(pkg, user);
601 
602             for (String grantedPermission : permissionsToGrant) {
603                 forceRootPermissionState(pkg, user, grantedPermission, true);
604             }
605             for (String deniedPermission : permissionsToDeny) {
606                 forceRootPermissionState(pkg, user, deniedPermission, false);
607             }
608 
609             return;
610         }
611 
612         setPermissionStateToPackageWithoutRoot(pkg, user, permissionsToGrant, permissionsToDeny);
613     }
614 
615     /**
616      * Sets an appOp state for a given package on a given user.
617      *
618      * Generally tests should not use this method directly. They should instead used the
619      * {@link #withAppOp} and {@link #withoutAppOp} methods.
620      *
621      * Note that if shell permission identity is adopted, then the app op state will not be queried
622      * for the package - and the shell package should have its app op state set instead.
623      */
624     public void setAppOpState(Package pkg, UserReference user, Collection<String> grantedAppOps, Collection<String> deniedAppOps) {
625         FailureDumper.Companion.getFailureDumpers().add(
626                 "com.android.bedstead.permissions.PermissionsAnnotationExecutor");
627         // Filter so we get just the appOps which require a state that they are not currently in
628         Set<String> filteredGrantedAppOps = grantedAppOps.stream()
629                 .filter(o -> pkg.appOps().get(o) != AppOpsMode.ALLOWED)
630                 .collect(Collectors.toSet());
631         Set<String> filteredDeniedAppOps = deniedAppOps.stream()
632                 .filter(o -> pkg.appOps().get(o) != AppOpsMode.IGNORED)
633                 .collect(Collectors.toSet());
634 
635         if (!filteredGrantedAppOps.isEmpty() || !filteredDeniedAppOps.isEmpty()) {
636             // We need MANAGE_APP_OPS_MODES to change app op permissions - but don't want to
637             // infinite loop so won't use .appOps().set()
638             Set<String> previousAdoptedShellPermissions =
639                     TestApisReflectionKt.getAdoptedShellPermissions(
640                             ShellCommandUtils.uiAutomation());
641             adoptShellPermissionIdentity(CommonPermissions.MANAGE_APP_OPS_MODES);
642             for (String appOp : filteredGrantedAppOps) {
643                 sAppOpsManager.setMode(appOp, pkg.uid(sUser),
644                         pkg.packageName(), AppOpsMode.ALLOWED.value());
645             }
646             for (String appOp : filteredDeniedAppOps) {
647                 sAppOpsManager.setMode(appOp, pkg.uid(sUser),
648                         pkg.packageName(), AppOpsMode.IGNORED.value());
649             }
650 
651             adoptShellPermissionIdentity(previousAdoptedShellPermissions);
652         }
653     }
654 
655     private void setPermissionStateToPackageWithoutAdoption(Package pkg, UserReference user, Collection<String> permissionsToGrant, Collection<String> permissionsToDeny) {
656         for (String permission : permissionsToGrant) {
657             if (canGrantPermission(permission)) {
658                 pkg.grantPermission(user, permission);
659             } else {
660                 removePermissionContextsUntilCanApplyPermissions();
661                 throwPermissionException(
662                         "Requires granting permission " + permission + " but cannot.", permission);
663             }
664         }
665 
666         for (String permission : permissionsToDeny) {
667             if (pkg.equals(TestApis.packages().instrumented()) && user.equals(TestApis.users().instrumented())) {
668                 // We can't deny permissions from ourselves or it'll kill the process
669                 removePermissionContextsUntilCanApplyPermissions();
670                 throwPermissionException(
671                         "Requires denying permission " + permission + " but cannot.", permission);
672             } else {
673                 pkg.denyPermission(user, permission);
674             }
675         }
676     }
677 
678     private void setPermissionStateToPackageWithoutRoot(Package pkg, UserReference user, Collection<String> permissionsToGrant, Collection<String> permissionsToDeny) {
679         if (!pkg.equals(TestApis.packages().instrumented()) || !SUPPORTS_ADOPT_SHELL_PERMISSIONS) {
680             // We can't adopt...
681             setPermissionStateToPackageWithoutAdoption(pkg, user, permissionsToGrant, permissionsToDeny);
682             return;
683         }
684 
685         if (TestApis.packages().instrumented().isInstantApp()) {
686             // Instant Apps aren't able to know the permissions of shell so we can't know if we can
687             // adopt it - we'll assume we can adopt and log
688             Log.i(LOG_TAG,
689                     "Adopting all shell permissions as can't check shell: " + mPermissionContexts);
690             adoptShellPermissionIdentity();
691             return;
692         }
693 
694         dropShellPermissionIdentity();
695         // We first try to use shell permissions, because they can be revoked/etc. much more easily
696 
697         Set<String> adoptedShellPermissions = new HashSet<>();
698         Set<String> grantedPermissions = new HashSet<>();
699         Set<String> deniedPermissions = new HashSet<>();
700         for (String permission : permissionsToGrant) {
701             Log.d(LOG_TAG, "Trying to grant " + permission);
702             if (sInstrumentedPackage.hasPermission(user, permission)) {
703                 // Already granted, can skip
704                 Log.d(LOG_TAG, permission + " already granted at runtime");
705             } else if (mInstrumentedRequestedPermissions.contains(permission)
706                     && sContext.checkSelfPermission(permission) == PERMISSION_GRANTED) {
707                 // Already granted, can skip
708                 Log.d(LOG_TAG, permission + " already granted from manifest");
709             } else if (mShellPermissions.contains(permission)) {
710                 adoptedShellPermissions.add(permission);
711             } else {
712                 grantedPermissions.add(permission);
713             }
714         }
715 
716         for (String permission : permissionsToDeny) {
717             if (!sInstrumentedPackage.hasPermission(sUser, permission)) {
718                 // Already denied, can skip
719             } else if (!mShellPermissions.contains(permission)) {
720                 adoptedShellPermissions.add(permission);
721             } else {
722                 deniedPermissions.add(permission);
723             }
724         }
725 
726         if (!adoptedShellPermissions.isEmpty()) {
727             adoptShellPermissionIdentity(adoptedShellPermissions);
728         }
729         if (!grantedPermissions.isEmpty() || !deniedPermissions.isEmpty()) {
730             setPermissionStateToPackageWithoutAdoption(pkg, user, grantedPermissions, deniedPermissions);
731         }
732     }
733 
734     /** Ensure that permissions are not being overridden for any packages. */
735     @Experimental
736     public void clearAllOverridePermissionStates() {
737         if (Versions.meetsMinimumSdkVersionRequirement(Versions.V)) {
738             try {
739                 TestApisReflectionKt.clearAllOverridePermissionStates(ShellCommandUtils.uiAutomation());
740             } catch (Exception e) {
741                 Log.e(LOG_TAG, "Error clearing all override permission states", e);
742             }
743         }
744     }
745 
746     private void resetRootPermissionState(Package pkg, UserReference user) {
747         TestApisReflectionKt.clearOverridePermissionStates(ShellCommandUtils.uiAutomation(),
748                 pkg.uid(user));
749     }
750 
751     private void forceRootPermissionState(Package pkg, UserReference user, String permission, boolean granted) {
752         TestApisReflectionKt.addOverridePermissionState(
753                 ShellCommandUtils.uiAutomation(), pkg.uid(user), permission,
754                 granted ? PERMISSION_GRANTED : PERMISSION_DENIED);
755     }
756 
757     public void removeRootPermissionState(Package pkg, UserReference user, String permission) {
758         TestApisReflectionKt.removeOverridePermissionState(
759                 ShellCommandUtils.uiAutomation(), pkg.uid(user), permission);
760     }
761 
762     private static boolean hasAdoptedShellPermissionIdentity = false;
763     private static void adoptShellPermissionIdentity(Collection<String> permissions) {
764         adoptShellPermissionIdentity(permissions.toArray(new String[0]));
765     }
766 
767     private static void adoptShellPermissionIdentity(String... permissions) {
768         if (permissions.length == 0) {
769             dropShellPermissionIdentity();
770             return;
771         }
772 
773         Log.d(LOG_TAG, "Adopting " + Arrays.toString(permissions));
774         hasAdoptedShellPermissionIdentity = true;
775         ShellCommandUtils.uiAutomation().adoptShellPermissionIdentity(permissions);
776     }
777 
778     private static void adoptShellPermissionIdentity() {
779         Log.d(LOG_TAG, "Adopting all shell permissions");
780         hasAdoptedShellPermissionIdentity = true;
781         ShellCommandUtils.uiAutomation().adoptShellPermissionIdentity();
782     }
783 
784     private static void dropShellPermissionIdentity() {
785         Log.d(LOG_TAG, "Dropping shell permissions");
786         hasAdoptedShellPermissionIdentity = false;
787         ShellCommandUtils.uiAutomation().dropShellPermissionIdentity();
788     }
789 
790     /** Get string dump of permissions state. */
791     public String dump() {
792         if (!Versions.meetsMinimumSdkVersionRequirement(Versions.V)) {
793             Log.i(LOG_TAG, "Cannot dump permission before V so dumping packages");
794             return TestApis.packages().dump();
795         }
796 
797         return ShellCommand.builder("dumpsys permissionmgr").validate((s) -> !s.isEmpty())
798                 .executeOrThrowNeneException("Error dumping permission state");
799     }
800 }
801