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