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