1 /*
2  * Copyright (C) 2020 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.server.location;
18 
19 import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
20 import static android.Manifest.permission.ACCESS_FINE_LOCATION;
21 import static android.Manifest.permission.LOCATION_BYPASS;
22 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
23 
24 import android.annotation.IntDef;
25 import android.app.AppOpsManager;
26 import android.content.Context;
27 import android.os.Binder;
28 
29 import java.lang.annotation.ElementType;
30 import java.lang.annotation.Retention;
31 import java.lang.annotation.RetentionPolicy;
32 import java.lang.annotation.Target;
33 
34 /** Utility class for dealing with location permissions. */
35 public final class LocationPermissions {
36 
37     /**
38      * Indicates no location permissions are present, or no location permission are required.
39      */
40     public static final int PERMISSION_NONE = 0;
41 
42     /**
43      * Indicates the coarse location permission is present, or either the coarse or fine permissions
44      * are required.
45      */
46     public static final int PERMISSION_COARSE = 1;
47 
48     /**
49      * Indicates the fine location permission is present, or the fine location permission is
50      * required.
51      */
52     public static final int PERMISSION_FINE = 2;
53 
54     @Target(ElementType.TYPE_USE)
55     @IntDef({PERMISSION_NONE, PERMISSION_COARSE, PERMISSION_FINE})
56     @Retention(RetentionPolicy.SOURCE)
57     public @interface PermissionLevel {}
58 
59     /**
60      * Converts the given permission level to the corresponding permission.
61      */
asPermission(@ermissionLevel int permissionLevel)62     public static String asPermission(@PermissionLevel int permissionLevel) {
63         switch (permissionLevel) {
64             case PERMISSION_COARSE:
65                 return ACCESS_COARSE_LOCATION;
66             case PERMISSION_FINE:
67                 return ACCESS_FINE_LOCATION;
68             default:
69                 throw new IllegalArgumentException();
70         }
71     }
72 
73     /**
74      * Converts the given permission level to the corresponding appop.
75      */
asAppOp(@ermissionLevel int permissionLevel)76     public static int asAppOp(@PermissionLevel int permissionLevel) {
77         switch (permissionLevel) {
78             case PERMISSION_COARSE:
79                 return AppOpsManager.OP_COARSE_LOCATION;
80             case PERMISSION_FINE:
81                 return AppOpsManager.OP_FINE_LOCATION;
82             default:
83                 throw new IllegalArgumentException();
84         }
85     }
86 
87     /**
88      * Throws a security exception if the caller does not hold the required location permissions.
89      */
enforceCallingOrSelfLocationPermission(Context context, @PermissionLevel int requiredPermissionLevel)90     public static void enforceCallingOrSelfLocationPermission(Context context,
91             @PermissionLevel int requiredPermissionLevel) {
92         enforceLocationPermission(Binder.getCallingUid(),
93                 getPermissionLevel(context, Binder.getCallingUid(), Binder.getCallingPid()),
94                 requiredPermissionLevel);
95     }
96 
97     /**
98      * Throws a security exception if the given uid/pid does not hold the required location
99      * permissions.
100      */
enforceLocationPermission(Context context, int uid, int pid, @PermissionLevel int requiredPermissionLevel)101     public static void enforceLocationPermission(Context context, int uid, int pid,
102             @PermissionLevel int requiredPermissionLevel) {
103         enforceLocationPermission(uid,
104                 getPermissionLevel(context, uid, pid),
105                 requiredPermissionLevel);
106     }
107 
108     /**
109      * Throws a security exception if the given permission level does not meet the required location
110      * permission level.
111      */
enforceLocationPermission(int uid, @PermissionLevel int permissionLevel, @PermissionLevel int requiredPermissionLevel)112     public static void enforceLocationPermission(int uid, @PermissionLevel int permissionLevel,
113             @PermissionLevel int requiredPermissionLevel) {
114         if (checkLocationPermission(permissionLevel, requiredPermissionLevel)) {
115             return;
116         }
117 
118         if (requiredPermissionLevel == PERMISSION_COARSE) {
119             throw new SecurityException("uid " + uid + " does not have " + ACCESS_COARSE_LOCATION
120                     + " or " + ACCESS_FINE_LOCATION + ".");
121         } else if (requiredPermissionLevel == PERMISSION_FINE) {
122             throw new SecurityException("uid " + uid + " does not have " + ACCESS_FINE_LOCATION
123                     + ".");
124         }
125     }
126 
127     /**
128      * Throws a security exception if the caller does not hold the required bypass permissions.
129      */
enforceCallingOrSelfBypassPermission(Context context)130     public static void enforceCallingOrSelfBypassPermission(Context context) {
131         enforceBypassPermission(context, Binder.getCallingUid(), Binder.getCallingPid());
132     }
133 
134     /**
135      * Throws a security exception if the given uid/pid does not hold the required bypass
136      * perissions.
137      */
enforceBypassPermission(Context context, int uid, int pid)138     public static void enforceBypassPermission(Context context, int uid, int pid) {
139         if (context.checkPermission(LOCATION_BYPASS, pid, uid) == PERMISSION_GRANTED) {
140             return;
141         }
142         throw new SecurityException("uid" + uid + " does not have " + LOCATION_BYPASS
143                 + ".");
144     }
145 
146     /**
147      * Returns false if the caller does not hold the required location permissions.
148      */
checkCallingOrSelfLocationPermission(Context context, @PermissionLevel int requiredPermissionLevel)149     public static boolean checkCallingOrSelfLocationPermission(Context context,
150             @PermissionLevel int requiredPermissionLevel) {
151         return checkLocationPermission(
152                 getCallingOrSelfPermissionLevel(context),
153                 requiredPermissionLevel);
154     }
155 
156     /**
157      * Returns false if the given uid/pid does not hold the required location permissions.
158      */
checkLocationPermission(Context context, int uid, int pid, @PermissionLevel int requiredPermissionLevel)159     public static boolean checkLocationPermission(Context context, int uid, int pid,
160             @PermissionLevel int requiredPermissionLevel) {
161         return checkLocationPermission(
162                 getPermissionLevel(context, uid, pid),
163                 requiredPermissionLevel);
164     }
165 
166     /**
167      * Returns false if the given permission level does not meet the required location permission
168      * level.
169      */
checkLocationPermission(@ermissionLevel int permissionLevel, @PermissionLevel int requiredPermissionLevel)170     public static boolean checkLocationPermission(@PermissionLevel int permissionLevel,
171             @PermissionLevel int requiredPermissionLevel) {
172         return permissionLevel >= requiredPermissionLevel;
173     }
174 
175     /**
176      * Returns the permission level of the caller.
177      */
178     @PermissionLevel
getCallingOrSelfPermissionLevel(Context context)179     public static int getCallingOrSelfPermissionLevel(Context context) {
180         return getPermissionLevel(context, Binder.getCallingUid(), Binder.getCallingPid());
181     }
182 
183     /**
184      * Returns the permission level of the given uid/pid.
185      */
186     @PermissionLevel
getPermissionLevel(Context context, int uid, int pid)187     public static int getPermissionLevel(Context context, int uid, int pid) {
188         if (context.checkPermission(ACCESS_FINE_LOCATION, pid, uid) == PERMISSION_GRANTED) {
189             return PERMISSION_FINE;
190         }
191         if (context.checkPermission(ACCESS_COARSE_LOCATION, pid, uid) == PERMISSION_GRANTED) {
192             return PERMISSION_COARSE;
193         }
194 
195         return PERMISSION_NONE;
196     }
197 
LocationPermissions()198     private LocationPermissions() {}
199 }
200