1/* 2 * Copyright (C) 2011 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 17package com.android.server.usb; 18 19import android.annotation.NonNull; 20import android.app.PendingIntent; 21import android.content.ActivityNotFoundException; 22import android.content.Context; 23import android.content.Intent; 24import android.content.pm.ApplicationInfo; 25import android.content.pm.PackageManager; 26import android.content.pm.PackageManager.NameNotFoundException; 27import android.hardware.usb.UsbAccessory; 28import android.hardware.usb.UsbDevice; 29import android.hardware.usb.UsbManager; 30import android.os.Binder; 31import android.os.Process; 32import android.os.UserHandle; 33import android.util.Slog; 34import android.util.SparseBooleanArray; 35 36import com.android.internal.util.IndentingPrintWriter; 37 38import java.util.HashMap; 39 40class UsbUserSettingsManager { 41 private static final String TAG = "UsbUserSettingsManager"; 42 private static final boolean DEBUG = false; 43 44 private final UserHandle mUser; 45 private final boolean mDisablePermissionDialogs; 46 47 private final Context mUserContext; 48 private final PackageManager mPackageManager; 49 50 // Temporary mapping USB device name to list of UIDs with permissions for the device 51 private final HashMap<String, SparseBooleanArray> mDevicePermissionMap = 52 new HashMap<>(); 53 // Temporary mapping UsbAccessory to list of UIDs with permissions for the accessory 54 private final HashMap<UsbAccessory, SparseBooleanArray> mAccessoryPermissionMap = 55 new HashMap<>(); 56 57 private final Object mLock = new Object(); 58 59 public UsbUserSettingsManager(Context context, UserHandle user) { 60 if (DEBUG) Slog.v(TAG, "Creating settings for " + user); 61 62 try { 63 mUserContext = context.createPackageContextAsUser("android", 0, user); 64 } catch (NameNotFoundException e) { 65 throw new RuntimeException("Missing android package"); 66 } 67 68 mPackageManager = mUserContext.getPackageManager(); 69 70 mUser = user; 71 72 mDisablePermissionDialogs = context.getResources().getBoolean( 73 com.android.internal.R.bool.config_disableUsbPermissionDialogs); 74 } 75 76 /** 77 * Remove all access permission for a device. 78 * 79 * @param device The device the permissions are for 80 */ 81 void removeDevicePermissions(@NonNull UsbDevice device) { 82 synchronized (mLock) { 83 mDevicePermissionMap.remove(device.getDeviceName()); 84 } 85 } 86 87 /** 88 * Remove all access permission for a accessory. 89 * 90 * @param accessory The accessory the permissions are for 91 */ 92 void removeAccessoryPermissions(@NonNull UsbAccessory accessory) { 93 synchronized (mLock) { 94 mAccessoryPermissionMap.remove(accessory); 95 } 96 } 97 98 99 public boolean hasPermission(UsbDevice device) { 100 synchronized (mLock) { 101 int uid = Binder.getCallingUid(); 102 if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) { 103 return true; 104 } 105 SparseBooleanArray uidList = mDevicePermissionMap.get(device.getDeviceName()); 106 if (uidList == null) { 107 return false; 108 } 109 return uidList.get(uid); 110 } 111 } 112 113 public boolean hasPermission(UsbAccessory accessory) { 114 synchronized (mLock) { 115 int uid = Binder.getCallingUid(); 116 if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) { 117 return true; 118 } 119 SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory); 120 if (uidList == null) { 121 return false; 122 } 123 return uidList.get(uid); 124 } 125 } 126 127 public void checkPermission(UsbDevice device) { 128 if (!hasPermission(device)) { 129 throw new SecurityException("User has not given permission to device " + device); 130 } 131 } 132 133 public void checkPermission(UsbAccessory accessory) { 134 if (!hasPermission(accessory)) { 135 throw new SecurityException("User has not given permission to accessory " + accessory); 136 } 137 } 138 139 private void requestPermissionDialog(Intent intent, String packageName, PendingIntent pi) { 140 final int uid = Binder.getCallingUid(); 141 142 // compare uid with packageName to foil apps pretending to be someone else 143 try { 144 ApplicationInfo aInfo = mPackageManager.getApplicationInfo(packageName, 0); 145 if (aInfo.uid != uid) { 146 throw new IllegalArgumentException("package " + packageName + 147 " does not match caller's uid " + uid); 148 } 149 } catch (PackageManager.NameNotFoundException e) { 150 throw new IllegalArgumentException("package " + packageName + " not found"); 151 } 152 153 long identity = Binder.clearCallingIdentity(); 154 intent.setClassName("com.android.systemui", 155 "com.android.systemui.usb.UsbPermissionActivity"); 156 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 157 intent.putExtra(Intent.EXTRA_INTENT, pi); 158 intent.putExtra("package", packageName); 159 intent.putExtra(Intent.EXTRA_UID, uid); 160 try { 161 mUserContext.startActivityAsUser(intent, mUser); 162 } catch (ActivityNotFoundException e) { 163 Slog.e(TAG, "unable to start UsbPermissionActivity"); 164 } finally { 165 Binder.restoreCallingIdentity(identity); 166 } 167 } 168 169 public void requestPermission(UsbDevice device, String packageName, PendingIntent pi) { 170 Intent intent = new Intent(); 171 172 // respond immediately if permission has already been granted 173 if (hasPermission(device)) { 174 intent.putExtra(UsbManager.EXTRA_DEVICE, device); 175 intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true); 176 try { 177 pi.send(mUserContext, 0, intent); 178 } catch (PendingIntent.CanceledException e) { 179 if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled"); 180 } 181 return; 182 } 183 184 // start UsbPermissionActivity so user can choose an activity 185 intent.putExtra(UsbManager.EXTRA_DEVICE, device); 186 requestPermissionDialog(intent, packageName, pi); 187 } 188 189 public void requestPermission(UsbAccessory accessory, String packageName, PendingIntent pi) { 190 Intent intent = new Intent(); 191 192 // respond immediately if permission has already been granted 193 if (hasPermission(accessory)) { 194 intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory); 195 intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true); 196 try { 197 pi.send(mUserContext, 0, intent); 198 } catch (PendingIntent.CanceledException e) { 199 if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled"); 200 } 201 return; 202 } 203 204 intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory); 205 requestPermissionDialog(intent, packageName, pi); 206 } 207 208 public void grantDevicePermission(UsbDevice device, int uid) { 209 synchronized (mLock) { 210 String deviceName = device.getDeviceName(); 211 SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName); 212 if (uidList == null) { 213 uidList = new SparseBooleanArray(1); 214 mDevicePermissionMap.put(deviceName, uidList); 215 } 216 uidList.put(uid, true); 217 } 218 } 219 220 public void grantAccessoryPermission(UsbAccessory accessory, int uid) { 221 synchronized (mLock) { 222 SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory); 223 if (uidList == null) { 224 uidList = new SparseBooleanArray(1); 225 mAccessoryPermissionMap.put(accessory, uidList); 226 } 227 uidList.put(uid, true); 228 } 229 } 230 231 public void dump(IndentingPrintWriter pw) { 232 synchronized (mLock) { 233 pw.println("Device permissions:"); 234 for (String deviceName : mDevicePermissionMap.keySet()) { 235 pw.print(" " + deviceName + ": "); 236 SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName); 237 int count = uidList.size(); 238 for (int i = 0; i < count; i++) { 239 pw.print(Integer.toString(uidList.keyAt(i)) + " "); 240 } 241 pw.println(); 242 } 243 pw.println("Accessory permissions:"); 244 for (UsbAccessory accessory : mAccessoryPermissionMap.keySet()) { 245 pw.print(" " + accessory + ": "); 246 SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory); 247 int count = uidList.size(); 248 for (int i = 0; i < count; i++) { 249 pw.print(Integer.toString(uidList.keyAt(i)) + " "); 250 } 251 pw.println(); 252 } 253 } 254 } 255} 256