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.TypedXmlPullParser;
52 import android.util.TypedXmlSerializer;
53 import android.util.Xml;
54 
55 import com.android.internal.annotations.GuardedBy;
56 import com.android.internal.util.XmlUtils;
57 import com.android.internal.util.dump.DualDumpOutputStream;
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 uid)249     boolean hasPermission(@NonNull UsbAccessory accessory, int uid) {
250         synchronized (mLock) {
251             if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) {
252                 return true;
253             }
254             AccessoryFilter filter = new AccessoryFilter(accessory);
255             SparseBooleanArray permissionsForAccessory =
256                     mAccessoryPersistentPermissionMap.get(filter);
257             if (permissionsForAccessory != null) {
258                 int idx = permissionsForAccessory.indexOfKey(uid);
259                 if (idx >= 0) {
260                     return permissionsForAccessory.valueAt(idx);
261                 }
262             }
263             SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
264             if (uidList == null) {
265                 return false;
266             }
267             return uidList.get(uid);
268         }
269     }
270 
setDevicePersistentPermission(@onNull UsbDevice device, int uid, boolean isGranted)271     void setDevicePersistentPermission(@NonNull UsbDevice device, int uid, boolean isGranted) {
272 
273         boolean isChanged;
274         DeviceFilter filter = new DeviceFilter(device);
275         synchronized (mLock) {
276             SparseBooleanArray permissionsForDevice = mDevicePersistentPermissionMap.get(filter);
277             if (permissionsForDevice == null) {
278                 permissionsForDevice = new SparseBooleanArray();
279                 mDevicePersistentPermissionMap.put(filter, permissionsForDevice);
280             }
281             int idx = permissionsForDevice.indexOfKey(uid);
282             if (idx >= 0) {
283                 isChanged = permissionsForDevice.valueAt(idx) != isGranted;
284                 permissionsForDevice.setValueAt(idx, isGranted);
285             } else {
286                 isChanged = true;
287                 permissionsForDevice.put(uid, isGranted);
288             }
289 
290             if (isChanged) {
291                 scheduleWritePermissionsLocked();
292             }
293         }
294     }
295 
setAccessoryPersistentPermission(@onNull UsbAccessory accessory, int uid, boolean isGranted)296     void setAccessoryPersistentPermission(@NonNull UsbAccessory accessory, int uid,
297             boolean isGranted) {
298 
299         boolean isChanged;
300         AccessoryFilter filter = new AccessoryFilter(accessory);
301         synchronized (mLock) {
302             SparseBooleanArray permissionsForAccessory =
303                     mAccessoryPersistentPermissionMap.get(filter);
304             if (permissionsForAccessory == null) {
305                 permissionsForAccessory = new SparseBooleanArray();
306                 mAccessoryPersistentPermissionMap.put(filter, permissionsForAccessory);
307             }
308             int idx = permissionsForAccessory.indexOfKey(uid);
309             if (idx >= 0) {
310                 isChanged = permissionsForAccessory.valueAt(idx) != isGranted;
311                 permissionsForAccessory.setValueAt(idx, isGranted);
312             } else {
313                 isChanged = true;
314                 permissionsForAccessory.put(uid, isGranted);
315             }
316 
317             if (isChanged) {
318                 scheduleWritePermissionsLocked();
319             }
320         }
321     }
322 
readPermission(@onNull XmlPullParser parser)323     private void readPermission(@NonNull XmlPullParser parser) throws XmlPullParserException,
324             IOException {
325         int uid;
326         boolean isGranted;
327 
328         try {
329             uid = XmlUtils.readIntAttribute(parser, "uid");
330         } catch (NumberFormatException e) {
331             Slog.e(TAG, "error reading usb permission uid", e);
332             XmlUtils.skipCurrentTag(parser);
333             return;
334         }
335 
336         // only use "true"/"false" as valid values
337         String isGrantedString = parser.getAttributeValue(null, "granted");
338         if (isGrantedString == null || !(isGrantedString.equals(Boolean.TRUE.toString())
339                 || isGrantedString.equals(Boolean.FALSE.toString()))) {
340             Slog.e(TAG, "error reading usb permission granted state");
341             XmlUtils.skipCurrentTag(parser);
342             return;
343         }
344         isGranted = isGrantedString.equals(Boolean.TRUE.toString());
345         XmlUtils.nextElement(parser);
346         if ("usb-device".equals(parser.getName())) {
347             DeviceFilter filter = DeviceFilter.read(parser);
348             int idx = mDevicePersistentPermissionMap.indexOfKey(filter);
349             if (idx >= 0) {
350                 SparseBooleanArray permissionsForDevice =
351                         mDevicePersistentPermissionMap.valueAt(idx);
352                 permissionsForDevice.put(uid, isGranted);
353             } else {
354                 SparseBooleanArray permissionsForDevice = new SparseBooleanArray();
355                 mDevicePersistentPermissionMap.put(filter, permissionsForDevice);
356                 permissionsForDevice.put(uid, isGranted);
357             }
358         } else if ("usb-accessory".equals(parser.getName())) {
359             AccessoryFilter filter = AccessoryFilter.read(parser);
360             int idx = mAccessoryPersistentPermissionMap.indexOfKey(filter);
361             if (idx >= 0) {
362                 SparseBooleanArray permissionsForAccessory =
363                         mAccessoryPersistentPermissionMap.valueAt(idx);
364                 permissionsForAccessory.put(uid, isGranted);
365             } else {
366                 SparseBooleanArray permissionsForAccessory = new SparseBooleanArray();
367                 mAccessoryPersistentPermissionMap.put(filter, permissionsForAccessory);
368                 permissionsForAccessory.put(uid, isGranted);
369             }
370         }
371     }
372 
373     @GuardedBy("mLock")
readPermissionsLocked()374     private void readPermissionsLocked() {
375         mDevicePersistentPermissionMap.clear();
376         mAccessoryPersistentPermissionMap.clear();
377 
378         try (FileInputStream in = mPermissionsFile.openRead()) {
379             TypedXmlPullParser parser = Xml.resolvePullParser(in);
380 
381             XmlUtils.nextElement(parser);
382             while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
383                 String tagName = parser.getName();
384                 if ("permission".equals(tagName)) {
385                     readPermission(parser);
386                 } else {
387                     XmlUtils.nextElement(parser);
388                 }
389             }
390         } catch (FileNotFoundException e) {
391             if (DEBUG) Slog.d(TAG, "usb permissions file not found");
392         } catch (Exception e) {
393             Slog.e(TAG, "error reading usb permissions file, deleting to start fresh", e);
394             mPermissionsFile.delete();
395         }
396     }
397 
398     @GuardedBy("mLock")
scheduleWritePermissionsLocked()399     private void scheduleWritePermissionsLocked() {
400         if (mIsCopyPermissionsScheduled) {
401             return;
402         }
403         mIsCopyPermissionsScheduled = true;
404 
405         AsyncTask.execute(() -> {
406             int numDevices;
407             DeviceFilter[] devices;
408             int[][] uidsForDevices;
409             boolean[][] grantedValuesForDevices;
410 
411             int numAccessories;
412             AccessoryFilter[] accessories;
413             int[][] uidsForAccessories;
414             boolean[][] grantedValuesForAccessories;
415 
416             synchronized (mLock) {
417                 // Copy the permission state so we can write outside of lock
418                 numDevices = mDevicePersistentPermissionMap.size();
419                 devices = new DeviceFilter[numDevices];
420                 uidsForDevices = new int[numDevices][];
421                 grantedValuesForDevices = new boolean[numDevices][];
422                 for (int deviceIdx = 0; deviceIdx < numDevices; deviceIdx++) {
423                     devices[deviceIdx] =
424                             new DeviceFilter(mDevicePersistentPermissionMap.keyAt(deviceIdx));
425                     SparseBooleanArray permissions =
426                             mDevicePersistentPermissionMap.valueAt(deviceIdx);
427                     int numPermissions = permissions.size();
428                     uidsForDevices[deviceIdx] = new int[numPermissions];
429                     grantedValuesForDevices[deviceIdx] = new boolean[numPermissions];
430                     for (int permissionIdx = 0; permissionIdx < numPermissions; permissionIdx++) {
431                         uidsForDevices[deviceIdx][permissionIdx] = permissions.keyAt(permissionIdx);
432                         grantedValuesForDevices[deviceIdx][permissionIdx] =
433                                 permissions.valueAt(permissionIdx);
434                     }
435                 }
436 
437                 numAccessories = mAccessoryPersistentPermissionMap.size();
438                 accessories = new AccessoryFilter[numAccessories];
439                 uidsForAccessories = new int[numAccessories][];
440                 grantedValuesForAccessories = new boolean[numAccessories][];
441                 for (int accessoryIdx = 0; accessoryIdx < numAccessories; accessoryIdx++) {
442                     accessories[accessoryIdx] = new AccessoryFilter(
443                                     mAccessoryPersistentPermissionMap.keyAt(accessoryIdx));
444                     SparseBooleanArray permissions =
445                             mAccessoryPersistentPermissionMap.valueAt(accessoryIdx);
446                     int numPermissions = permissions.size();
447                     uidsForAccessories[accessoryIdx] = new int[numPermissions];
448                     grantedValuesForAccessories[accessoryIdx] = new boolean[numPermissions];
449                     for (int permissionIdx = 0; permissionIdx < numPermissions; permissionIdx++) {
450                         uidsForAccessories[accessoryIdx][permissionIdx] =
451                                 permissions.keyAt(permissionIdx);
452                         grantedValuesForAccessories[accessoryIdx][permissionIdx] =
453                                 permissions.valueAt(permissionIdx);
454                     }
455                 }
456                 mIsCopyPermissionsScheduled = false;
457             }
458 
459             synchronized (mPermissionsFile) {
460                 FileOutputStream out = null;
461                 try {
462                     out = mPermissionsFile.startWrite();
463                     TypedXmlSerializer serializer = Xml.resolveSerializer(out);
464                     serializer.startDocument(null, true);
465                     serializer.startTag(null, "permissions");
466 
467                     for (int i = 0; i < numDevices; i++) {
468                         int numPermissions = uidsForDevices[i].length;
469                         for (int j = 0; j < numPermissions; j++) {
470                             serializer.startTag(null, "permission");
471                             serializer.attribute(null, "uid",
472                                     Integer.toString(uidsForDevices[i][j]));
473                             serializer.attribute(null, "granted",
474                                     Boolean.toString(grantedValuesForDevices[i][j]));
475                             devices[i].write(serializer);
476                             serializer.endTag(null, "permission");
477                         }
478                     }
479 
480                     for (int i = 0; i < numAccessories; i++) {
481                         int numPermissions = uidsForAccessories[i].length;
482                         for (int j = 0; j < numPermissions; j++) {
483                             serializer.startTag(null, "permission");
484                             serializer.attribute(null, "uid",
485                                     Integer.toString(uidsForAccessories[i][j]));
486                             serializer.attribute(null, "granted",
487                                     Boolean.toString(grantedValuesForDevices[i][j]));
488                             accessories[i].write(serializer);
489                             serializer.endTag(null, "permission");
490                         }
491                     }
492 
493                     serializer.endTag(null, "permissions");
494                     serializer.endDocument();
495 
496                     mPermissionsFile.finishWrite(out);
497                 } catch (IOException e) {
498                     Slog.e(TAG, "Failed to write permissions", e);
499                     if (out != null) {
500                         mPermissionsFile.failWrite(out);
501                     }
502                 }
503             }
504         });
505     }
506 
507     /**
508      * Creates UI dialog to request permission for the given package to access the device
509      * or accessory.
510      *
511      * @param device       The USB device attached
512      * @param accessory    The USB accessory attached
513      * @param canBeDefault Whether the calling pacakge can set as default handler
514      *                     of the USB device or accessory
515      * @param packageName  The package name of the calling package
516      * @param uid          The uid of the calling package
517      * @param userContext  The context to start the UI dialog
518      * @param pi           PendingIntent for returning result
519      */
requestPermissionDialog(@ullable UsbDevice device, @Nullable UsbAccessory accessory, boolean canBeDefault, @NonNull String packageName, int uid, @NonNull Context userContext, @NonNull PendingIntent pi)520     void requestPermissionDialog(@Nullable UsbDevice device,
521             @Nullable UsbAccessory accessory,
522             boolean canBeDefault,
523             @NonNull String packageName,
524             int uid,
525             @NonNull Context userContext,
526             @NonNull PendingIntent pi) {
527         final long identity = Binder.clearCallingIdentity();
528         try {
529             Intent intent = new Intent();
530             if (device != null) {
531                 intent.putExtra(UsbManager.EXTRA_DEVICE, device);
532             } else {
533                 intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
534             }
535             intent.putExtra(Intent.EXTRA_INTENT, pi);
536             intent.putExtra(Intent.EXTRA_UID, uid);
537             intent.putExtra(UsbManager.EXTRA_CAN_BE_DEFAULT, canBeDefault);
538             intent.putExtra(UsbManager.EXTRA_PACKAGE, packageName);
539             intent.setComponent(
540                     ComponentName.unflattenFromString(userContext.getResources().getString(
541                             com.android.internal.R.string.config_usbPermissionActivity)));
542             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
543 
544             userContext.startActivityAsUser(intent, mUser);
545         } catch (ActivityNotFoundException e) {
546             Slog.e(TAG, "unable to start UsbPermissionActivity");
547         } finally {
548             Binder.restoreCallingIdentity(identity);
549         }
550     }
551 
dump(@onNull DualDumpOutputStream dump, String idName, long id)552     void dump(@NonNull DualDumpOutputStream dump, String idName, long id) {
553         long token = dump.start(idName, id);
554         synchronized (mLock) {
555             dump.write("user_id", UsbUserPermissionsManagerProto.USER_ID, mUser.getIdentifier());
556             int numMappings = mDevicePermissionMap.size();
557             for (int mappingsIdx = 0; mappingsIdx < numMappings; mappingsIdx++) {
558                 String deviceName = mDevicePermissionMap.keyAt(mappingsIdx);
559                 long devicePermissionToken = dump.start("device_permissions",
560                         UsbUserPermissionsManagerProto.DEVICE_PERMISSIONS);
561 
562                 dump.write("device_name", UsbDevicePermissionProto.DEVICE_NAME, deviceName);
563 
564                 SparseBooleanArray uidList = mDevicePermissionMap.valueAt(mappingsIdx);
565                 int numUids = uidList.size();
566                 for (int uidsIdx = 0; uidsIdx < numUids; uidsIdx++) {
567                     dump.write("uids", UsbDevicePermissionProto.UIDS, uidList.keyAt(uidsIdx));
568                 }
569 
570                 dump.end(devicePermissionToken);
571             }
572 
573             numMappings = mAccessoryPermissionMap.size();
574             for (int mappingsIdx = 0; mappingsIdx < numMappings; ++mappingsIdx) {
575                 UsbAccessory accessory = mAccessoryPermissionMap.keyAt(mappingsIdx);
576                 long accessoryPermissionToken = dump.start("accessory_permissions",
577                         UsbUserPermissionsManagerProto.ACCESSORY_PERMISSIONS);
578 
579                 dump.write("accessory_description",
580                         UsbAccessoryPermissionProto.ACCESSORY_DESCRIPTION,
581                         accessory.getDescription());
582 
583                 SparseBooleanArray uidList = mAccessoryPermissionMap.valueAt(mappingsIdx);
584                 int numUids = uidList.size();
585                 for (int uidsIdx = 0; uidsIdx < numUids; uidsIdx++) {
586                     dump.write("uids", UsbAccessoryPermissionProto.UIDS, uidList.keyAt(uidsIdx));
587                 }
588 
589                 dump.end(accessoryPermissionToken);
590             }
591 
592             numMappings = mDevicePersistentPermissionMap.size();
593             for (int mappingsIdx = 0; mappingsIdx < numMappings; mappingsIdx++) {
594                 DeviceFilter filter = mDevicePersistentPermissionMap.keyAt(mappingsIdx);
595                 long devicePermissionToken = dump.start("device_persistent_permissions",
596                         UsbUserPermissionsManagerProto.DEVICE_PERSISTENT_PERMISSIONS);
597                 filter.dump(dump, "device",
598                         UsbDevicePersistentPermissionProto.DEVICE_FILTER);
599                 SparseBooleanArray permissions =
600                         mDevicePersistentPermissionMap.valueAt(mappingsIdx);
601                 int numPermissions = permissions.size();
602                 for (int permissionsIdx = 0; permissionsIdx < numPermissions; permissionsIdx++) {
603                     long uidPermissionToken = dump.start("uid_permission",
604                             UsbDevicePersistentPermissionProto.PERMISSION_VALUES);
605                     dump.write("uid", UsbUidPermissionProto.UID, permissions.keyAt(permissionsIdx));
606                     dump.write("is_granted",
607                             UsbUidPermissionProto.IS_GRANTED, permissions.valueAt(permissionsIdx));
608                     dump.end(uidPermissionToken);
609                 }
610                 dump.end(devicePermissionToken);
611             }
612 
613             numMappings = mAccessoryPersistentPermissionMap.size();
614             for (int mappingsIdx = 0; mappingsIdx < numMappings; mappingsIdx++) {
615                 AccessoryFilter filter = mAccessoryPersistentPermissionMap.keyAt(mappingsIdx);
616                 long accessoryPermissionToken = dump.start("accessory_persistent_permissions",
617                         UsbUserPermissionsManagerProto.ACCESSORY_PERSISTENT_PERMISSIONS);
618                 filter.dump(dump, "accessory",
619                         UsbAccessoryPersistentPermissionProto.ACCESSORY_FILTER);
620                 SparseBooleanArray permissions =
621                         mAccessoryPersistentPermissionMap.valueAt(mappingsIdx);
622                 int numPermissions = permissions.size();
623                 for (int permissionsIdx = 0; permissionsIdx < numPermissions; permissionsIdx++) {
624                     long uidPermissionToken = dump.start("uid_permission",
625                             UsbAccessoryPersistentPermissionProto.PERMISSION_VALUES);
626                     dump.write("uid", UsbUidPermissionProto.UID, permissions.keyAt(permissionsIdx));
627                     dump.write("is_granted",
628                             UsbUidPermissionProto.IS_GRANTED, permissions.valueAt(permissionsIdx));
629                     dump.end(uidPermissionToken);
630                 }
631                 dump.end(accessoryPermissionToken);
632             }
633         }
634         dump.end(token);
635     }
636 
637     /**
638      * Check for camera permission of the calling process.
639      *
640      * @param packageName Package name of the caller.
641      * @param pid         Linux pid of the calling process.
642      * @param uid         Linux uid of the calling process.
643      * @return True in case camera permission is available, False otherwise.
644      */
isCameraPermissionGranted(String packageName, int pid, int uid)645     private boolean isCameraPermissionGranted(String packageName, int pid, int uid) {
646         int targetSdkVersion = android.os.Build.VERSION_CODES.P;
647         try {
648             ApplicationInfo aInfo = mContext.getPackageManager().getApplicationInfo(packageName, 0);
649             // compare uid with packageName to foil apps pretending to be someone else
650             if (aInfo.uid != uid) {
651                 Slog.i(TAG, "Package " + packageName + " does not match caller's uid " + uid);
652                 return false;
653             }
654             targetSdkVersion = aInfo.targetSdkVersion;
655         } catch (PackageManager.NameNotFoundException e) {
656             Slog.i(TAG, "Package not found, likely due to invalid package name!");
657             return false;
658         }
659 
660         if (targetSdkVersion >= android.os.Build.VERSION_CODES.P) {
661             int allowed = mContext.checkPermission(android.Manifest.permission.CAMERA, pid, uid);
662             if (android.content.pm.PackageManager.PERMISSION_DENIED == allowed) {
663                 Slog.i(TAG, "Camera permission required for USB video class devices");
664                 return false;
665             }
666         }
667 
668         return true;
669     }
670 
checkPermission(UsbDevice device, String packageName, int pid, int uid)671     public void checkPermission(UsbDevice device, String packageName, int pid, int uid) {
672         if (!hasPermission(device, packageName, pid, uid)) {
673             throw new SecurityException("User has not given " + uid + "/" + packageName
674                     + " permission to access device " + device.getDeviceName());
675         }
676     }
677 
checkPermission(UsbAccessory accessory, int uid)678     public void checkPermission(UsbAccessory accessory, int uid) {
679         if (!hasPermission(accessory, uid)) {
680             throw new SecurityException("User has not given " + uid + " permission to accessory "
681                     + accessory);
682         }
683     }
684 
requestPermissionDialog(@ullable UsbDevice device, @Nullable UsbAccessory accessory, boolean canBeDefault, String packageName, PendingIntent pi, int uid)685     private void requestPermissionDialog(@Nullable UsbDevice device,
686             @Nullable UsbAccessory accessory,
687             boolean canBeDefault,
688             String packageName,
689             PendingIntent pi,
690             int uid) {
691         boolean throwException = false;
692 
693         // compare uid with packageName to foil apps pretending to be someone else
694         try {
695             ApplicationInfo aInfo = mContext.getPackageManager().getApplicationInfo(packageName, 0);
696             if (aInfo.uid != uid) {
697                 Slog.w(TAG, "package " + packageName
698                         + " does not match caller's uid " + uid);
699                 EventLog.writeEvent(SNET_EVENT_LOG_ID, "180104273", -1, "");
700                 throwException = true;
701             }
702         } catch (PackageManager.NameNotFoundException e) {
703             throwException = true;
704         } finally {
705             if (throwException)
706                 throw new IllegalArgumentException("package " + packageName + " not found");
707         }
708 
709         requestPermissionDialog(device, accessory, canBeDefault, packageName, uid, mContext, pi);
710     }
711 
requestPermission(UsbDevice device, String packageName, PendingIntent pi, int pid, int uid)712     public void requestPermission(UsbDevice device, String packageName, PendingIntent pi, int pid,
713             int uid) {
714         Intent intent = new Intent();
715 
716         // respond immediately if permission has already been granted
717         if (hasPermission(device, packageName, pid, uid)) {
718             intent.putExtra(UsbManager.EXTRA_DEVICE, device);
719             intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true);
720             try {
721                 pi.send(mContext, 0, intent);
722             } catch (PendingIntent.CanceledException e) {
723                 if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled");
724             }
725             return;
726         }
727         // If the app doesn't have camera permission do not request permission to the USB device.
728         // Note that if the USB camera also has a microphone, a warning will be shown to the user if
729         // the app doesn't have RECORD_AUDIO permission.
730         if (device.getHasVideoCapture()) {
731             if (!isCameraPermissionGranted(packageName, pid, uid)) {
732                 intent.putExtra(UsbManager.EXTRA_DEVICE, device);
733                 intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false);
734                 try {
735                     pi.send(mContext, 0, intent);
736                 } catch (PendingIntent.CanceledException e) {
737                     if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled");
738                 }
739                 return;
740             }
741         }
742 
743         requestPermissionDialog(device, null,
744                 mUsbUserSettingsManager.canBeDefault(device, packageName), packageName, pi, uid);
745     }
746 
requestPermission(UsbAccessory accessory, String packageName, PendingIntent pi, int uid)747     public void requestPermission(UsbAccessory accessory, String packageName, PendingIntent pi,
748             int uid) {
749         // respond immediately if permission has already been granted
750         if (hasPermission(accessory, uid)) {
751             Intent intent = new Intent();
752             intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
753             intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true);
754             try {
755                 pi.send(mContext, 0, intent);
756             } catch (PendingIntent.CanceledException e) {
757                 if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled");
758             }
759             return;
760         }
761 
762         requestPermissionDialog(null, accessory,
763                 mUsbUserSettingsManager.canBeDefault(accessory, packageName), packageName, pi, uid);
764     }
765 }
766