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