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