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.server.companion.utils; 18 19 import static android.Manifest.permission.BLUETOOTH_CONNECT; 20 import static android.Manifest.permission.BLUETOOTH_SCAN; 21 import static android.Manifest.permission.INTERACT_ACROSS_USERS; 22 import static android.Manifest.permission.MANAGE_COMPANION_DEVICES; 23 import static android.Manifest.permission.REQUEST_COMPANION_SELF_MANAGED; 24 import static android.Manifest.permission.REQUEST_OBSERVE_DEVICE_UUID_PRESENCE; 25 import static android.app.AppOpsManager.MODE_ALLOWED; 26 import static android.companion.AssociationRequest.DEVICE_PROFILE_APP_STREAMING; 27 import static android.companion.AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PROJECTION; 28 import static android.companion.AssociationRequest.DEVICE_PROFILE_COMPUTER; 29 import static android.companion.AssociationRequest.DEVICE_PROFILE_GLASSES; 30 import static android.companion.AssociationRequest.DEVICE_PROFILE_NEARBY_DEVICE_STREAMING; 31 import static android.companion.AssociationRequest.DEVICE_PROFILE_VIRTUAL_DEVICE; 32 import static android.companion.AssociationRequest.DEVICE_PROFILE_WATCH; 33 import static android.companion.AssociationRequest.DEVICE_PROFILE_WEARABLE_SENSING; 34 import static android.content.pm.PackageManager.PERMISSION_GRANTED; 35 import static android.os.Binder.getCallingPid; 36 import static android.os.Binder.getCallingUid; 37 import static android.os.Process.SYSTEM_UID; 38 import static android.os.UserHandle.getCallingUserId; 39 40 import static com.android.server.companion.utils.RolesUtils.isRoleHolder; 41 42 import static java.util.Collections.unmodifiableMap; 43 import static java.util.Collections.unmodifiableSet; 44 45 import android.Manifest; 46 import android.annotation.NonNull; 47 import android.annotation.Nullable; 48 import android.annotation.UserIdInt; 49 import android.companion.AssociationRequest; 50 import android.companion.CompanionDeviceManager; 51 import android.content.Context; 52 import android.os.Binder; 53 import android.os.RemoteException; 54 import android.os.ServiceManager; 55 import android.util.ArrayMap; 56 import android.util.ArraySet; 57 58 import com.android.internal.app.IAppOpsService; 59 60 import java.util.Map; 61 import java.util.Set; 62 63 /** 64 * Utility methods for checking permissions required for accessing {@link CompanionDeviceManager} 65 * APIs (such as {@link Manifest.permission#REQUEST_COMPANION_PROFILE_WATCH}, 66 * {@link Manifest.permission#REQUEST_COMPANION_PROFILE_APP_STREAMING}, 67 * {@link Manifest.permission#REQUEST_COMPANION_SELF_MANAGED} etc.) 68 */ 69 public final class PermissionsUtils { 70 71 private static final Set<String> SYSTEM_ONLY_DEVICE_PROFILES; 72 static { 73 final Set<String> set = new ArraySet<>(); 74 set.add(DEVICE_PROFILE_WEARABLE_SENSING); 75 SYSTEM_ONLY_DEVICE_PROFILES = unmodifiableSet(set); 76 } 77 78 private static final Map<String, String> DEVICE_PROFILE_TO_PERMISSION; 79 static { 80 final Map<String, String> map = new ArrayMap<>(); map.put(DEVICE_PROFILE_WATCH, Manifest.permission.REQUEST_COMPANION_PROFILE_WATCH)81 map.put(DEVICE_PROFILE_WATCH, Manifest.permission.REQUEST_COMPANION_PROFILE_WATCH); map.put(DEVICE_PROFILE_APP_STREAMING, Manifest.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING)82 map.put(DEVICE_PROFILE_APP_STREAMING, 83 Manifest.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING); map.put(DEVICE_PROFILE_AUTOMOTIVE_PROJECTION, Manifest.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION)84 map.put(DEVICE_PROFILE_AUTOMOTIVE_PROJECTION, 85 Manifest.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION); map.put(DEVICE_PROFILE_COMPUTER, Manifest.permission.REQUEST_COMPANION_PROFILE_COMPUTER)86 map.put(DEVICE_PROFILE_COMPUTER, Manifest.permission.REQUEST_COMPANION_PROFILE_COMPUTER); map.put(DEVICE_PROFILE_GLASSES, Manifest.permission.REQUEST_COMPANION_PROFILE_GLASSES)87 map.put(DEVICE_PROFILE_GLASSES, Manifest.permission.REQUEST_COMPANION_PROFILE_GLASSES); map.put(DEVICE_PROFILE_NEARBY_DEVICE_STREAMING, Manifest.permission.REQUEST_COMPANION_PROFILE_NEARBY_DEVICE_STREAMING)88 map.put(DEVICE_PROFILE_NEARBY_DEVICE_STREAMING, 89 Manifest.permission.REQUEST_COMPANION_PROFILE_NEARBY_DEVICE_STREAMING); map.put(DEVICE_PROFILE_VIRTUAL_DEVICE, Manifest.permission.REQUEST_COMPANION_PROFILE_VIRTUAL_DEVICE)90 map.put(DEVICE_PROFILE_VIRTUAL_DEVICE, 91 Manifest.permission.REQUEST_COMPANION_PROFILE_VIRTUAL_DEVICE); 92 93 DEVICE_PROFILE_TO_PERMISSION = unmodifiableMap(map); 94 } 95 96 /** 97 * Require the app to declare necessary permission for creating association. 98 */ enforcePermissionForCreatingAssociation(@onNull Context context, @NonNull AssociationRequest request, int packageUid)99 public static void enforcePermissionForCreatingAssociation(@NonNull Context context, 100 @NonNull AssociationRequest request, int packageUid) { 101 enforcePermissionForRequestingProfile(context, request.getDeviceProfile(), packageUid); 102 103 if (request.isSelfManaged() || request.getDeviceIcon() != null) { 104 enforcePermissionForRequestingSelfManaged(context, packageUid); 105 } 106 } 107 108 /** 109 * Require the app to declare necessary permission for creating association with profile. 110 */ enforcePermissionForRequestingProfile( @onNull Context context, @Nullable String deviceProfile, int packageUid)111 public static void enforcePermissionForRequestingProfile( 112 @NonNull Context context, @Nullable String deviceProfile, int packageUid) { 113 // Device profile can be null. 114 if (deviceProfile == null) return; 115 116 if (!DEVICE_PROFILE_TO_PERMISSION.containsKey(deviceProfile) 117 && !SYSTEM_ONLY_DEVICE_PROFILES.contains(deviceProfile)) { 118 throw new IllegalArgumentException("Unsupported device profile: " + deviceProfile); 119 } 120 121 if (SYSTEM_ONLY_DEVICE_PROFILES.contains(deviceProfile) && getCallingUid() != SYSTEM_UID) { 122 throw new SecurityException("Caller must be system to associate with a device with " 123 + deviceProfile + " profile."); 124 } 125 126 final String permission = DEVICE_PROFILE_TO_PERMISSION.getOrDefault(deviceProfile, null); 127 if (permission != null && context.checkPermission(permission, getCallingPid(), packageUid) 128 != PERMISSION_GRANTED) { 129 throw new SecurityException("Application must hold " + permission + " to associate " 130 + "with a device with " + deviceProfile + " profile."); 131 } 132 } 133 134 /** 135 * Require the app to declare necessary permission for creating self-managed association. 136 */ enforcePermissionForRequestingSelfManaged(@onNull Context context, int packageUid)137 public static void enforcePermissionForRequestingSelfManaged(@NonNull Context context, 138 int packageUid) { 139 if (context.checkPermission(REQUEST_COMPANION_SELF_MANAGED, getCallingPid(), packageUid) 140 != PERMISSION_GRANTED) { 141 throw new SecurityException("Application does not hold " 142 + REQUEST_COMPANION_SELF_MANAGED); 143 } 144 } 145 146 /** 147 * Check if the caller can interact with the user. 148 */ checkCallerCanInteractWithUserId(@onNull Context context, int userId)149 public static boolean checkCallerCanInteractWithUserId(@NonNull Context context, int userId) { 150 if (getCallingUserId() == userId) return true; 151 152 return context.checkCallingPermission(INTERACT_ACROSS_USERS) == PERMISSION_GRANTED; 153 } 154 155 /** 156 * Require the caller to be able to interact with the user. 157 */ enforceCallerCanInteractWithUserId(@onNull Context context, int userId)158 public static void enforceCallerCanInteractWithUserId(@NonNull Context context, int userId) { 159 if (getCallingUserId() == userId) return; 160 161 context.enforceCallingPermission(INTERACT_ACROSS_USERS, null); 162 } 163 164 /** 165 * Require the caller to be system UID or to be able to interact with the user. 166 */ enforceCallerIsSystemOrCanInteractWithUserId(@onNull Context context, int userId)167 public static void enforceCallerIsSystemOrCanInteractWithUserId(@NonNull Context context, 168 int userId) { 169 if (getCallingUid() == SYSTEM_UID) return; 170 171 enforceCallerCanInteractWithUserId(context, userId); 172 } 173 174 /** 175 * Check if the calling user id matches the userId, and if the package belongs to 176 * the calling uid. 177 */ enforceCallerIsSystemOr(@serIdInt int userId, @NonNull String packageName)178 public static void enforceCallerIsSystemOr(@UserIdInt int userId, @NonNull String packageName) { 179 final int callingUid = getCallingUid(); 180 if (callingUid == SYSTEM_UID) return; 181 182 final int callingUserId = getCallingUserId(); 183 if (getCallingUserId() != userId) { 184 throw new SecurityException("Calling UserId (" + callingUserId + ") does not match " 185 + "the expected UserId (" + userId + ")"); 186 } 187 188 if (!checkPackage(callingUid, packageName)) { 189 throw new SecurityException(packageName + " doesn't belong to calling uid (" 190 + callingUid + ")"); 191 } 192 } 193 194 /** 195 * Require the caller to be able to manage the associations for the package. 196 */ enforceCallerCanManageAssociationsForPackage(@onNull Context context, @UserIdInt int userId, @NonNull String packageName, @Nullable String actionDescription)197 public static void enforceCallerCanManageAssociationsForPackage(@NonNull Context context, 198 @UserIdInt int userId, @NonNull String packageName, 199 @Nullable String actionDescription) { 200 final int callingUid = getCallingUid(); 201 202 // If the caller is the system 203 if (callingUid == SYSTEM_UID) { 204 return; 205 } 206 207 // If caller can manage the package or has the permissions to manage companion devices 208 boolean canInteractAcrossUsers = context.checkCallingPermission(INTERACT_ACROSS_USERS) 209 == PERMISSION_GRANTED; 210 boolean canManageCompanionDevices = context.checkCallingPermission(MANAGE_COMPANION_DEVICES) 211 == PERMISSION_GRANTED; 212 if (getCallingUserId() == userId) { 213 if (checkPackage(callingUid, packageName) || canManageCompanionDevices) { 214 return; 215 } 216 } else if (canInteractAcrossUsers && canManageCompanionDevices) { 217 return; 218 } 219 220 throw new SecurityException("Caller (uid=" + getCallingUid() + ") does not have " 221 + "permissions to " 222 + (actionDescription != null ? actionDescription : "manage associations") 223 + " for u" + userId + "/" + packageName); 224 } 225 226 /** 227 * Require the caller to hold necessary permission to observe device presence by UUID. 228 */ enforceCallerCanObserveDevicePresenceByUuid(@onNull Context context, String packageName, int userId)229 public static void enforceCallerCanObserveDevicePresenceByUuid(@NonNull Context context, 230 String packageName, int userId) { 231 if (!hasRequirePermissions(context, packageName, userId)) { 232 throw new SecurityException("Caller (uid=" + getCallingUid() + ") does not have " 233 + "permissions to request observing device presence base on the UUID"); 234 } 235 } 236 checkPackage(@serIdInt int uid, @NonNull String packageName)237 private static boolean checkPackage(@UserIdInt int uid, @NonNull String packageName) { 238 try { 239 return getAppOpsService().checkPackage(uid, packageName) == MODE_ALLOWED; 240 } catch (RemoteException e) { 241 // Can't happen: AppOpsManager is running in the same process. 242 return true; 243 } 244 } 245 getAppOpsService()246 private static IAppOpsService getAppOpsService() { 247 if (sAppOpsService == null) { 248 synchronized (PermissionsUtils.class) { 249 if (sAppOpsService == null) { 250 sAppOpsService = IAppOpsService.Stub.asInterface( 251 ServiceManager.getService(Context.APP_OPS_SERVICE)); 252 } 253 } 254 } 255 return sAppOpsService; 256 } 257 hasRequirePermissions( @onNull Context context, String packageName, int userId)258 private static boolean hasRequirePermissions( 259 @NonNull Context context, String packageName, int userId) { 260 return context.checkCallingPermission( 261 REQUEST_OBSERVE_DEVICE_UUID_PRESENCE) == PERMISSION_GRANTED 262 && context.checkCallingPermission(BLUETOOTH_SCAN) == PERMISSION_GRANTED 263 && context.checkCallingPermission(BLUETOOTH_CONNECT) == PERMISSION_GRANTED 264 && Boolean.TRUE.equals(Binder.withCleanCallingIdentity( 265 () -> isRoleHolder(context, userId, packageName, 266 DEVICE_PROFILE_AUTOMOTIVE_PROJECTION))); 267 } 268 269 // DO NOT USE DIRECTLY! Access via getAppOpsService(). 270 private static IAppOpsService sAppOpsService = null; 271 PermissionsUtils()272 private PermissionsUtils() {} 273 } 274