• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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 android.content;
18 
19 import android.annotation.IntDef;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.app.AppOpsManager;
23 import android.content.pm.PackageManager;
24 import android.os.Binder;
25 import android.os.Process;
26 
27 import java.lang.annotation.Retention;
28 import java.lang.annotation.RetentionPolicy;
29 
30 /**
31  * This class provides permission check APIs that verify both the
32  * permission and the associated app op for this permission if
33  * such is defined.
34  * <p>
35  * In the new permission model permissions with protection level
36  * dangerous are runtime permissions. For apps targeting {@link android.os.Build.VERSION_CODES#M}
37  * and above the user may not grant such permissions or revoke
38  * them at any time. For apps targeting API lower than {@link android.os.Build.VERSION_CODES#M}
39  * these permissions are always granted as such apps do not expect
40  * permission revocations and would crash. Therefore, when the
41  * user disables a permission for a legacy app in the UI the
42  * platform disables the APIs guarded by this permission making
43  * them a no-op which is doing nothing or returning an empty
44  * result or default error.
45  * </p>
46  * <p>
47  * It is important that when you perform an operation on behalf of
48  * another app you use these APIs to check for permissions as the
49  * app may be a legacy app that does not participate in the new
50  * permission model for which the user had disabled the "permission"
51  * which is achieved by disallowing the corresponding app op.
52  * </p>
53  *
54  * @hide
55  */
56 public final class PermissionChecker {
57     /** Permission result: The permission is granted. */
58     public static final int PERMISSION_GRANTED =  PackageManager.PERMISSION_GRANTED;
59 
60     /** Permission result: The permission is denied. */
61     public static final int PERMISSION_DENIED =  PackageManager.PERMISSION_DENIED;
62 
63     /** Permission result: The permission is denied because the app op is not allowed. */
64     public static final int PERMISSION_DENIED_APP_OP =  PackageManager.PERMISSION_DENIED  - 1;
65 
66     /** @hide */
67     @IntDef({PERMISSION_GRANTED,
68             PERMISSION_DENIED,
69             PERMISSION_DENIED_APP_OP})
70     @Retention(RetentionPolicy.SOURCE)
71     public @interface PermissionResult {}
72 
PermissionChecker()73     private PermissionChecker() {
74         /* do nothing */
75     }
76 
77     /**
78      * Checks whether a given package in a UID and PID has a given permission
79      * and whether the app op that corresponds to this permission is allowed.
80      *
81      * @param context Context for accessing resources.
82      * @param permission The permission to check.
83      * @param pid The process id for which to check.
84      * @param uid The uid for which to check.
85      * @param packageName The package name for which to check. If null the
86      *     the first package for the calling UID will be used.
87      * @return The permission check result which is either {@link #PERMISSION_GRANTED}
88      *     or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.
89      */
90     @PermissionResult
checkPermission(@onNull Context context, @NonNull String permission, int pid, int uid, @Nullable String packageName)91     public static int checkPermission(@NonNull Context context, @NonNull String permission,
92             int pid, int uid, @Nullable String packageName) {
93         if (context.checkPermission(permission, pid, uid) == PackageManager.PERMISSION_DENIED) {
94             return PERMISSION_DENIED;
95         }
96 
97         AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
98         String op = appOpsManager.permissionToOp(permission);
99         if (op == null) {
100             return PERMISSION_GRANTED;
101         }
102 
103         if (packageName == null) {
104             String[] packageNames = context.getPackageManager().getPackagesForUid(uid);
105             if (packageNames == null || packageNames.length <= 0) {
106                 return PERMISSION_DENIED;
107             }
108             packageName = packageNames[0];
109         }
110 
111         if (appOpsManager.noteProxyOpNoThrow(op, packageName)
112                 != AppOpsManager.MODE_ALLOWED) {
113             return PERMISSION_DENIED_APP_OP;
114         }
115 
116         return PERMISSION_GRANTED;
117     }
118 
119     /**
120      * Checks whether your app has a given permission and whether the app op
121      * that corresponds to this permission is allowed.
122      *
123      * @param context Context for accessing resources.
124      * @param permission The permission to check.
125      * @return The permission check result which is either {@link #PERMISSION_GRANTED}
126      *     or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.
127      */
128     @PermissionResult
checkSelfPermission(@onNull Context context, @NonNull String permission)129     public static int checkSelfPermission(@NonNull Context context,
130             @NonNull String permission) {
131         return checkPermission(context, permission, Process.myPid(),
132                 Process.myUid(), context.getPackageName());
133     }
134 
135     /**
136      * Checks whether the IPC you are handling has a given permission and whether
137      * the app op that corresponds to this permission is allowed.
138      *
139      * @param context Context for accessing resources.
140      * @param permission The permission to check.
141      * @param packageName The package name making the IPC. If null the
142      *     the first package for the calling UID will be used.
143      * @return The permission check result which is either {@link #PERMISSION_GRANTED}
144      *     or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.
145      */
146     @PermissionResult
checkCallingPermission(@onNull Context context, @NonNull String permission, @Nullable String packageName)147     public static int checkCallingPermission(@NonNull Context context,
148             @NonNull String permission, @Nullable String packageName) {
149         if (Binder.getCallingPid() == Process.myPid()) {
150             return PERMISSION_DENIED;
151         }
152         return checkPermission(context, permission, Binder.getCallingPid(),
153                 Binder.getCallingUid(), packageName);
154     }
155 
156     /**
157      * Checks whether the IPC you are handling or your app has a given permission
158      * and whether the app op that corresponds to this permission is allowed.
159      *
160      * @param context Context for accessing resources.
161      * @param permission The permission to check.
162      * @return The permission check result which is either {@link #PERMISSION_GRANTED}
163      *     or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.
164      */
165     @PermissionResult
checkCallingOrSelfPermission(@onNull Context context, @NonNull String permission)166     public static int checkCallingOrSelfPermission(@NonNull Context context,
167             @NonNull String permission) {
168         String packageName = (Binder.getCallingPid() == Process.myPid())
169                 ? context.getPackageName() : null;
170         return checkPermission(context, permission, Binder.getCallingPid(),
171                 Binder.getCallingUid(), packageName);
172     }
173 }
174