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; 18 19 import static android.Manifest.permission.INTERACT_ACROSS_USERS; 20 import static android.Manifest.permission.MANAGE_COMPANION_DEVICES; 21 import static android.Manifest.permission.REQUEST_COMPANION_SELF_MANAGED; 22 import static android.app.AppOpsManager.MODE_ALLOWED; 23 import static android.companion.AssociationRequest.DEVICE_PROFILE_APP_STREAMING; 24 import static android.companion.AssociationRequest.DEVICE_PROFILE_AUTOMOTIVE_PROJECTION; 25 import static android.companion.AssociationRequest.DEVICE_PROFILE_COMPUTER; 26 import static android.companion.AssociationRequest.DEVICE_PROFILE_WATCH; 27 import static android.content.pm.PackageManager.PERMISSION_GRANTED; 28 import static android.os.Binder.getCallingPid; 29 import static android.os.Binder.getCallingUid; 30 import static android.os.Process.SYSTEM_UID; 31 import static android.os.UserHandle.getCallingUserId; 32 33 import static java.util.Collections.unmodifiableMap; 34 35 import android.Manifest; 36 import android.annotation.NonNull; 37 import android.annotation.Nullable; 38 import android.annotation.UserIdInt; 39 import android.companion.AssociationInfo; 40 import android.companion.AssociationRequest; 41 import android.companion.CompanionDeviceManager; 42 import android.content.Context; 43 import android.os.RemoteException; 44 import android.os.ServiceManager; 45 import android.util.ArrayMap; 46 47 import com.android.internal.app.IAppOpsService; 48 49 import java.util.Map; 50 51 /** 52 * Utility methods for checking permissions required for accessing {@link CompanionDeviceManager} 53 * APIs (such as {@link Manifest.permission#REQUEST_COMPANION_PROFILE_WATCH}, 54 * {@link Manifest.permission#REQUEST_COMPANION_PROFILE_APP_STREAMING}, 55 * {@link Manifest.permission#REQUEST_COMPANION_SELF_MANAGED} etc.) 56 */ 57 final class PermissionsUtils { 58 59 private static final Map<String, String> DEVICE_PROFILE_TO_PERMISSION; 60 static { 61 final Map<String, String> map = new ArrayMap<>(); map.put(DEVICE_PROFILE_WATCH, Manifest.permission.REQUEST_COMPANION_PROFILE_WATCH)62 map.put(DEVICE_PROFILE_WATCH, Manifest.permission.REQUEST_COMPANION_PROFILE_WATCH); map.put(DEVICE_PROFILE_APP_STREAMING, Manifest.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING)63 map.put(DEVICE_PROFILE_APP_STREAMING, 64 Manifest.permission.REQUEST_COMPANION_PROFILE_APP_STREAMING); map.put(DEVICE_PROFILE_AUTOMOTIVE_PROJECTION, Manifest.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION)65 map.put(DEVICE_PROFILE_AUTOMOTIVE_PROJECTION, 66 Manifest.permission.REQUEST_COMPANION_PROFILE_AUTOMOTIVE_PROJECTION); map.put(DEVICE_PROFILE_COMPUTER, Manifest.permission.REQUEST_COMPANION_PROFILE_COMPUTER)67 map.put(DEVICE_PROFILE_COMPUTER, Manifest.permission.REQUEST_COMPANION_PROFILE_COMPUTER); 68 69 DEVICE_PROFILE_TO_PERMISSION = unmodifiableMap(map); 70 } 71 enforcePermissionsForAssociation(@onNull Context context, @NonNull AssociationRequest request, int packageUid)72 static void enforcePermissionsForAssociation(@NonNull Context context, 73 @NonNull AssociationRequest request, int packageUid) { 74 enforceRequestDeviceProfilePermissions(context, request.getDeviceProfile(), packageUid); 75 76 if (request.isSelfManaged()) { 77 enforceRequestSelfManagedPermission(context, packageUid); 78 } 79 } 80 enforceRequestDeviceProfilePermissions( @onNull Context context, @Nullable String deviceProfile, int packageUid)81 static void enforceRequestDeviceProfilePermissions( 82 @NonNull Context context, @Nullable String deviceProfile, int packageUid) { 83 // Device profile can be null. 84 if (deviceProfile == null) return; 85 86 if (!DEVICE_PROFILE_TO_PERMISSION.containsKey(deviceProfile)) { 87 throw new IllegalArgumentException("Unsupported device profile: " + deviceProfile); 88 } 89 90 final String permission = DEVICE_PROFILE_TO_PERMISSION.get(deviceProfile); 91 if (context.checkPermission(permission, getCallingPid(), packageUid) 92 != PERMISSION_GRANTED) { 93 throw new SecurityException("Application must hold " + permission + " to associate " 94 + "with a device with " + deviceProfile + " profile."); 95 } 96 } 97 enforceRequestSelfManagedPermission(@onNull Context context, int packageUid)98 static void enforceRequestSelfManagedPermission(@NonNull Context context, int packageUid) { 99 if (context.checkPermission(REQUEST_COMPANION_SELF_MANAGED, getCallingPid(), packageUid) 100 != PERMISSION_GRANTED) { 101 throw new SecurityException("Application does not hold " 102 + REQUEST_COMPANION_SELF_MANAGED); 103 } 104 } 105 checkCallerCanInteractWithUserId(@onNull Context context, int userId)106 static boolean checkCallerCanInteractWithUserId(@NonNull Context context, int userId) { 107 if (getCallingUserId() == userId) return true; 108 109 return context.checkCallingPermission(INTERACT_ACROSS_USERS) == PERMISSION_GRANTED; 110 } 111 enforceCallerCanInteractWithUserId(@onNull Context context, int userId)112 static void enforceCallerCanInteractWithUserId(@NonNull Context context, int userId) { 113 if (getCallingUserId() == userId) return; 114 115 context.enforceCallingPermission(INTERACT_ACROSS_USERS, null); 116 } 117 enforceCallerIsSystemOrCanInteractWithUserId(@onNull Context context, int userId)118 static void enforceCallerIsSystemOrCanInteractWithUserId(@NonNull Context context, int userId) { 119 if (getCallingUid() == SYSTEM_UID) return; 120 121 enforceCallerCanInteractWithUserId(context, userId); 122 } 123 checkCallerIsSystemOr(@serIdInt int userId, @NonNull String packageName)124 static boolean checkCallerIsSystemOr(@UserIdInt int userId, @NonNull String packageName) { 125 final int callingUid = getCallingUid(); 126 if (callingUid == SYSTEM_UID) return true; 127 128 if (getCallingUserId() != userId) return false; 129 130 if (!checkPackage(callingUid, packageName)) return false; 131 132 return true; 133 } 134 enforceCallerIsSystemOr(@serIdInt int userId, @NonNull String packageName)135 static void enforceCallerIsSystemOr(@UserIdInt int userId, @NonNull String packageName) { 136 final int callingUid = getCallingUid(); 137 if (callingUid == SYSTEM_UID) return; 138 139 final int callingUserId = getCallingUserId(); 140 if (getCallingUserId() != userId) { 141 throw new SecurityException("Calling UserId (" + callingUserId + ") does not match " 142 + "the expected UserId (" + userId + ")"); 143 } 144 145 if (!checkPackage(callingUid, packageName)) { 146 throw new SecurityException(packageName + " doesn't belong to calling uid (" 147 + callingUid + ")"); 148 } 149 } 150 checkCallerCanManageCompanionDevice(@onNull Context context)151 static boolean checkCallerCanManageCompanionDevice(@NonNull Context context) { 152 if (getCallingUid() == SYSTEM_UID) return true; 153 154 return context.checkCallingPermission(MANAGE_COMPANION_DEVICES) == PERMISSION_GRANTED; 155 } 156 enforceCallerCanManageCompanionDevice(@onNull Context context, @Nullable String message)157 static void enforceCallerCanManageCompanionDevice(@NonNull Context context, 158 @Nullable String message) { 159 if (getCallingUid() == SYSTEM_UID) return; 160 161 context.enforceCallingPermission(MANAGE_COMPANION_DEVICES, message); 162 } 163 enforceCallerCanManageAssociationsForPackage(@onNull Context context, @UserIdInt int userId, @NonNull String packageName, @Nullable String actionDescription)164 static void enforceCallerCanManageAssociationsForPackage(@NonNull Context context, 165 @UserIdInt int userId, @NonNull String packageName, 166 @Nullable String actionDescription) { 167 if (checkCallerCanManageAssociationsForPackage(context, userId, packageName)) return; 168 169 throw new SecurityException("Caller (uid=" + getCallingUid() + ") does not have " 170 + "permissions to " 171 + (actionDescription != null ? actionDescription : "manage associations") 172 + " for u" + userId + "/" + packageName); 173 } 174 175 /** 176 * Check if the caller is either: 177 * <ul> 178 * <li> the package itself 179 * <li> the System ({@link android.os.Process#SYSTEM_UID}) 180 * <li> holds {@link Manifest.permission#MANAGE_COMPANION_DEVICES} and, if belongs to a 181 * different user, also holds {@link Manifest.permission#INTERACT_ACROSS_USERS}. 182 * </ul> 183 * @return whether the caller is one of the above. 184 */ checkCallerCanManageAssociationsForPackage(@onNull Context context, @UserIdInt int userId, @NonNull String packageName)185 static boolean checkCallerCanManageAssociationsForPackage(@NonNull Context context, 186 @UserIdInt int userId, @NonNull String packageName) { 187 if (checkCallerIsSystemOr(userId, packageName)) return true; 188 189 if (!checkCallerCanInteractWithUserId(context, userId)) return false; 190 191 return checkCallerCanManageCompanionDevice(context); 192 } 193 sanitizeWithCallerChecks(@onNull Context context, @Nullable AssociationInfo association)194 static @Nullable AssociationInfo sanitizeWithCallerChecks(@NonNull Context context, 195 @Nullable AssociationInfo association) { 196 if (association == null) return null; 197 198 final int userId = association.getUserId(); 199 final String packageName = association.getPackageName(); 200 if (!checkCallerCanManageAssociationsForPackage(context, userId, packageName)) { 201 return null; 202 } 203 204 return association; 205 } 206 checkPackage(@serIdInt int uid, @NonNull String packageName)207 private static boolean checkPackage(@UserIdInt int uid, @NonNull String packageName) { 208 try { 209 return getAppOpsService().checkPackage(uid, packageName) == MODE_ALLOWED; 210 } catch (RemoteException e) { 211 // Can't happen: AppOpsManager is running in the same process. 212 return true; 213 } 214 } 215 getAppOpsService()216 private static IAppOpsService getAppOpsService() { 217 if (sAppOpsService == null) { 218 synchronized (PermissionsUtils.class) { 219 if (sAppOpsService == null) { 220 sAppOpsService = IAppOpsService.Stub.asInterface( 221 ServiceManager.getService(Context.APP_OPS_SERVICE)); 222 } 223 } 224 } 225 return sAppOpsService; 226 } 227 228 // DO NOT USE DIRECTLY! Access via getAppOpsService(). 229 private static IAppOpsService sAppOpsService = null; 230 PermissionsUtils()231 private PermissionsUtils() {} 232 } 233