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 
17 package com.android.server.usb;
18 
19 import android.annotation.NonNull;
20 import android.app.PendingIntent;
21 import android.content.ActivityNotFoundException;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.pm.ApplicationInfo;
25 import android.content.pm.PackageManager;
26 import android.content.pm.PackageManager.NameNotFoundException;
27 import android.hardware.usb.UsbAccessory;
28 import android.hardware.usb.UsbConstants;
29 import android.hardware.usb.UsbDevice;
30 import android.hardware.usb.UsbInterface;
31 import android.hardware.usb.UsbManager;
32 import android.os.Binder;
33 import android.os.Process;
34 import android.os.UserHandle;
35 import android.service.usb.UsbSettingsAccessoryPermissionProto;
36 import android.service.usb.UsbSettingsDevicePermissionProto;
37 import android.service.usb.UsbUserSettingsManagerProto;
38 import android.util.Slog;
39 import android.util.SparseBooleanArray;
40 
41 import com.android.internal.util.dump.DualDumpOutputStream;
42 
43 import java.util.HashMap;
44 
45 class UsbUserSettingsManager {
46     private static final String TAG = "UsbUserSettingsManager";
47     private static final boolean DEBUG = false;
48 
49     private final UserHandle mUser;
50     private final boolean mDisablePermissionDialogs;
51 
52     private final Context mUserContext;
53     private final PackageManager mPackageManager;
54 
55     // Temporary mapping USB device name to list of UIDs with permissions for the device
56     private final HashMap<String, SparseBooleanArray> mDevicePermissionMap =
57             new HashMap<>();
58     // Temporary mapping UsbAccessory to list of UIDs with permissions for the accessory
59     private final HashMap<UsbAccessory, SparseBooleanArray> mAccessoryPermissionMap =
60             new HashMap<>();
61 
62     private final Object mLock = new Object();
63 
UsbUserSettingsManager(Context context, UserHandle user)64     public UsbUserSettingsManager(Context context, UserHandle user) {
65         if (DEBUG) Slog.v(TAG, "Creating settings for " + user);
66 
67         try {
68             mUserContext = context.createPackageContextAsUser("android", 0, user);
69         } catch (NameNotFoundException e) {
70             throw new RuntimeException("Missing android package");
71         }
72 
73         mPackageManager = mUserContext.getPackageManager();
74 
75         mUser = user;
76 
77         mDisablePermissionDialogs = context.getResources().getBoolean(
78                 com.android.internal.R.bool.config_disableUsbPermissionDialogs);
79     }
80 
81     /**
82      * Remove all access permission for a device.
83      *
84      * @param device The device the permissions are for
85      */
removeDevicePermissions(@onNull UsbDevice device)86     void removeDevicePermissions(@NonNull UsbDevice device) {
87         synchronized (mLock) {
88             mDevicePermissionMap.remove(device.getDeviceName());
89         }
90     }
91 
92     /**
93      * Remove all access permission for a accessory.
94      *
95      * @param accessory The accessory the permissions are for
96      */
removeAccessoryPermissions(@onNull UsbAccessory accessory)97     void removeAccessoryPermissions(@NonNull UsbAccessory accessory) {
98         synchronized (mLock) {
99             mAccessoryPermissionMap.remove(accessory);
100         }
101     }
102 
103     /**
104      * Check whether a particular device or any of its interfaces
105      * is of class VIDEO.
106      *
107      * @param device The device that needs to get scanned
108      * @return True in case a VIDEO device or interface is present,
109      *         False otherwise.
110      */
isCameraDevicePresent(UsbDevice device)111     private boolean isCameraDevicePresent(UsbDevice device) {
112         if (device.getDeviceClass() == UsbConstants.USB_CLASS_VIDEO) {
113             return true;
114         }
115 
116         for (int i = 0; i < device.getInterfaceCount(); i++) {
117             UsbInterface iface = device.getInterface(i);
118             if (iface.getInterfaceClass() == UsbConstants.USB_CLASS_VIDEO) {
119                 return true;
120             }
121         }
122 
123         return false;
124     }
125 
126     /**
127      * Check for camera permission of the calling process.
128      *
129      * @param packageName Package name of the caller.
130      * @param uid Linux uid of the calling process.
131      *
132      * @return True in case camera permission is available, False otherwise.
133      */
isCameraPermissionGranted(String packageName, int uid)134     private boolean isCameraPermissionGranted(String packageName, int uid) {
135         int targetSdkVersion = android.os.Build.VERSION_CODES.P;
136         try {
137             ApplicationInfo aInfo = mPackageManager.getApplicationInfo(packageName, 0);
138             // compare uid with packageName to foil apps pretending to be someone else
139             if (aInfo.uid != uid) {
140                 Slog.i(TAG, "Package " + packageName + " does not match caller's uid " + uid);
141                 return false;
142             }
143             targetSdkVersion = aInfo.targetSdkVersion;
144         } catch (PackageManager.NameNotFoundException e) {
145             Slog.i(TAG, "Package not found, likely due to invalid package name!");
146             return false;
147         }
148 
149         if (targetSdkVersion >= android.os.Build.VERSION_CODES.P) {
150             int allowed = mUserContext.checkCallingPermission(android.Manifest.permission.CAMERA);
151             if (android.content.pm.PackageManager.PERMISSION_DENIED == allowed) {
152                 Slog.i(TAG, "Camera permission required for USB video class devices");
153                 return false;
154             }
155         }
156 
157         return true;
158     }
159 
hasPermission(UsbDevice device, String packageName, int uid)160     public boolean hasPermission(UsbDevice device, String packageName, int uid) {
161         synchronized (mLock) {
162             if (isCameraDevicePresent(device)) {
163                 if (!isCameraPermissionGranted(packageName, uid)) {
164                     return false;
165                 }
166             }
167             if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) {
168                 return true;
169             }
170             SparseBooleanArray uidList = mDevicePermissionMap.get(device.getDeviceName());
171             if (uidList == null) {
172                 return false;
173             }
174             return uidList.get(uid);
175         }
176     }
177 
hasPermission(UsbAccessory accessory)178     public boolean hasPermission(UsbAccessory accessory) {
179         synchronized (mLock) {
180             int uid = Binder.getCallingUid();
181             if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) {
182                 return true;
183             }
184             SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
185             if (uidList == null) {
186                 return false;
187             }
188             return uidList.get(uid);
189         }
190     }
191 
checkPermission(UsbDevice device, String packageName, int uid)192     public void checkPermission(UsbDevice device, String packageName, int uid) {
193         if (!hasPermission(device, packageName, uid)) {
194             throw new SecurityException("User has not given permission to device " + device);
195         }
196     }
197 
checkPermission(UsbAccessory accessory)198     public void checkPermission(UsbAccessory accessory) {
199         if (!hasPermission(accessory)) {
200             throw new SecurityException("User has not given permission to accessory " + accessory);
201         }
202     }
203 
requestPermissionDialog(Intent intent, String packageName, PendingIntent pi)204     private void requestPermissionDialog(Intent intent, String packageName, PendingIntent pi) {
205         final int uid = Binder.getCallingUid();
206 
207         // compare uid with packageName to foil apps pretending to be someone else
208         try {
209             ApplicationInfo aInfo = mPackageManager.getApplicationInfo(packageName, 0);
210             if (aInfo.uid != uid) {
211                 throw new IllegalArgumentException("package " + packageName +
212                         " does not match caller's uid " + uid);
213             }
214         } catch (PackageManager.NameNotFoundException e) {
215             throw new IllegalArgumentException("package " + packageName + " not found");
216         }
217 
218         long identity = Binder.clearCallingIdentity();
219         intent.setClassName("com.android.systemui",
220                 "com.android.systemui.usb.UsbPermissionActivity");
221         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
222         intent.putExtra(Intent.EXTRA_INTENT, pi);
223         intent.putExtra("package", packageName);
224         intent.putExtra(Intent.EXTRA_UID, uid);
225         try {
226             mUserContext.startActivityAsUser(intent, mUser);
227         } catch (ActivityNotFoundException e) {
228             Slog.e(TAG, "unable to start UsbPermissionActivity");
229         } finally {
230             Binder.restoreCallingIdentity(identity);
231         }
232     }
233 
requestPermission(UsbDevice device, String packageName, PendingIntent pi, int uid)234     public void requestPermission(UsbDevice device, String packageName, PendingIntent pi, int uid) {
235       Intent intent = new Intent();
236 
237         // respond immediately if permission has already been granted
238       if (hasPermission(device, packageName, uid)) {
239             intent.putExtra(UsbManager.EXTRA_DEVICE, device);
240             intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true);
241             try {
242                 pi.send(mUserContext, 0, intent);
243             } catch (PendingIntent.CanceledException e) {
244                 if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled");
245             }
246             return;
247         }
248         if (isCameraDevicePresent(device)) {
249             if (!isCameraPermissionGranted(packageName, uid)) {
250                 intent.putExtra(UsbManager.EXTRA_DEVICE, device);
251                 intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false);
252                 try {
253                     pi.send(mUserContext, 0, intent);
254                 } catch (PendingIntent.CanceledException e) {
255                     if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled");
256                 }
257                 return;
258             }
259         }
260 
261         // start UsbPermissionActivity so user can choose an activity
262         intent.putExtra(UsbManager.EXTRA_DEVICE, device);
263         requestPermissionDialog(intent, packageName, pi);
264     }
265 
requestPermission(UsbAccessory accessory, String packageName, PendingIntent pi)266     public void requestPermission(UsbAccessory accessory, String packageName, PendingIntent pi) {
267         Intent intent = new Intent();
268 
269         // respond immediately if permission has already been granted
270         if (hasPermission(accessory)) {
271             intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
272             intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true);
273             try {
274                 pi.send(mUserContext, 0, intent);
275             } catch (PendingIntent.CanceledException e) {
276                 if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled");
277             }
278             return;
279         }
280 
281         intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
282         requestPermissionDialog(intent, packageName, pi);
283     }
284 
grantDevicePermission(UsbDevice device, int uid)285     public void grantDevicePermission(UsbDevice device, int uid) {
286         synchronized (mLock) {
287             String deviceName = device.getDeviceName();
288             SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName);
289             if (uidList == null) {
290                 uidList = new SparseBooleanArray(1);
291                 mDevicePermissionMap.put(deviceName, uidList);
292             }
293             uidList.put(uid, true);
294         }
295     }
296 
grantAccessoryPermission(UsbAccessory accessory, int uid)297     public void grantAccessoryPermission(UsbAccessory accessory, int uid) {
298         synchronized (mLock) {
299             SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
300             if (uidList == null) {
301                 uidList = new SparseBooleanArray(1);
302                 mAccessoryPermissionMap.put(accessory, uidList);
303             }
304             uidList.put(uid, true);
305         }
306     }
307 
dump(@onNull DualDumpOutputStream dump, @NonNull String idName, long id)308     public void dump(@NonNull DualDumpOutputStream dump, @NonNull String idName, long id) {
309         long token = dump.start(idName, id);
310 
311         synchronized (mLock) {
312             dump.write("user_id", UsbUserSettingsManagerProto.USER_ID, mUser.getIdentifier());
313 
314             for (String deviceName : mDevicePermissionMap.keySet()) {
315                 long devicePermissionToken = dump.start("device_permissions",
316                         UsbUserSettingsManagerProto.DEVICE_PERMISSIONS);
317 
318                 dump.write("device_name", UsbSettingsDevicePermissionProto.DEVICE_NAME, deviceName);
319 
320                 SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName);
321                 int count = uidList.size();
322                 for (int i = 0; i < count; i++) {
323                     dump.write("uids", UsbSettingsDevicePermissionProto.UIDS, uidList.keyAt(i));
324                 }
325 
326                 dump.end(devicePermissionToken);
327             }
328             for (UsbAccessory accessory : mAccessoryPermissionMap.keySet()) {
329                 long accessoryPermissionToken = dump.start("accessory_permissions",
330                         UsbUserSettingsManagerProto.ACCESSORY_PERMISSIONS);
331 
332                 dump.write("accessory_description",
333                         UsbSettingsAccessoryPermissionProto.ACCESSORY_DESCRIPTION,
334                         accessory.getDescription());
335 
336                 SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
337                 int count = uidList.size();
338                 for (int i = 0; i < count; i++) {
339                     dump.write("uids", UsbSettingsAccessoryPermissionProto.UIDS, uidList.keyAt(i));
340                 }
341 
342                 dump.end(accessoryPermissionToken);
343             }
344         }
345 
346         dump.end(token);
347     }
348 }
349