1 /*
2  * Copyright (C) 2018 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.annotation.Nullable;
21 import android.app.PendingIntent;
22 import android.content.ActivityNotFoundException;
23 import android.content.ComponentName;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.pm.ApplicationInfo;
27 import android.content.pm.PackageManager;
28 import android.hardware.SensorPrivacyManager.Sensors;
29 import android.hardware.SensorPrivacyManagerInternal;
30 import android.hardware.usb.AccessoryFilter;
31 import android.hardware.usb.DeviceFilter;
32 import android.hardware.usb.UsbAccessory;
33 import android.hardware.usb.UsbDevice;
34 import android.hardware.usb.UsbManager;
35 import android.os.AsyncTask;
36 import android.os.Binder;
37 import android.os.Environment;
38 import android.os.Process;
39 import android.os.UserHandle;
40 import android.service.usb.UsbAccessoryPermissionProto;
41 import android.service.usb.UsbAccessoryPersistentPermissionProto;
42 import android.service.usb.UsbDevicePermissionProto;
43 import android.service.usb.UsbDevicePersistentPermissionProto;
44 import android.service.usb.UsbUidPermissionProto;
45 import android.service.usb.UsbUserPermissionsManagerProto;
46 import android.util.ArrayMap;
47 import android.util.AtomicFile;
48 import android.util.EventLog;
49 import android.util.Slog;
50 import android.util.SparseBooleanArray;
51 import android.util.Xml;
52 
53 import com.android.internal.annotations.GuardedBy;
54 import com.android.internal.util.XmlUtils;
55 import com.android.internal.util.dump.DualDumpOutputStream;
56 import com.android.modules.utils.TypedXmlPullParser;
57 import com.android.modules.utils.TypedXmlSerializer;
58 import com.android.server.LocalServices;
59 
60 import org.xmlpull.v1.XmlPullParser;
61 import org.xmlpull.v1.XmlPullParserException;
62 
63 import java.io.File;
64 import java.io.FileInputStream;
65 import java.io.FileNotFoundException;
66 import java.io.FileOutputStream;
67 import java.io.IOException;
68 
69 /**
70  * UsbUserPermissionManager manages usb device or accessory access permissions.
71  *
72  * @hide
73  */
74 class UsbUserPermissionManager {
75     private static final String TAG = UsbUserPermissionManager.class.getSimpleName();
76     private static final boolean DEBUG = false;
77 
78     private static final int SNET_EVENT_LOG_ID = 0x534e4554;
79 
80     @GuardedBy("mLock")
81     /** Mapping of USB device name to list of UIDs with permissions for the device
82      * Each entry lasts until device is disconnected*/
83     private final ArrayMap<String, SparseBooleanArray> mDevicePermissionMap =
84             new ArrayMap<>();
85     @GuardedBy("mLock")
86     /** Temporary mapping UsbAccessory to list of UIDs with permissions for the accessory
87      * Each entry lasts until accessory is disconnected*/
88     private final ArrayMap<UsbAccessory, SparseBooleanArray> mAccessoryPermissionMap =
89             new ArrayMap<>();
90 
91     @GuardedBy("mLock")
92     /** Maps USB device to list of UIDs with persistent permissions for the device*/
93     private final ArrayMap<DeviceFilter, SparseBooleanArray>
94             mDevicePersistentPermissionMap = new ArrayMap<>();
95     @GuardedBy("mLock")
96     /** Maps Usb Accessory to list of UIDs with persistent permissions for the accessory*/
97     private final ArrayMap<AccessoryFilter, SparseBooleanArray>
98             mAccessoryPersistentPermissionMap = new ArrayMap<>();
99 
100     private final Context mContext;
101     private final UserHandle mUser;
102     private final UsbUserSettingsManager mUsbUserSettingsManager;
103     private final boolean mDisablePermissionDialogs;
104 
105     private final @NonNull AtomicFile mPermissionsFile;
106 
107     private final Object mLock = new Object();
108 
109     /**
110      * If a async task to persist the mDevicePersistentPreferenceMap and
111      * mAccessoryPersistentPreferenceMap is currently scheduled.
112      */
113     @GuardedBy("mLock")
114     private boolean mIsCopyPermissionsScheduled;
115     private final SensorPrivacyManagerInternal mSensorPrivacyMgrInternal;
116 
UsbUserPermissionManager(@onNull Context context, @NonNull UsbUserSettingsManager usbUserSettingsManager)117     UsbUserPermissionManager(@NonNull Context context,
118             @NonNull UsbUserSettingsManager usbUserSettingsManager) {
119         mContext = context;
120         mUser = context.getUser();
121         mUsbUserSettingsManager = usbUserSettingsManager;
122         mSensorPrivacyMgrInternal = LocalServices.getService(SensorPrivacyManagerInternal.class);
123         mDisablePermissionDialogs = context.getResources().getBoolean(
124                 com.android.internal.R.bool.config_disableUsbPermissionDialogs);
125 
126         mPermissionsFile = new AtomicFile(new File(
127                 Environment.getUserSystemDirectory(mUser.getIdentifier()),
128                 "usb_permissions.xml"), "usb-permissions");
129         synchronized (mLock) {
130             readPermissionsLocked();
131         }
132     }
133 
134     /**
135      * Removes access permissions of all packages for the USB accessory.
136      *
137      * @param accessory to remove permissions for
138      */
removeAccessoryPermissions(@onNull UsbAccessory accessory)139     void removeAccessoryPermissions(@NonNull UsbAccessory accessory) {
140         synchronized (mLock) {
141             mAccessoryPermissionMap.remove(accessory);
142         }
143     }
144 
145     /**
146      * Removes access permissions of all packages for the USB device.
147      *
148      * @param device to remove permissions for
149      */
removeDevicePermissions(@onNull UsbDevice device)150     void removeDevicePermissions(@NonNull UsbDevice device) {
151         synchronized (mLock) {
152             mDevicePermissionMap.remove(device.getDeviceName());
153         }
154     }
155 
156     /**
157      * Grants permission for USB device without showing system dialog for package with uid.
158      *
159      * @param device to grant permission for
160      * @param uid to grant permission for
161      */
grantDevicePermission(@onNull UsbDevice device, int uid)162     void grantDevicePermission(@NonNull UsbDevice device, int uid) {
163         synchronized (mLock) {
164             String deviceName = device.getDeviceName();
165             SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName);
166             if (uidList == null) {
167                 uidList = new SparseBooleanArray(1);
168                 mDevicePermissionMap.put(deviceName, uidList);
169             }
170             uidList.put(uid, true);
171         }
172     }
173 
174     /**
175      * Grants permission for USB accessory without showing system dialog for package with uid.
176      *
177      * @param accessory to grant permission for
178      * @param uid to grant permission for
179      */
grantAccessoryPermission(@onNull UsbAccessory accessory, int uid)180     void grantAccessoryPermission(@NonNull UsbAccessory accessory, int uid) {
181         synchronized (mLock) {
182             SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
183             if (uidList == null) {
184                 uidList = new SparseBooleanArray(1);
185                 mAccessoryPermissionMap.put(accessory, uidList);
186             }
187             uidList.put(uid, true);
188         }
189     }
190 
191     /**
192      * Returns true if package with uid has permission to access the device.
193      *
194      * @param device to check permission for
195      * @param pid to check permission for
196      * @param uid to check permission for
197      * @return {@code true} if package with uid has permission
198      */
hasPermission(@onNull UsbDevice device, @NonNull String packageName, int pid, int uid)199     boolean hasPermission(@NonNull UsbDevice device, @NonNull String packageName, int pid,
200             int uid) {
201         if (device.getHasVideoCapture()) {
202             boolean isCameraPrivacyEnabled = mSensorPrivacyMgrInternal.isSensorPrivacyEnabled(
203                     UserHandle.getUserId(uid), Sensors.CAMERA);
204             if (DEBUG) {
205                 Slog.d(TAG, "isCameraPrivacyEnabled: " + isCameraPrivacyEnabled);
206             }
207             if (isCameraPrivacyEnabled || !isCameraPermissionGranted(packageName, pid, uid)) {
208                 return false;
209             }
210         }
211         // Only check for microphone privacy and not RECORD_AUDIO permission, because access to usb
212         // camera device with audio recording capabilities may still be granted with a warning
213         if (device.getHasAudioCapture() && mSensorPrivacyMgrInternal.isSensorPrivacyEnabled(
214                 UserHandle.getUserId(uid), Sensors.MICROPHONE)) {
215             if (DEBUG) {
216                 Slog.d(TAG,
217                         "Access to device with audio recording capabilities denied because "
218                                 + "microphone privacy is enabled.");
219             }
220             return false;
221         }
222         synchronized (mLock) {
223             if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) {
224                 return true;
225             }
226             DeviceFilter filter = new DeviceFilter(device);
227             SparseBooleanArray permissionsForDevice = mDevicePersistentPermissionMap.get(filter);
228             if (permissionsForDevice != null) {
229                 int idx = permissionsForDevice.indexOfKey(uid);
230                 if (idx >= 0) {
231                     return permissionsForDevice.valueAt(idx);
232                 }
233             }
234             SparseBooleanArray uidList = mDevicePermissionMap.get(device.getDeviceName());
235             if (uidList == null) {
236                 return false;
237             }
238             return uidList.get(uid);
239         }
240     }
241 
242     /**
243      * Returns true if caller has permission to access the accessory.
244      *
245      * @param accessory to check permission for
246      * @param uid to check permission for
247      * @return {@code true} if caller has permssion
248      */
hasPermission(@onNull UsbAccessory accessory, int pid, int uid)249     boolean hasPermission(@NonNull UsbAccessory accessory, int pid, int uid) {
250         synchronized (mLock) {
251             if (uid == Process.SYSTEM_UID
252                     || mDisablePermissionDialogs
253                     || mContext.checkPermission(
254                         android.Manifest.permission.MANAGE_USB, pid, uid)
255                          == android.content.pm.PackageManager.PERMISSION_GRANTED) {
256                 return true;
257             }
258             AccessoryFilter filter = new AccessoryFilter(accessory);
259             SparseBooleanArray permissionsForAccessory =
260                     mAccessoryPersistentPermissionMap.get(filter);
261             if (permissionsForAccessory != null) {
262                 int idx = permissionsForAccessory.indexOfKey(uid);
263                 if (idx >= 0) {
264                     return permissionsForAccessory.valueAt(idx);
265                 }
266             }
267             SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
268             if (uidList == null) {
269                 return false;
270             }
271             return uidList.get(uid);
272         }
273     }
274 
setDevicePersistentPermission(@onNull UsbDevice device, int uid, boolean isGranted)275     void setDevicePersistentPermission(@NonNull UsbDevice device, int uid, boolean isGranted) {
276 
277         boolean isChanged;
278         DeviceFilter filter = new DeviceFilter(device);
279         synchronized (mLock) {
280             SparseBooleanArray permissionsForDevice = mDevicePersistentPermissionMap.get(filter);
281             if (permissionsForDevice == null) {
282                 permissionsForDevice = new SparseBooleanArray();
283                 mDevicePersistentPermissionMap.put(filter, permissionsForDevice);
284             }
285             int idx = permissionsForDevice.indexOfKey(uid);
286             if (idx >= 0) {
287                 isChanged = permissionsForDevice.valueAt(idx) != isGranted;
288                 permissionsForDevice.setValueAt(idx, isGranted);
289             } else {
290                 isChanged = true;
291                 permissionsForDevice.put(uid, isGranted);
292             }
293 
294             if (isChanged) {
295                 scheduleWritePermissionsLocked();
296             }
297         }
298     }
299 
setAccessoryPersistentPermission(@onNull UsbAccessory accessory, int uid, boolean isGranted)300     void setAccessoryPersistentPermission(@NonNull UsbAccessory accessory, int uid,
301             boolean isGranted) {
302 
303         boolean isChanged;
304         AccessoryFilter filter = new AccessoryFilter(accessory);
305         synchronized (mLock) {
306             SparseBooleanArray permissionsForAccessory =
307                     mAccessoryPersistentPermissionMap.get(filter);
308             if (permissionsForAccessory == null) {
309                 permissionsForAccessory = new SparseBooleanArray();
310                 mAccessoryPersistentPermissionMap.put(filter, permissionsForAccessory);
311             }
312             int idx = permissionsForAccessory.indexOfKey(uid);
313             if (idx >= 0) {
314                 isChanged = permissionsForAccessory.valueAt(idx) != isGranted;
315                 permissionsForAccessory.setValueAt(idx, isGranted);
316             } else {
317                 isChanged = true;
318                 permissionsForAccessory.put(uid, isGranted);
319             }
320 
321             if (isChanged) {
322                 scheduleWritePermissionsLocked();
323             }
324         }
325     }
326 
readPermission(@onNull XmlPullParser parser)327     private void readPermission(@NonNull XmlPullParser parser) throws XmlPullParserException,
328             IOException {
329         int uid;
330         boolean isGranted;
331 
332         try {
333             uid = XmlUtils.readIntAttribute(parser, "uid");
334         } catch (NumberFormatException e) {
335             Slog.e(TAG, "error reading usb permission uid", e);
336             XmlUtils.skipCurrentTag(parser);
337             return;
338         }
339 
340         // only use "true"/"false" as valid values
341         String isGrantedString = parser.getAttributeValue(null, "granted");
342         if (isGrantedString == null || !(isGrantedString.equals(Boolean.TRUE.toString())
343                 || isGrantedString.equals(Boolean.FALSE.toString()))) {
344             Slog.e(TAG, "error reading usb permission granted state");
345             XmlUtils.skipCurrentTag(parser);
346             return;
347         }
348         isGranted = isGrantedString.equals(Boolean.TRUE.toString());
349         XmlUtils.nextElement(parser);
350         if ("usb-device".equals(parser.getName())) {
351             DeviceFilter filter = DeviceFilter.read(parser);
352             int idx = mDevicePersistentPermissionMap.indexOfKey(filter);
353             if (idx >= 0) {
354                 SparseBooleanArray permissionsForDevice =
355                         mDevicePersistentPermissionMap.valueAt(idx);
356                 permissionsForDevice.put(uid, isGranted);
357             } else {
358                 SparseBooleanArray permissionsForDevice = new SparseBooleanArray();
359                 mDevicePersistentPermissionMap.put(filter, permissionsForDevice);
360                 permissionsForDevice.put(uid, isGranted);
361             }
362         } else if ("usb-accessory".equals(parser.getName())) {
363             AccessoryFilter filter = AccessoryFilter.read(parser);
364             int idx = mAccessoryPersistentPermissionMap.indexOfKey(filter);
365             if (idx >= 0) {
366                 SparseBooleanArray permissionsForAccessory =
367                         mAccessoryPersistentPermissionMap.valueAt(idx);
368                 permissionsForAccessory.put(uid, isGranted);
369             } else {
370                 SparseBooleanArray permissionsForAccessory = new SparseBooleanArray();
371                 mAccessoryPersistentPermissionMap.put(filter, permissionsForAccessory);
372                 permissionsForAccessory.put(uid, isGranted);
373             }
374         }
375     }
376 
377     @GuardedBy("mLock")
readPermissionsLocked()378     private void readPermissionsLocked() {
379         mDevicePersistentPermissionMap.clear();
380         mAccessoryPersistentPermissionMap.clear();
381 
382         try (FileInputStream in = mPermissionsFile.openRead()) {
383             TypedXmlPullParser parser = Xml.resolvePullParser(in);
384 
385             XmlUtils.nextElement(parser);
386             while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
387                 String tagName = parser.getName();
388                 if ("permission".equals(tagName)) {
389                     readPermission(parser);
390                 } else {
391                     XmlUtils.nextElement(parser);
392                 }
393             }
394         } catch (FileNotFoundException e) {
395             if (DEBUG) Slog.d(TAG, "usb permissions file not found");
396         } catch (Exception e) {
397             Slog.e(TAG, "error reading usb permissions file, deleting to start fresh", e);
398             mPermissionsFile.delete();
399         }
400     }
401 
402     @GuardedBy("mLock")
scheduleWritePermissionsLocked()403     private void scheduleWritePermissionsLocked() {
404         if (mIsCopyPermissionsScheduled) {
405             return;
406         }
407         mIsCopyPermissionsScheduled = true;
408 
409         AsyncTask.execute(() -> {
410             int numDevices;
411             DeviceFilter[] devices;
412             int[][] uidsForDevices;
413             boolean[][] grantedValuesForDevices;
414 
415             int numAccessories;
416             AccessoryFilter[] accessories;
417             int[][] uidsForAccessories;
418             boolean[][] grantedValuesForAccessories;
419 
420             synchronized (mLock) {
421                 // Copy the permission state so we can write outside of lock
422                 numDevices = mDevicePersistentPermissionMap.size();
423                 devices = new DeviceFilter[numDevices];
424                 uidsForDevices = new int[numDevices][];
425                 grantedValuesForDevices = new boolean[numDevices][];
426                 for (int deviceIdx = 0; deviceIdx < numDevices; deviceIdx++) {
427                     devices[deviceIdx] =
428                             new DeviceFilter(mDevicePersistentPermissionMap.keyAt(deviceIdx));
429                     SparseBooleanArray permissions =
430                             mDevicePersistentPermissionMap.valueAt(deviceIdx);
431                     int numPermissions = permissions.size();
432                     uidsForDevices[deviceIdx] = new int[numPermissions];
433                     grantedValuesForDevices[deviceIdx] = new boolean[numPermissions];
434                     for (int permissionIdx = 0; permissionIdx < numPermissions; permissionIdx++) {
435                         uidsForDevices[deviceIdx][permissionIdx] = permissions.keyAt(permissionIdx);
436                         grantedValuesForDevices[deviceIdx][permissionIdx] =
437                                 permissions.valueAt(permissionIdx);
438                     }
439                 }
440 
441                 numAccessories = mAccessoryPersistentPermissionMap.size();
442                 accessories = new AccessoryFilter[numAccessories];
443                 uidsForAccessories = new int[numAccessories][];
444                 grantedValuesForAccessories = new boolean[numAccessories][];
445                 for (int accessoryIdx = 0; accessoryIdx < numAccessories; accessoryIdx++) {
446                     accessories[accessoryIdx] = new AccessoryFilter(
447                                     mAccessoryPersistentPermissionMap.keyAt(accessoryIdx));
448                     SparseBooleanArray permissions =
449                             mAccessoryPersistentPermissionMap.valueAt(accessoryIdx);
450                     int numPermissions = permissions.size();
451                     uidsForAccessories[accessoryIdx] = new int[numPermissions];
452                     grantedValuesForAccessories[accessoryIdx] = new boolean[numPermissions];
453                     for (int permissionIdx = 0; permissionIdx < numPermissions; permissionIdx++) {
454                         uidsForAccessories[accessoryIdx][permissionIdx] =
455                                 permissions.keyAt(permissionIdx);
456                         grantedValuesForAccessories[accessoryIdx][permissionIdx] =
457                                 permissions.valueAt(permissionIdx);
458                     }
459                 }
460                 mIsCopyPermissionsScheduled = false;
461             }
462 
463             synchronized (mPermissionsFile) {
464                 FileOutputStream out = null;
465                 try {
466                     out = mPermissionsFile.startWrite();
467                     TypedXmlSerializer serializer = Xml.resolveSerializer(out);
468                     serializer.startDocument(null, true);
469                     serializer.startTag(null, "permissions");
470 
471                     for (int i = 0; i < numDevices; i++) {
472                         int numPermissions = uidsForDevices[i].length;
473                         for (int j = 0; j < numPermissions; j++) {
474                             serializer.startTag(null, "permission");
475                             serializer.attribute(null, "uid",
476                                     Integer.toString(uidsForDevices[i][j]));
477                             serializer.attribute(null, "granted",
478                                     Boolean.toString(grantedValuesForDevices[i][j]));
479                             devices[i].write(serializer);
480                             serializer.endTag(null, "permission");
481                         }
482                     }
483 
484                     for (int i = 0; i < numAccessories; i++) {
485                         int numPermissions = uidsForAccessories[i].length;
486                         for (int j = 0; j < numPermissions; j++) {
487                             serializer.startTag(null, "permission");
488                             serializer.attribute(null, "uid",
489                                     Integer.toString(uidsForAccessories[i][j]));
490                             serializer.attribute(null, "granted",
491                                     Boolean.toString(grantedValuesForDevices[i][j]));
492                             accessories[i].write(serializer);
493                             serializer.endTag(null, "permission");
494                         }
495                     }
496 
497                     serializer.endTag(null, "permissions");
498                     serializer.endDocument();
499 
500                     mPermissionsFile.finishWrite(out);
501                 } catch (IOException e) {
502                     Slog.e(TAG, "Failed to write permissions", e);
503                     if (out != null) {
504                         mPermissionsFile.failWrite(out);
505                     }
506                 }
507             }
508         });
509     }
510 
511     /**
512      * Creates UI dialog to request permission for the given package to access the device
513      * or accessory.
514      *
515      * @param device       The USB device attached
516      * @param accessory    The USB accessory attached
517      * @param canBeDefault Whether the calling pacakge can set as default handler
518      *                     of the USB device or accessory
519      * @param packageName  The package name of the calling package
520      * @param uid          The uid of the calling package
521      * @param userContext  The context to start the UI dialog
522      * @param pi           PendingIntent for returning result
523      */
requestPermissionDialog(@ullable UsbDevice device, @Nullable UsbAccessory accessory, boolean canBeDefault, @NonNull String packageName, int uid, @NonNull Context userContext, @NonNull PendingIntent pi)524     void requestPermissionDialog(@Nullable UsbDevice device,
525             @Nullable UsbAccessory accessory,
526             boolean canBeDefault,
527             @NonNull String packageName,
528             int uid,
529             @NonNull Context userContext,
530             @NonNull PendingIntent pi) {
531         final long identity = Binder.clearCallingIdentity();
532         try {
533             Intent intent = new Intent();
534             if (device != null) {
535                 intent.putExtra(UsbManager.EXTRA_DEVICE, device);
536             } else {
537                 intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
538             }
539             intent.putExtra(Intent.EXTRA_INTENT, pi);
540             intent.putExtra(Intent.EXTRA_UID, uid);
541             intent.putExtra(UsbManager.EXTRA_CAN_BE_DEFAULT, canBeDefault);
542             intent.putExtra(UsbManager.EXTRA_PACKAGE, packageName);
543             intent.setComponent(
544                     ComponentName.unflattenFromString(userContext.getResources().getString(
545                             com.android.internal.R.string.config_usbPermissionActivity)));
546             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
547 
548             userContext.startActivityAsUser(intent, mUser);
549         } catch (ActivityNotFoundException e) {
550             Slog.e(TAG, "unable to start UsbPermissionActivity");
551         } finally {
552             Binder.restoreCallingIdentity(identity);
553         }
554     }
555 
dump(@onNull DualDumpOutputStream dump, String idName, long id)556     void dump(@NonNull DualDumpOutputStream dump, String idName, long id) {
557         long token = dump.start(idName, id);
558         synchronized (mLock) {
559             dump.write("user_id", UsbUserPermissionsManagerProto.USER_ID, mUser.getIdentifier());
560             int numMappings = mDevicePermissionMap.size();
561             for (int mappingsIdx = 0; mappingsIdx < numMappings; mappingsIdx++) {
562                 String deviceName = mDevicePermissionMap.keyAt(mappingsIdx);
563                 long devicePermissionToken = dump.start("device_permissions",
564                         UsbUserPermissionsManagerProto.DEVICE_PERMISSIONS);
565 
566                 dump.write("device_name", UsbDevicePermissionProto.DEVICE_NAME, deviceName);
567 
568                 SparseBooleanArray uidList = mDevicePermissionMap.valueAt(mappingsIdx);
569                 int numUids = uidList.size();
570                 for (int uidsIdx = 0; uidsIdx < numUids; uidsIdx++) {
571                     dump.write("uids", UsbDevicePermissionProto.UIDS, uidList.keyAt(uidsIdx));
572                 }
573 
574                 dump.end(devicePermissionToken);
575             }
576 
577             numMappings = mAccessoryPermissionMap.size();
578             for (int mappingsIdx = 0; mappingsIdx < numMappings; ++mappingsIdx) {
579                 UsbAccessory accessory = mAccessoryPermissionMap.keyAt(mappingsIdx);
580                 long accessoryPermissionToken = dump.start("accessory_permissions",
581                         UsbUserPermissionsManagerProto.ACCESSORY_PERMISSIONS);
582 
583                 dump.write("accessory_description",
584                         UsbAccessoryPermissionProto.ACCESSORY_DESCRIPTION,
585                         accessory.getDescription());
586 
587                 SparseBooleanArray uidList = mAccessoryPermissionMap.valueAt(mappingsIdx);
588                 int numUids = uidList.size();
589                 for (int uidsIdx = 0; uidsIdx < numUids; uidsIdx++) {
590                     dump.write("uids", UsbAccessoryPermissionProto.UIDS, uidList.keyAt(uidsIdx));
591                 }
592 
593                 dump.end(accessoryPermissionToken);
594             }
595 
596             numMappings = mDevicePersistentPermissionMap.size();
597             for (int mappingsIdx = 0; mappingsIdx < numMappings; mappingsIdx++) {
598                 DeviceFilter filter = mDevicePersistentPermissionMap.keyAt(mappingsIdx);
599                 long devicePermissionToken = dump.start("device_persistent_permissions",
600                         UsbUserPermissionsManagerProto.DEVICE_PERSISTENT_PERMISSIONS);
601                 filter.dump(dump, "device",
602                         UsbDevicePersistentPermissionProto.DEVICE_FILTER);
603                 SparseBooleanArray permissions =
604                         mDevicePersistentPermissionMap.valueAt(mappingsIdx);
605                 int numPermissions = permissions.size();
606                 for (int permissionsIdx = 0; permissionsIdx < numPermissions; permissionsIdx++) {
607                     long uidPermissionToken = dump.start("uid_permission",
608                             UsbDevicePersistentPermissionProto.PERMISSION_VALUES);
609                     dump.write("uid", UsbUidPermissionProto.UID, permissions.keyAt(permissionsIdx));
610                     dump.write("is_granted",
611                             UsbUidPermissionProto.IS_GRANTED, permissions.valueAt(permissionsIdx));
612                     dump.end(uidPermissionToken);
613                 }
614                 dump.end(devicePermissionToken);
615             }
616 
617             numMappings = mAccessoryPersistentPermissionMap.size();
618             for (int mappingsIdx = 0; mappingsIdx < numMappings; mappingsIdx++) {
619                 AccessoryFilter filter = mAccessoryPersistentPermissionMap.keyAt(mappingsIdx);
620                 long accessoryPermissionToken = dump.start("accessory_persistent_permissions",
621                         UsbUserPermissionsManagerProto.ACCESSORY_PERSISTENT_PERMISSIONS);
622                 filter.dump(dump, "accessory",
623                         UsbAccessoryPersistentPermissionProto.ACCESSORY_FILTER);
624                 SparseBooleanArray permissions =
625                         mAccessoryPersistentPermissionMap.valueAt(mappingsIdx);
626                 int numPermissions = permissions.size();
627                 for (int permissionsIdx = 0; permissionsIdx < numPermissions; permissionsIdx++) {
628                     long uidPermissionToken = dump.start("uid_permission",
629                             UsbAccessoryPersistentPermissionProto.PERMISSION_VALUES);
630                     dump.write("uid", UsbUidPermissionProto.UID, permissions.keyAt(permissionsIdx));
631                     dump.write("is_granted",
632                             UsbUidPermissionProto.IS_GRANTED, permissions.valueAt(permissionsIdx));
633                     dump.end(uidPermissionToken);
634                 }
635                 dump.end(accessoryPermissionToken);
636             }
637         }
638         dump.end(token);
639     }
640 
641     /**
642      * Check for camera permission of the calling process.
643      *
644      * @param packageName Package name of the caller.
645      * @param pid         Linux pid of the calling process.
646      * @param uid         Linux uid of the calling process.
647      * @return True in case camera permission is available, False otherwise.
648      */
isCameraPermissionGranted(String packageName, int pid, int uid)649     private boolean isCameraPermissionGranted(String packageName, int pid, int uid) {
650         int targetSdkVersion = android.os.Build.VERSION_CODES.P;
651         try {
652             ApplicationInfo aInfo = mContext.getPackageManager().getApplicationInfo(packageName, 0);
653             // compare uid with packageName to foil apps pretending to be someone else
654             if (aInfo.uid != uid) {
655                 Slog.i(TAG, "Package " + packageName + " does not match caller's uid " + uid);
656                 return false;
657             }
658             targetSdkVersion = aInfo.targetSdkVersion;
659         } catch (PackageManager.NameNotFoundException e) {
660             Slog.i(TAG, "Package not found, likely due to invalid package name!");
661             return false;
662         }
663 
664         if (targetSdkVersion >= android.os.Build.VERSION_CODES.P) {
665             int allowed = mContext.checkPermission(android.Manifest.permission.CAMERA, pid, uid);
666             if (android.content.pm.PackageManager.PERMISSION_DENIED == allowed) {
667                 Slog.i(TAG, "Camera permission required for USB video class devices");
668                 return false;
669             }
670         }
671 
672         return true;
673     }
674 
checkPermission(UsbDevice device, String packageName, int pid, int uid)675     public void checkPermission(UsbDevice device, String packageName, int pid, int uid) {
676         if (!hasPermission(device, packageName, pid, uid)) {
677             throw new SecurityException("User has not given " + uid + "/" + packageName
678                     + " permission to access device " + device.getDeviceName());
679         }
680     }
681 
checkPermission(UsbAccessory accessory, int pid, int uid)682     public void checkPermission(UsbAccessory accessory, int pid, int uid) {
683         if (!hasPermission(accessory, pid, uid)) {
684             throw new SecurityException("User has not given " + uid + " permission to accessory "
685                     + accessory);
686         }
687     }
688 
requestPermissionDialog(@ullable UsbDevice device, @Nullable UsbAccessory accessory, boolean canBeDefault, String packageName, PendingIntent pi, int uid)689     private void requestPermissionDialog(@Nullable UsbDevice device,
690             @Nullable UsbAccessory accessory,
691             boolean canBeDefault,
692             String packageName,
693             PendingIntent pi,
694             int uid) {
695         boolean throwException = false;
696 
697         // compare uid with packageName to foil apps pretending to be someone else
698         try {
699             ApplicationInfo aInfo = mContext.getPackageManager().getApplicationInfo(packageName, 0);
700             if (aInfo.uid != uid) {
701                 Slog.w(TAG, "package " + packageName
702                         + " does not match caller's uid " + uid);
703                 EventLog.writeEvent(SNET_EVENT_LOG_ID, "180104273", -1, "");
704                 throwException = true;
705             }
706         } catch (PackageManager.NameNotFoundException e) {
707             throwException = true;
708         } finally {
709             if (throwException)
710                 throw new IllegalArgumentException("package " + packageName + " not found");
711         }
712 
713         requestPermissionDialog(device, accessory, canBeDefault, packageName, uid, mContext, pi);
714     }
715 
requestPermission(UsbDevice device, String packageName, PendingIntent pi, int pid, int uid)716     public void requestPermission(UsbDevice device, String packageName, PendingIntent pi, int pid,
717             int uid) {
718         Intent intent = new Intent();
719 
720         // respond immediately if permission has already been granted
721         if (hasPermission(device, packageName, pid, uid)) {
722             intent.putExtra(UsbManager.EXTRA_DEVICE, device);
723             intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true);
724             try {
725                 pi.send(mContext, 0, intent);
726             } catch (PendingIntent.CanceledException e) {
727                 if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled");
728             }
729             return;
730         }
731         // If the app doesn't have camera permission do not request permission to the USB device.
732         // Note that if the USB camera also has a microphone, a warning will be shown to the user if
733         // the app doesn't have RECORD_AUDIO permission.
734         if (device.getHasVideoCapture()) {
735             if (!isCameraPermissionGranted(packageName, pid, uid)) {
736                 intent.putExtra(UsbManager.EXTRA_DEVICE, device);
737                 intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false);
738                 try {
739                     pi.send(mContext, 0, intent);
740                 } catch (PendingIntent.CanceledException e) {
741                     if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled");
742                 }
743                 return;
744             }
745         }
746 
747         requestPermissionDialog(device, null,
748                 mUsbUserSettingsManager.canBeDefault(device, packageName), packageName, pi, uid);
749     }
750 
requestPermission(UsbAccessory accessory, String packageName, PendingIntent pi, int pid, int uid)751     public void requestPermission(UsbAccessory accessory, String packageName, PendingIntent pi,
752             int pid, int uid) {
753         // respond immediately if permission has already been granted
754         if (hasPermission(accessory, pid, uid)) {
755             Intent intent = new Intent();
756             intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
757             intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true);
758             try {
759                 pi.send(mContext, 0, intent);
760             } catch (PendingIntent.CanceledException e) {
761                 if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled");
762             }
763             return;
764         }
765 
766         requestPermissionDialog(null, accessory,
767                 mUsbUserSettingsManager.canBeDefault(accessory, packageName), packageName, pi, uid);
768     }
769 }
770