summaryrefslogtreecommitdiff
path: root/services/usb
diff options
context:
space:
mode:
authorXin Li <delphij@google.com>2020-08-31 21:21:38 -0700
committerXin Li <delphij@google.com>2020-08-31 21:21:38 -0700
commit628590d7ec80e10a3fc24b1c18a1afb55cca10a8 (patch)
tree4b1c3f52d86d7fb53afbe9e9438468588fa489f8 /services/usb
parentb11b8ec3aec8bb42f2c07e1c5ac7942da293baa8 (diff)
parentd2d3a20624d968199353ccf6ddbae6f3ac39c9af (diff)
Merge Android R (rvc-dev-plus-aosp-without-vendor@6692709)
Bug: 166295507 Merged-In: I3d92a6de21a938f6b352ec26dc23420c0fe02b27 Change-Id: Ifdb80563ef042738778ebb8a7581a97c4e3d96e2
Diffstat (limited to 'services/usb')
-rw-r--r--services/usb/Android.bp2
-rw-r--r--services/usb/java/com/android/server/usb/UsbDeviceManager.java107
-rw-r--r--services/usb/java/com/android/server/usb/UsbHandlerManager.java16
-rw-r--r--services/usb/java/com/android/server/usb/UsbHostManager.java41
-rw-r--r--services/usb/java/com/android/server/usb/UsbPermissionManager.java247
-rw-r--r--services/usb/java/com/android/server/usb/UsbPortManager.java22
-rw-r--r--services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java330
-rw-r--r--services/usb/java/com/android/server/usb/UsbSerialReader.java19
-rw-r--r--services/usb/java/com/android/server/usb/UsbService.java204
-rw-r--r--services/usb/java/com/android/server/usb/UsbSettingsManager.java57
-rw-r--r--services/usb/java/com/android/server/usb/UsbUserPermissionManager.java758
-rw-r--r--services/usb/java/com/android/server/usb/UsbUserSettingsManager.java201
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/ByteStream.java7
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbACEndpoint.java11
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbACInterface.java39
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java33
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbDescriptor.java103
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java178
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java16
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java17
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java17
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbVCEndpoint.java74
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbVCHeader.java50
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbVCHeaderInterface.java56
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbVCInputTerminal.java49
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbVCInterface.java114
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbVCOutputTerminal.java49
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbVCProcessingUnit.java50
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/UsbVCSelectorUnit.java50
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/report/UsbStrings.java7
-rw-r--r--services/usb/java/com/android/server/usb/descriptors/tree/UsbDescriptorsTree.java8
31 files changed, 2272 insertions, 660 deletions
diff --git a/services/usb/Android.bp b/services/usb/Android.bp
index d2c973abbc74..4e984093cfec 100644
--- a/services/usb/Android.bp
+++ b/services/usb/Android.bp
@@ -7,6 +7,7 @@ filegroup {
java_library_static {
name: "services.usb",
+ defaults: ["services_defaults"],
srcs: [":services.usb-sources"],
libs: [
@@ -19,5 +20,6 @@ java_library_static {
"android.hardware.usb-V1.1-java",
"android.hardware.usb-V1.2-java",
"android.hardware.usb.gadget-V1.0-java",
+ "android.hardware.usb.gadget-V1.1-java",
],
}
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 68bd301498ad..98b9dcd2cc2f 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -41,6 +41,7 @@ import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.debug.AdbManagerInternal;
+import android.debug.AdbNotifications;
import android.debug.AdbTransportType;
import android.debug.IAdbTransport;
import android.hardware.usb.ParcelableUsbPort;
@@ -162,6 +163,7 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
private static final int MSG_GET_CURRENT_USB_FUNCTIONS = 16;
private static final int MSG_FUNCTION_SWITCH_TIMEOUT = 17;
private static final int MSG_GADGET_HAL_REGISTERED = 18;
+ private static final int MSG_RESET_USB_GADGET = 19;
private static final int AUDIO_MODE_SOURCE = 1;
@@ -251,7 +253,7 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
}
public UsbDeviceManager(Context context, UsbAlsaManager alsaManager,
- UsbSettingsManager settingsManager) {
+ UsbSettingsManager settingsManager, UsbPermissionManager permissionManager) {
mContext = context;
mContentResolver = context.getContentResolver();
PackageManager pm = mContext.getPackageManager();
@@ -285,13 +287,13 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
* Initialze the legacy UsbHandler
*/
mHandler = new UsbHandlerLegacy(FgThread.get().getLooper(), mContext, this,
- alsaManager, settingsManager);
+ alsaManager, permissionManager);
} else {
/**
* Initialize HAL based UsbHandler
*/
mHandler = new UsbHandlerHal(FgThread.get().getLooper(), mContext, this,
- alsaManager, settingsManager);
+ alsaManager, permissionManager);
}
if (nativeIsStartRequested()) {
@@ -444,7 +446,6 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
abstract static class UsbHandler extends Handler {
// current USB state
- private boolean mConnected;
private boolean mHostConnected;
private boolean mSourcePower;
private boolean mSinkPower;
@@ -469,9 +470,10 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
private final Context mContext;
private final UsbAlsaManager mUsbAlsaManager;
- private final UsbSettingsManager mSettingsManager;
+ private final UsbPermissionManager mPermissionManager;
private NotificationManager mNotificationManager;
+ protected boolean mConnected;
protected long mScreenUnlockedFunctions;
protected boolean mBootCompleted;
protected boolean mCurrentFunctionsApplied;
@@ -490,12 +492,12 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
protected static final String USB_PERSISTENT_CONFIG_PROPERTY = "persist.sys.usb.config";
UsbHandler(Looper looper, Context context, UsbDeviceManager deviceManager,
- UsbAlsaManager alsaManager, UsbSettingsManager settingsManager) {
+ UsbAlsaManager alsaManager, UsbPermissionManager permissionManager) {
super(looper);
mContext = context;
mUsbDeviceManager = deviceManager;
mUsbAlsaManager = alsaManager;
- mSettingsManager = settingsManager;
+ mPermissionManager = permissionManager;
mContentResolver = context.getContentResolver();
mCurrentUser = ActivityManager.getCurrentUser();
@@ -626,7 +628,7 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
// successfully entered accessory mode
String[] accessoryStrings = mUsbDeviceManager.getAccessoryStrings();
if (accessoryStrings != null) {
- UsbSerialReader serialReader = new UsbSerialReader(mContext, mSettingsManager,
+ UsbSerialReader serialReader = new UsbSerialReader(mContext, mPermissionManager,
accessoryStrings[UsbAccessory.SERIAL_STRING]);
mCurrentAccessory = new UsbAccessory(
@@ -664,7 +666,7 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
if (mCurrentAccessory != null) {
if (mBootCompleted) {
- mSettingsManager.usbAccessoryRemoved(mCurrentAccessory);
+ mPermissionManager.usbAccessoryRemoved(mCurrentAccessory);
}
mCurrentAccessory = null;
}
@@ -1179,7 +1181,6 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
protected void updateAdbNotification(boolean force) {
if (mNotificationManager == null) return;
final int id = SystemMessage.NOTE_ADB_ACTIVE;
- final int titleRes = com.android.internal.R.string.adb_active_notification_title;
if (isAdbEnabled() && mConnected) {
if ("0".equals(getSystemProperty("persist.adb.notify", ""))) return;
@@ -1190,37 +1191,10 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
}
if (!mAdbNotificationShown) {
- Resources r = mContext.getResources();
- CharSequence title = r.getText(titleRes);
- CharSequence message = r.getText(
- com.android.internal.R.string.adb_active_notification_message);
-
- Intent intent = new Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_CLEAR_TASK);
- PendingIntent pi = PendingIntent.getActivityAsUser(mContext, 0,
- intent, 0, null, UserHandle.CURRENT);
-
- Notification notification =
- new Notification.Builder(mContext, SystemNotificationChannels.DEVELOPER)
- .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
- .setWhen(0)
- .setOngoing(true)
- .setTicker(title)
- .setDefaults(0) // please be quiet
- .setColor(mContext.getColor(
- com.android.internal.R.color
- .system_notification_accent_color))
- .setContentTitle(title)
- .setContentText(message)
- .setContentIntent(pi)
- .setVisibility(Notification.VISIBILITY_PUBLIC)
- .extend(new Notification.TvExtender()
- .setChannelId(ADB_NOTIFICATION_CHANNEL_ID_TV))
- .build();
+ Notification notification = AdbNotifications.createNotification(mContext,
+ AdbTransportType.USB);
mAdbNotificationShown = true;
- mNotificationManager.notifyAsUser(null, id, notification,
- UserHandle.ALL);
+ mNotificationManager.notifyAsUser(null, id, notification, UserHandle.ALL);
}
} else if (mAdbNotificationShown) {
mAdbNotificationShown = false;
@@ -1346,8 +1320,8 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
private boolean mUsbDataUnlocked;
UsbHandlerLegacy(Looper looper, Context context, UsbDeviceManager deviceManager,
- UsbAlsaManager alsaManager, UsbSettingsManager settingsManager) {
- super(looper, context, deviceManager, alsaManager, settingsManager);
+ UsbAlsaManager alsaManager, UsbPermissionManager permissionManager) {
+ super(looper, context, deviceManager, alsaManager, permissionManager);
try {
readOemUsbOverrideConfig(context);
// Restore default functions.
@@ -1726,8 +1700,8 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
protected boolean mCurrentUsbFunctionsRequested;
UsbHandlerHal(Looper looper, Context context, UsbDeviceManager deviceManager,
- UsbAlsaManager alsaManager, UsbSettingsManager settingsManager) {
- super(looper, context, deviceManager, alsaManager, settingsManager);
+ UsbAlsaManager alsaManager, UsbPermissionManager permissionManager) {
+ super(looper, context, deviceManager, alsaManager, permissionManager);
try {
ServiceNotification serviceNotification = new ServiceNotification();
@@ -1792,7 +1766,8 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
case MSG_SET_FUNCTIONS_TIMEOUT:
Slog.e(TAG, "Set functions timed out! no reply from usb hal");
if (msg.arg1 != 1) {
- setEnabledFunctions(UsbManager.FUNCTION_NONE, false);
+ // Set this since default function may be selected from Developer options
+ setEnabledFunctions(mScreenUnlockedFunctions, false);
}
break;
case MSG_GET_CURRENT_USB_FUNCTIONS:
@@ -1814,7 +1789,8 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
* Dont force to default when the configuration is already set to default.
*/
if (msg.arg1 != 1) {
- setEnabledFunctions(UsbManager.FUNCTION_NONE, !isAdbEnabled());
+ // Set this since default function may be selected from Developer options
+ setEnabledFunctions(mScreenUnlockedFunctions, false);
}
break;
case MSG_GADGET_HAL_REGISTERED:
@@ -1834,6 +1810,23 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
}
}
break;
+ case MSG_RESET_USB_GADGET:
+ synchronized (mGadgetProxyLock) {
+ if (mGadgetProxy == null) {
+ Slog.e(TAG, "reset Usb Gadget mGadgetProxy is null");
+ break;
+ }
+
+ try {
+ android.hardware.usb.gadget.V1_1.IUsbGadget gadgetProxy =
+ android.hardware.usb.gadget.V1_1.IUsbGadget
+ .castFrom(mGadgetProxy);
+ gadgetProxy.reset();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "reset Usb Gadget failed", e);
+ }
+ }
+ break;
default:
super.handleMessage(msg);
}
@@ -1917,8 +1910,11 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
SET_FUNCTIONS_TIMEOUT_MS - SET_FUNCTIONS_LEEWAY_MS);
sendMessageDelayed(MSG_SET_FUNCTIONS_TIMEOUT, chargingFunctions,
SET_FUNCTIONS_TIMEOUT_MS);
- sendMessageDelayed(MSG_FUNCTION_SWITCH_TIMEOUT, chargingFunctions,
- SET_FUNCTIONS_TIMEOUT_MS + ENUMERATION_TIME_OUT_MS);
+ if (mConnected) {
+ // Only queue timeout of enumeration when the USB is connected
+ sendMessageDelayed(MSG_FUNCTION_SWITCH_TIMEOUT, chargingFunctions,
+ SET_FUNCTIONS_TIMEOUT_MS + ENUMERATION_TIME_OUT_MS);
+ }
if (DEBUG) Slog.d(TAG, "timeout message queued");
} catch (RemoteException e) {
Slog.e(TAG, "Remoteexception while calling setCurrentUsbFunctions", e);
@@ -1967,7 +1963,7 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
* @param uid Uid of the caller
*/
public ParcelFileDescriptor openAccessory(UsbAccessory accessory,
- UsbUserSettingsManager settings, int uid) {
+ UsbUserPermissionManager permissions, int uid) {
UsbAccessory currentAccessory = mHandler.getCurrentAccessory();
if (currentAccessory == null) {
throw new IllegalArgumentException("no accessory attached");
@@ -1978,7 +1974,7 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
+ currentAccessory;
throw new IllegalArgumentException(error);
}
- settings.checkPermission(accessory, uid);
+ permissions.checkPermission(accessory, uid);
return nativeOpenAccessory();
}
@@ -2044,6 +2040,17 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
mHandler.sendMessage(MSG_SET_SCREEN_UNLOCKED_FUNCTIONS, functions);
}
+ /**
+ * Resets the USB Gadget.
+ */
+ public void resetUsbGadget() {
+ if (DEBUG) {
+ Slog.d(TAG, "reset Usb Gadget");
+ }
+
+ mHandler.sendMessage(MSG_RESET_USB_GADGET, null);
+ }
+
private void onAdbEnabled(boolean enabled) {
mHandler.sendMessage(MSG_ENABLE_ADB, enabled);
}
diff --git a/services/usb/java/com/android/server/usb/UsbHandlerManager.java b/services/usb/java/com/android/server/usb/UsbHandlerManager.java
index 1730d8f22950..f3112743bcf2 100644
--- a/services/usb/java/com/android/server/usb/UsbHandlerManager.java
+++ b/services/usb/java/com/android/server/usb/UsbHandlerManager.java
@@ -19,6 +19,7 @@ package com.android.server.usb;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ResolveInfo;
@@ -59,8 +60,9 @@ class UsbHandlerManager {
if (uri != null && uri.length() > 0) {
// display URI to user
Intent dialogIntent = createDialogIntent();
- dialogIntent.setClassName("com.android.systemui",
- "com.android.systemui.usb.UsbAccessoryUriActivity");
+ dialogIntent.setComponent(ComponentName.unflattenFromString(
+ mContext.getResources().getString(
+ com.android.internal.R.string.config_usbAccessoryUriActivity)));
dialogIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
dialogIntent.putExtra("uri", uri);
try {
@@ -84,8 +86,9 @@ class UsbHandlerManager {
@Nullable UsbAccessory accessory) {
Intent resolverIntent = createDialogIntent();
// start UsbConfirmActivity if there is only one choice
- resolverIntent.setClassName("com.android.systemui",
- "com.android.systemui.usb.UsbConfirmActivity");
+ resolverIntent.setComponent(ComponentName.unflattenFromString(
+ mContext.getResources().getString(
+ com.android.internal.R.string.config_usbConfirmActivity)));
resolverIntent.putExtra("rinfo", rInfo);
UserHandle user =
UserHandle.getUserHandleForUid(rInfo.activityInfo.applicationInfo.uid);
@@ -115,8 +118,9 @@ class UsbHandlerManager {
void selectUsbHandler(@NonNull ArrayList<ResolveInfo> matches,
@NonNull UserHandle user, @NonNull Intent intent) {
Intent resolverIntent = createDialogIntent();
- resolverIntent.setClassName("com.android.systemui",
- "com.android.systemui.usb.UsbResolverActivity");
+ resolverIntent.setComponent(ComponentName.unflattenFromString(
+ mContext.getResources().getString(
+ com.android.internal.R.string.config_usbResolverActivity)));
resolverIntent.putParcelableArrayListExtra("rlist", matches);
resolverIntent.putExtra(Intent.EXTRA_INTENT, intent);
diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java
index c0097bfc6f81..f33001c9241e 100644
--- a/services/usb/java/com/android/server/usb/UsbHostManager.java
+++ b/services/usb/java/com/android/server/usb/UsbHostManager.java
@@ -34,9 +34,9 @@ import android.service.usb.UsbIsHeadsetProto;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Slog;
-import android.util.StatsLog;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.dump.DualDumpOutputStream;
import com.android.server.usb.descriptors.UsbDescriptor;
@@ -65,7 +65,7 @@ public class UsbHostManager {
private final String[] mHostDenyList;
private final UsbAlsaManager mUsbAlsaManager;
- private final UsbSettingsManager mSettingsManager;
+ private final UsbPermissionManager mPermissionManager;
private final Object mLock = new Object();
@GuardedBy("mLock")
@@ -232,13 +232,13 @@ public class UsbHostManager {
* UsbHostManager
*/
public UsbHostManager(Context context, UsbAlsaManager alsaManager,
- UsbSettingsManager settingsManager) {
+ UsbPermissionManager permissionManager) {
mContext = context;
mHostDenyList = context.getResources().getStringArray(
com.android.internal.R.array.config_usbHostDenylist);
mUsbAlsaManager = alsaManager;
- mSettingsManager = settingsManager;
+ mPermissionManager = permissionManager;
String deviceConnectionHandler = context.getResources().getString(
com.android.internal.R.string.config_UsbDeviceConnectionHandling_component);
if (!TextUtils.isEmpty(deviceConnectionHandler)) {
@@ -386,15 +386,15 @@ public class UsbHostManager {
return false;
}
- UsbDevice.Builder newDeviceBuilder = parser.toAndroidUsbDevice();
+ UsbDevice.Builder newDeviceBuilder = parser.toAndroidUsbDeviceBuilder();
if (newDeviceBuilder == null) {
Slog.e(TAG, "Couldn't create UsbDevice object.");
// Tracking
addConnectionRecord(deviceAddress, ConnectionRecord.CONNECT_BADDEVICE,
parser.getRawDescriptors());
} else {
- UsbSerialReader serialNumberReader = new UsbSerialReader(mContext, mSettingsManager,
- newDeviceBuilder.serialNumber);
+ UsbSerialReader serialNumberReader = new UsbSerialReader(mContext,
+ mPermissionManager, newDeviceBuilder.serialNumber);
UsbDevice newDevice = newDeviceBuilder.build(serialNumberReader);
serialNumberReader.setDevice(newDevice);
@@ -418,10 +418,11 @@ public class UsbHostManager {
parser.getRawDescriptors());
// Stats collection
- StatsLog.write(StatsLog.USB_DEVICE_ATTACHED, newDevice.getVendorId(),
- newDevice.getProductId(), parser.hasAudioInterface(),
- parser.hasHIDInterface(), parser.hasStorageInterface(),
- StatsLog.USB_DEVICE_ATTACHED__STATE__STATE_CONNECTED, 0);
+ FrameworkStatsLog.write(FrameworkStatsLog.USB_DEVICE_ATTACHED,
+ newDevice.getVendorId(), newDevice.getProductId(),
+ parser.hasAudioInterface(), parser.hasHIDInterface(),
+ parser.hasStorageInterface(),
+ FrameworkStatsLog.USB_DEVICE_ATTACHED__STATE__STATE_CONNECTED, 0);
}
}
@@ -444,7 +445,7 @@ public class UsbHostManager {
if (device != null) {
Slog.d(TAG, "Removed device at " + deviceAddress + ": " + device.getProductName());
mUsbAlsaManager.usbDeviceRemoved(deviceAddress);
- mSettingsManager.usbDeviceRemoved(device);
+ mPermissionManager.usbDeviceRemoved(device);
getCurrentUserSettings().usbDeviceRemoved(device);
ConnectionRecord current = mConnected.get(deviceAddress);
// Tracking
@@ -454,10 +455,10 @@ public class UsbHostManager {
UsbDescriptorParser parser = new UsbDescriptorParser(deviceAddress,
current.mDescriptors);
// Stats collection
- StatsLog.write(StatsLog.USB_DEVICE_ATTACHED, device.getVendorId(),
- device.getProductId(), parser.hasAudioInterface(),
+ FrameworkStatsLog.write(FrameworkStatsLog.USB_DEVICE_ATTACHED,
+ device.getVendorId(), device.getProductId(), parser.hasAudioInterface(),
parser.hasHIDInterface(), parser.hasStorageInterface(),
- StatsLog.USB_DEVICE_ATTACHED__STATE__STATE_DISCONNECTED,
+ FrameworkStatsLog.USB_DEVICE_ATTACHED__STATE__STATE_DISCONNECTED,
System.currentTimeMillis() - current.mTimestamp);
}
} else {
@@ -484,9 +485,11 @@ public class UsbHostManager {
}
}
- /* Opens the specified USB device */
- public ParcelFileDescriptor openDevice(String deviceAddress, UsbUserSettingsManager settings,
- String packageName, int pid, int uid) {
+ /**
+ * Opens the specified USB device
+ */
+ public ParcelFileDescriptor openDevice(String deviceAddress,
+ UsbUserPermissionManager permissions, String packageName, int pid, int uid) {
synchronized (mLock) {
if (isDenyListed(deviceAddress)) {
throw new SecurityException("USB device is on a restricted bus");
@@ -498,7 +501,7 @@ public class UsbHostManager {
"device " + deviceAddress + " does not exist or is restricted");
}
- settings.checkPermission(device, packageName, pid, uid);
+ permissions.checkPermission(device, packageName, pid, uid);
return nativeOpenDevice(deviceAddress);
}
}
diff --git a/services/usb/java/com/android/server/usb/UsbPermissionManager.java b/services/usb/java/com/android/server/usb/UsbPermissionManager.java
index dd2f29ba74ae..5d3ed4fc6acf 100644
--- a/services/usb/java/com/android/server/usb/UsbPermissionManager.java
+++ b/services/usb/java/com/android/server/usb/UsbPermissionManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,230 +17,119 @@
package com.android.server.usb;
import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.PendingIntent;
-import android.content.ActivityNotFoundException;
+import android.annotation.UserIdInt;
import android.content.Context;
import android.content.Intent;
+import android.content.pm.UserInfo;
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager;
-import android.os.Binder;
-import android.os.Process;
import android.os.UserHandle;
-import android.service.usb.UsbSettingsAccessoryPermissionProto;
-import android.service.usb.UsbSettingsDevicePermissionProto;
-import android.service.usb.UsbUserSettingsManagerProto;
+import android.os.UserManager;
+import android.service.usb.UsbSettingsManagerProto;
import android.util.Slog;
-import android.util.SparseBooleanArray;
+import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.dump.DualDumpOutputStream;
-import java.util.HashMap;
+import java.util.List;
-/**
- * UsbPermissionManager manages usb device or accessory access permissions.
- *
- * @hide
- */
class UsbPermissionManager {
private static final String LOG_TAG = UsbPermissionManager.class.getSimpleName();
+ private static final boolean DEBUG = false;
- @GuardedBy("mLock")
- /** Temporary mapping USB device name to list of UIDs with permissions for the device*/
- private final HashMap<String, SparseBooleanArray> mDevicePermissionMap =
- new HashMap<>();
- @GuardedBy("mLock")
- /** Temporary mapping UsbAccessory to list of UIDs with permissions for the accessory*/
- private final HashMap<UsbAccessory, SparseBooleanArray> mAccessoryPermissionMap =
- new HashMap<>();
+ /** Context to be used by this module */
+ private final @NonNull Context mContext;
- private final UserHandle mUser;
- private final boolean mDisablePermissionDialogs;
+ /** Map from user id to {@link UsbUserPermissionManager} for the user */
+ @GuardedBy("mPermissionsByUser")
+ private final SparseArray<UsbUserPermissionManager> mPermissionsByUser = new SparseArray<>();
- private final Object mLock = new Object();
+ final UsbService mUsbService;
- UsbPermissionManager(@NonNull Context context, @NonNull UserHandle user) {
- mUser = user;
- mDisablePermissionDialogs = context.getResources().getBoolean(
- com.android.internal.R.bool.config_disableUsbPermissionDialogs);
+ UsbPermissionManager(@NonNull Context context,
+ @NonNull UsbService usbService) {
+ mContext = context;
+ mUsbService = usbService;
}
- /**
- * Removes access permissions of all packages for the USB accessory.
- *
- * @param accessory to remove permissions for
- */
- void removeAccessoryPermissions(@NonNull UsbAccessory accessory) {
- synchronized (mLock) {
- mAccessoryPermissionMap.remove(accessory);
+ @NonNull UsbUserPermissionManager getPermissionsForUser(@UserIdInt int userId) {
+ synchronized (mPermissionsByUser) {
+ UsbUserPermissionManager permissions = mPermissionsByUser.get(userId);
+ if (permissions == null) {
+ permissions = new UsbUserPermissionManager(mContext.createContextAsUser(
+ UserHandle.of(userId), 0), mUsbService.getSettingsForUser(userId));
+ mPermissionsByUser.put(userId, permissions);
+ }
+ return permissions;
}
}
- /**
- * Removes access permissions of all packages for the USB device.
- *
- * @param device to remove permissions for
- */
- void removeDevicePermissions(@NonNull UsbDevice device) {
- synchronized (mLock) {
- mDevicePermissionMap.remove(device.getDeviceName());
- }
+ @NonNull UsbUserPermissionManager getPermissionsForUser(@NonNull UserHandle user) {
+ return getPermissionsForUser(user.getIdentifier());
}
- /**
- * Grants permission for USB device without showing system dialog for package with uid.
- *
- * @param device to grant permission for
- * @param uid to grant permission for
- */
- void grantDevicePermission(@NonNull UsbDevice device, int uid) {
- synchronized (mLock) {
- String deviceName = device.getDeviceName();
- SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName);
- if (uidList == null) {
- uidList = new SparseBooleanArray(1);
- mDevicePermissionMap.put(deviceName, uidList);
- }
- uidList.put(uid, true);
+ void remove(@NonNull UserHandle userToRemove) {
+ synchronized (mPermissionsByUser) {
+ mPermissionsByUser.remove(userToRemove.getIdentifier());
}
}
/**
- * Grants permission for USB accessory without showing system dialog for package with uid.
+ * Remove temporary access permission and broadcast that a device was removed.
*
- * @param accessory to grant permission for
- * @param uid to grant permission for
+ * @param device The device that is removed
*/
- void grantAccessoryPermission(@NonNull UsbAccessory accessory, int uid) {
- synchronized (mLock) {
- SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
- if (uidList == null) {
- uidList = new SparseBooleanArray(1);
- mAccessoryPermissionMap.put(accessory, uidList);
+ void usbDeviceRemoved(@NonNull UsbDevice device) {
+ synchronized (mPermissionsByUser) {
+ for (int i = 0; i < mPermissionsByUser.size(); i++) {
+ // clear temporary permissions for the device
+ mPermissionsByUser.valueAt(i).removeDevicePermissions(device);
}
- uidList.put(uid, true);
}
- }
- /**
- * Returns true if package with uid has permission to access the device.
- *
- * @param device to check permission for
- * @param uid to check permission for
- * @return {@code true} if package with uid has permission
- */
- boolean hasPermission(@NonNull UsbDevice device, int uid) {
- synchronized (mLock) {
- if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) {
- return true;
- }
- SparseBooleanArray uidList = mDevicePermissionMap.get(device.getDeviceName());
- if (uidList == null) {
- return false;
- }
- return uidList.get(uid);
+ Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_DETACHED);
+ intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+ intent.putExtra(UsbManager.EXTRA_DEVICE, device);
+
+ if (DEBUG) {
+ Slog.d(LOG_TAG, "usbDeviceRemoved, sending " + intent);
}
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
}
/**
- * Returns true if caller has permission to access the accessory.
+ * Remove temporary access permission and broadcast that a accessory was removed.
*
- * @param accessory to check permission for
- * @param uid to check permission for
- * @return {@code true} if caller has permssion
+ * @param accessory The accessory that is removed
*/
- boolean hasPermission(@NonNull UsbAccessory accessory, int uid) {
- synchronized (mLock) {
- if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) {
- return true;
- }
- SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
- if (uidList == null) {
- return false;
+ void usbAccessoryRemoved(@NonNull UsbAccessory accessory) {
+ synchronized (mPermissionsByUser) {
+ for (int i = 0; i < mPermissionsByUser.size(); i++) {
+ // clear temporary permissions for the accessory
+ mPermissionsByUser.valueAt(i).removeAccessoryPermissions(accessory);
}
- return uidList.get(uid);
}
- }
- /**
- * Creates UI dialog to request permission for the given package to access the device
- * or accessory.
- *
- * @param device The USB device attached
- * @param accessory The USB accessory attached
- * @param canBeDefault Whether the calling pacakge can set as default handler
- * of the USB device or accessory
- * @param packageName The package name of the calling package
- * @param uid The uid of the calling package
- * @param userContext The context to start the UI dialog
- * @param pi PendingIntent for returning result
- */
- void requestPermissionDialog(@Nullable UsbDevice device,
- @Nullable UsbAccessory accessory,
- boolean canBeDefault,
- @NonNull String packageName,
- int uid,
- @NonNull Context userContext,
- @NonNull PendingIntent pi) {
- long identity = Binder.clearCallingIdentity();
- Intent intent = new Intent();
- if (device != null) {
- intent.putExtra(UsbManager.EXTRA_DEVICE, device);
- } else {
- intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
- }
- intent.putExtra(Intent.EXTRA_INTENT, pi);
- intent.putExtra(Intent.EXTRA_UID, uid);
- intent.putExtra(UsbManager.EXTRA_CAN_BE_DEFAULT, canBeDefault);
- intent.putExtra(UsbManager.EXTRA_PACKAGE, packageName);
- intent.setClassName("com.android.systemui",
- "com.android.systemui.usb.UsbPermissionActivity");
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
- try {
- userContext.startActivityAsUser(intent, mUser);
- } catch (ActivityNotFoundException e) {
- Slog.e(LOG_TAG, "unable to start UsbPermissionActivity");
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
+ Intent intent = new Intent(UsbManager.ACTION_USB_ACCESSORY_DETACHED);
+ intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+ intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
}
- void dump(@NonNull DualDumpOutputStream dump) {
- synchronized (mLock) {
- for (String deviceName : mDevicePermissionMap.keySet()) {
- long devicePermissionToken = dump.start("device_permissions",
- UsbUserSettingsManagerProto.DEVICE_PERMISSIONS);
-
- dump.write("device_name", UsbSettingsDevicePermissionProto.DEVICE_NAME, deviceName);
-
- SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName);
- int count = uidList.size();
- for (int i = 0; i < count; i++) {
- dump.write("uids", UsbSettingsDevicePermissionProto.UIDS, uidList.keyAt(i));
- }
-
- dump.end(devicePermissionToken);
- }
-
- for (UsbAccessory accessory : mAccessoryPermissionMap.keySet()) {
- long accessoryPermissionToken = dump.start("accessory_permissions",
- UsbUserSettingsManagerProto.ACCESSORY_PERMISSIONS);
-
- dump.write("accessory_description",
- UsbSettingsAccessoryPermissionProto.ACCESSORY_DESCRIPTION,
- accessory.getDescription());
-
- SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
- int count = uidList.size();
- for (int i = 0; i < count; i++) {
- dump.write("uids", UsbSettingsAccessoryPermissionProto.UIDS, uidList.keyAt(i));
- }
-
- dump.end(accessoryPermissionToken);
+ void dump(@NonNull DualDumpOutputStream dump, String idName, long id) {
+ long token = dump.start(idName, id);
+ UserManager userManager = mContext.getSystemService(UserManager.class);
+ synchronized (mPermissionsByUser) {
+ List<UserInfo> users = userManager.getUsers();
+ int numUsers = users.size();
+ for (int i = 0; i < numUsers; i++) {
+ getPermissionsForUser(users.get(i).id).dump(dump, "user_permissions",
+ UsbSettingsManagerProto.USER_SETTINGS);
}
}
+ dump.end(token);
}
+
}
diff --git a/services/usb/java/com/android/server/usb/UsbPortManager.java b/services/usb/java/com/android/server/usb/UsbPortManager.java
index 0e30f9380ff3..ec7d4bd0d8c0 100644
--- a/services/usb/java/com/android/server/usb/UsbPortManager.java
+++ b/services/usb/java/com/android/server/usb/UsbPortManager.java
@@ -34,6 +34,7 @@ import android.annotation.NonNull;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
@@ -65,11 +66,11 @@ import android.service.usb.UsbServiceProto;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Slog;
-import android.util.StatsLog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.notification.SystemNotificationChannels;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.dump.DualDumpOutputStream;
import com.android.server.FgThread;
@@ -225,8 +226,8 @@ public class UsbPortManager {
Intent intent = new Intent();
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- intent.setClassName("com.android.systemui",
- "com.android.systemui.usb.UsbContaminantActivity");
+ intent.setComponent(ComponentName.unflattenFromString(r.getString(
+ com.android.internal.R.string.config_usbContaminantActivity)));
intent.putExtra(UsbManager.EXTRA_PORT, ParcelableUsbPort.of(currentPortInfo.mUsbPort));
PendingIntent pi = PendingIntent.getActivityAsUser(mContext, 0,
@@ -1039,8 +1040,9 @@ public class UsbPortManager {
if (mConnected.containsKey(portInfo.mUsbPort.getId())) {
//Previous logged a connected. Set it to disconnected.
if (mConnected.get(portInfo.mUsbPort.getId())) {
- StatsLog.write(StatsLog.USB_CONNECTOR_STATE_CHANGED,
- StatsLog.USB_CONNECTOR_STATE_CHANGED__STATE__STATE_DISCONNECTED,
+ FrameworkStatsLog.write(FrameworkStatsLog.USB_CONNECTOR_STATE_CHANGED,
+ FrameworkStatsLog
+ .USB_CONNECTOR_STATE_CHANGED__STATE__STATE_DISCONNECTED,
portInfo.mUsbPort.getId(), portInfo.mLastConnectDurationMillis);
}
mConnected.remove(portInfo.mUsbPort.getId());
@@ -1050,7 +1052,7 @@ public class UsbPortManager {
//Previous logged a contaminant detected. Set it to not detected.
if ((mContaminantStatus.get(portInfo.mUsbPort.getId())
== UsbPortStatus.CONTAMINANT_DETECTION_DETECTED)) {
- StatsLog.write(StatsLog.USB_CONTAMINANT_REPORTED,
+ FrameworkStatsLog.write(FrameworkStatsLog.USB_CONTAMINANT_REPORTED,
portInfo.mUsbPort.getId(),
convertContaminantDetectionStatusToProto(
UsbPortStatus.CONTAMINANT_DETECTION_NOT_DETECTED));
@@ -1064,10 +1066,10 @@ public class UsbPortManager {
|| (mConnected.get(portInfo.mUsbPort.getId())
!= portInfo.mUsbPortStatus.isConnected())) {
mConnected.put(portInfo.mUsbPort.getId(), portInfo.mUsbPortStatus.isConnected());
- StatsLog.write(StatsLog.USB_CONNECTOR_STATE_CHANGED,
+ FrameworkStatsLog.write(FrameworkStatsLog.USB_CONNECTOR_STATE_CHANGED,
portInfo.mUsbPortStatus.isConnected()
- ? StatsLog.USB_CONNECTOR_STATE_CHANGED__STATE__STATE_CONNECTED :
- StatsLog.USB_CONNECTOR_STATE_CHANGED__STATE__STATE_DISCONNECTED,
+ ? FrameworkStatsLog.USB_CONNECTOR_STATE_CHANGED__STATE__STATE_CONNECTED :
+ FrameworkStatsLog.USB_CONNECTOR_STATE_CHANGED__STATE__STATE_DISCONNECTED,
portInfo.mUsbPort.getId(), portInfo.mLastConnectDurationMillis);
}
@@ -1076,7 +1078,7 @@ public class UsbPortManager {
!= portInfo.mUsbPortStatus.getContaminantDetectionStatus())) {
mContaminantStatus.put(portInfo.mUsbPort.getId(),
portInfo.mUsbPortStatus.getContaminantDetectionStatus());
- StatsLog.write(StatsLog.USB_CONTAMINANT_REPORTED,
+ FrameworkStatsLog.write(FrameworkStatsLog.USB_CONTAMINANT_REPORTED,
portInfo.mUsbPort.getId(),
convertContaminantDetectionStatusToProto(
portInfo.mUsbPortStatus.getContaminantDetectionStatus()));
diff --git a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
index 74c3939a1b1c..d7b6b5d0d36a 100644
--- a/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbProfileGroupSettingsManager.java
@@ -46,6 +46,8 @@ import android.service.usb.UsbProfileGroupSettingsManagerProto;
import android.service.usb.UsbSettingsAccessoryPreferenceProto;
import android.service.usb.UsbSettingsDevicePreferenceProto;
import android.service.usb.UserPackageProto;
+import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.AtomicFile;
import android.util.Log;
import android.util.Slog;
@@ -70,6 +72,7 @@ import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.net.ProtocolException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
@@ -102,10 +105,20 @@ class UsbProfileGroupSettingsManager {
@GuardedBy("mLock")
private final HashMap<DeviceFilter, UserPackage> mDevicePreferenceMap = new HashMap<>();
+ /** Maps DeviceFilter to set of UserPackages not to ask for launch preference anymore */
+ @GuardedBy("mLock")
+ private final ArrayMap<DeviceFilter, ArraySet<UserPackage>> mDevicePreferenceDeniedMap =
+ new ArrayMap<>();
+
/** Maps AccessoryFilter to user preferred application package */
@GuardedBy("mLock")
private final HashMap<AccessoryFilter, UserPackage> mAccessoryPreferenceMap = new HashMap<>();
+ /** Maps AccessoryFilter to set of UserPackages not to ask for launch preference anymore */
+ @GuardedBy("mLock")
+ private final ArrayMap<AccessoryFilter, ArraySet<UserPackage>> mAccessoryPreferenceDeniedMap =
+ new ArrayMap<>();
+
private final Object mLock = new Object();
/**
@@ -248,11 +261,11 @@ class UsbProfileGroupSettingsManager {
}
/**
- * Remove all defaults for a user.
+ * Remove all defaults and denied packages for a user.
*
- * @param userToRemove The user the defaults belong to.
+ * @param userToRemove The user
*/
- void removeAllDefaultsForUser(@NonNull UserHandle userToRemove) {
+ void removeUser(@NonNull UserHandle userToRemove) {
synchronized (mLock) {
boolean needToPersist = false;
Iterator<Map.Entry<DeviceFilter, UserPackage>> devicePreferenceIt = mDevicePreferenceMap
@@ -277,6 +290,28 @@ class UsbProfileGroupSettingsManager {
}
}
+ int numEntries = mDevicePreferenceDeniedMap.size();
+ for (int i = 0; i < numEntries; i++) {
+ ArraySet<UserPackage> userPackages = mDevicePreferenceDeniedMap.valueAt(i);
+ for (int j = userPackages.size() - 1; j >= 0; j--) {
+ if (userPackages.valueAt(j).user.equals(userToRemove)) {
+ userPackages.removeAt(j);
+ needToPersist = true;
+ }
+ }
+ }
+
+ numEntries = mAccessoryPreferenceDeniedMap.size();
+ for (int i = 0; i < numEntries; i++) {
+ ArraySet<UserPackage> userPackages = mAccessoryPreferenceDeniedMap.valueAt(i);
+ for (int j = userPackages.size() - 1; j >= 0; j--) {
+ if (userPackages.valueAt(j).user.equals(userToRemove)) {
+ userPackages.removeAt(j);
+ needToPersist = true;
+ }
+ }
+ }
+
if (needToPersist) {
scheduleWriteSettingsLocked();
}
@@ -284,7 +319,7 @@ class UsbProfileGroupSettingsManager {
}
private void readPreference(XmlPullParser parser)
- throws XmlPullParserException, IOException {
+ throws IOException, XmlPullParserException {
String packageName = null;
// If not set, assume it to be the parent profile
@@ -317,9 +352,70 @@ class UsbProfileGroupSettingsManager {
XmlUtils.nextElement(parser);
}
+ private void readPreferenceDeniedList(@NonNull XmlPullParser parser)
+ throws IOException, XmlPullParserException {
+ int outerDepth = parser.getDepth();
+ if (!XmlUtils.nextElementWithin(parser, outerDepth)) {
+ return;
+ }
+
+ if ("usb-device".equals(parser.getName())) {
+ DeviceFilter filter = DeviceFilter.read(parser);
+ while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+ if ("user-package".equals(parser.getName())) {
+ try {
+ int userId = XmlUtils.readIntAttribute(parser, "user");
+
+ String packageName = XmlUtils.readStringAttribute(parser, "package");
+ if (packageName == null) {
+ Slog.e(TAG, "Unable to parse package name");
+ }
+
+ ArraySet<UserPackage> set = mDevicePreferenceDeniedMap.get(filter);
+ if (set == null) {
+ set = new ArraySet<>();
+ mDevicePreferenceDeniedMap.put(filter, set);
+ }
+ set.add(new UserPackage(packageName, UserHandle.of(userId)));
+ } catch (ProtocolException e) {
+ Slog.e(TAG, "Unable to parse user id", e);
+ }
+ }
+ }
+ } else if ("usb-accessory".equals(parser.getName())) {
+ AccessoryFilter filter = AccessoryFilter.read(parser);
+
+ while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+ if ("user-package".equals(parser.getName())) {
+ try {
+ int userId = XmlUtils.readIntAttribute(parser, "user");
+
+ String packageName = XmlUtils.readStringAttribute(parser, "package");
+ if (packageName == null) {
+ Slog.e(TAG, "Unable to parse package name");
+ }
+
+ ArraySet<UserPackage> set = mAccessoryPreferenceDeniedMap.get(filter);
+ if (set == null) {
+ set = new ArraySet<>();
+ mAccessoryPreferenceDeniedMap.put(filter, set);
+ }
+ set.add(new UserPackage(packageName, UserHandle.of(userId)));
+ } catch (ProtocolException e) {
+ Slog.e(TAG, "Unable to parse user id", e);
+ }
+ }
+ }
+ }
+
+ while (parser.getDepth() > outerDepth) {
+ parser.nextTag(); // ignore unknown tags
+ }
+ }
+
/**
* Upgrade any single-user settings from {@link #sSingleUserSettingsFile}.
- * Should only by called by owner.
+ * Should only be called by owner.
*/
@GuardedBy("mLock")
private void upgradeSingleUserLocked() {
@@ -373,6 +469,8 @@ class UsbProfileGroupSettingsManager {
String tagName = parser.getName();
if ("preference".equals(tagName)) {
readPreference(parser);
+ } else if ("preference-denied-list".equals(tagName)) {
+ readPreferenceDeniedList(parser);
} else {
XmlUtils.nextElement(parser);
}
@@ -436,6 +534,46 @@ class UsbProfileGroupSettingsManager {
serializer.endTag(null, "preference");
}
+ int numEntries = mDevicePreferenceDeniedMap.size();
+ for (int i = 0; i < numEntries; i++) {
+ DeviceFilter filter = mDevicePreferenceDeniedMap.keyAt(i);
+ ArraySet<UserPackage> userPackageSet = mDevicePreferenceDeniedMap
+ .valueAt(i);
+ serializer.startTag(null, "preference-denied-list");
+ filter.write(serializer);
+
+ int numUserPackages = userPackageSet.size();
+ for (int j = 0; j < numUserPackages; j++) {
+ UserPackage userPackage = userPackageSet.valueAt(j);
+ serializer.startTag(null, "user-package");
+ serializer.attribute(null, "user",
+ String.valueOf(getSerial(userPackage.user)));
+ serializer.attribute(null, "package", userPackage.packageName);
+ serializer.endTag(null, "user-package");
+ }
+ serializer.endTag(null, "preference-denied-list");
+ }
+
+ numEntries = mAccessoryPreferenceDeniedMap.size();
+ for (int i = 0; i < numEntries; i++) {
+ AccessoryFilter filter = mAccessoryPreferenceDeniedMap.keyAt(i);
+ ArraySet<UserPackage> userPackageSet =
+ mAccessoryPreferenceDeniedMap.valueAt(i);
+ serializer.startTag(null, "preference-denied-list");
+ filter.write(serializer);
+
+ int numUserPackages = userPackageSet.size();
+ for (int j = 0; j < numUserPackages; j++) {
+ UserPackage userPackage = userPackageSet.valueAt(j);
+ serializer.startTag(null, "user-package");
+ serializer.attribute(null, "user",
+ String.valueOf(getSerial(userPackage.user)));
+ serializer.attribute(null, "package", userPackage.packageName);
+ serializer.endTag(null, "user-package");
+ }
+ serializer.endTag(null, "preference-denied-list");
+ }
+
serializer.endTag(null, "settings");
serializer.endDocument();
@@ -783,7 +921,7 @@ class UsbProfileGroupSettingsManager {
return;
}
- mSettingsManager.getSettingsForUser(UserHandle.getUserId(appInfo.uid))
+ mSettingsManager.mUsbService.getPermissionsForUser(UserHandle.getUserId(appInfo.uid))
.grantDevicePermission(device, appInfo.uid);
Intent activityIntent = new Intent(intent);
@@ -834,6 +972,25 @@ class UsbProfileGroupSettingsManager {
private void resolveActivity(@NonNull Intent intent, @NonNull ArrayList<ResolveInfo> matches,
@Nullable ActivityInfo defaultActivity, @Nullable UsbDevice device,
@Nullable UsbAccessory accessory) {
+ // Remove all matches which are on the denied list
+ ArraySet deniedPackages = null;
+ if (device != null) {
+ deniedPackages = mDevicePreferenceDeniedMap.get(new DeviceFilter(device));
+ } else if (accessory != null) {
+ deniedPackages = mAccessoryPreferenceDeniedMap.get(new AccessoryFilter(accessory));
+ }
+ if (deniedPackages != null) {
+ for (int i = matches.size() - 1; i >= 0; i--) {
+ ResolveInfo match = matches.get(i);
+ String packageName = match.activityInfo.packageName;
+ UserHandle user = UserHandle
+ .getUserHandleForUid(match.activityInfo.applicationInfo.uid);
+ if (deniedPackages.contains(new UserPackage(packageName, user))) {
+ matches.remove(i);
+ }
+ }
+ }
+
// don't show the resolver activity if there are no choices available
if (matches.size() == 0) {
if (accessory != null) {
@@ -844,14 +1001,15 @@ class UsbProfileGroupSettingsManager {
}
if (defaultActivity != null) {
- UsbUserSettingsManager defaultRIUserSettings = mSettingsManager.getSettingsForUser(
- UserHandle.getUserId(defaultActivity.applicationInfo.uid));
+ UsbUserPermissionManager defaultRIUserPermissions =
+ mSettingsManager.mUsbService.getPermissionsForUser(
+ UserHandle.getUserId(defaultActivity.applicationInfo.uid));
// grant permission for default activity
if (device != null) {
- defaultRIUserSettings.
- grantDevicePermission(device, defaultActivity.applicationInfo.uid);
+ defaultRIUserPermissions
+ .grantDevicePermission(device, defaultActivity.applicationInfo.uid);
} else if (accessory != null) {
- defaultRIUserSettings.grantAccessoryPermission(accessory,
+ defaultRIUserPermissions.grantAccessoryPermission(accessory,
defaultActivity.applicationInfo.uid);
}
@@ -1075,6 +1233,156 @@ class UsbProfileGroupSettingsManager {
}
/**
+ * Add package to the denied for handling a device
+ *
+ * @param device the device to add to the denied
+ * @param packageNames the packages to not become handler
+ * @param user the user
+ */
+ void addDevicePackagesToDenied(@NonNull UsbDevice device, @NonNull String[] packageNames,
+ @NonNull UserHandle user) {
+ if (packageNames.length == 0) {
+ return;
+ }
+ DeviceFilter filter = new DeviceFilter(device);
+
+ synchronized (mLock) {
+ ArraySet<UserPackage> userPackages;
+ if (mDevicePreferenceDeniedMap.containsKey(filter)) {
+ userPackages = mDevicePreferenceDeniedMap.get(filter);
+ } else {
+ userPackages = new ArraySet<>();
+ mDevicePreferenceDeniedMap.put(filter, userPackages);
+ }
+
+ boolean shouldWrite = false;
+ for (String packageName : packageNames) {
+ UserPackage userPackage = new UserPackage(packageName, user);
+ if (!userPackages.contains(userPackage)) {
+ userPackages.add(userPackage);
+ shouldWrite = true;
+ }
+ }
+
+ if (shouldWrite) {
+ scheduleWriteSettingsLocked();
+ }
+ }
+ }
+
+ /**
+ * Add package to the denied for handling a accessory
+ *
+ * @param accessory the accessory to add to the denied
+ * @param packageNames the packages to not become handler
+ * @param user the user
+ */
+ void addAccessoryPackagesToDenied(@NonNull UsbAccessory accessory,
+ @NonNull String[] packageNames, @NonNull UserHandle user) {
+ if (packageNames.length == 0) {
+ return;
+ }
+ AccessoryFilter filter = new AccessoryFilter(accessory);
+
+ synchronized (mLock) {
+ ArraySet<UserPackage> userPackages;
+ if (mAccessoryPreferenceDeniedMap.containsKey(filter)) {
+ userPackages = mAccessoryPreferenceDeniedMap.get(filter);
+ } else {
+ userPackages = new ArraySet<>();
+ mAccessoryPreferenceDeniedMap.put(filter, userPackages);
+ }
+
+ boolean shouldWrite = false;
+ for (String packageName : packageNames) {
+ UserPackage userPackage = new UserPackage(packageName, user);
+ if (!userPackages.contains(userPackage)) {
+ userPackages.add(userPackage);
+ shouldWrite = true;
+ }
+ }
+
+ if (shouldWrite) {
+ scheduleWriteSettingsLocked();
+ }
+ }
+ }
+
+ /**
+ * Remove UserPackage from the denied for handling a device
+ *
+ * @param device the device to remove denied packages from
+ * @param packageName the packages to remove
+ * @param user the user
+ */
+ void removeDevicePackagesFromDenied(@NonNull UsbDevice device, @NonNull String[] packageNames,
+ @NonNull UserHandle user) {
+ DeviceFilter filter = new DeviceFilter(device);
+
+ synchronized (mLock) {
+ ArraySet<UserPackage> userPackages = mDevicePreferenceDeniedMap.get(filter);
+
+ if (userPackages != null) {
+ boolean shouldWrite = false;
+ for (String packageName : packageNames) {
+ UserPackage userPackage = new UserPackage(packageName, user);
+
+ if (userPackages.contains(userPackage)) {
+ userPackages.remove(userPackage);
+ shouldWrite = true;
+
+ if (userPackages.size() == 0) {
+ mDevicePreferenceDeniedMap.remove(filter);
+ break;
+ }
+ }
+ }
+
+ if (shouldWrite) {
+ scheduleWriteSettingsLocked();
+ }
+ }
+ }
+ }
+
+ /**
+ * Remove UserPackage from the denied for handling a accessory
+ *
+ * @param accessory the accessory to remove denied packages from
+ * @param packageName the packages to remove
+ * @param user the user
+ */
+ void removeAccessoryPackagesFromDenied(@NonNull UsbAccessory accessory,
+ @NonNull String[] packageNames, @NonNull UserHandle user) {
+ AccessoryFilter filter = new AccessoryFilter(accessory);
+
+ synchronized (mLock) {
+ ArraySet<UserPackage> userPackages = mAccessoryPreferenceDeniedMap.get(filter);
+
+ if (userPackages != null) {
+ boolean shouldWrite = false;
+ for (String packageName : packageNames) {
+ UserPackage userPackage = new UserPackage(packageName, user);
+
+ if (userPackages.contains(userPackage)) {
+ userPackages.remove(userPackage);
+ shouldWrite = true;
+
+ if (userPackages.size() == 0) {
+ mAccessoryPreferenceDeniedMap.remove(filter);
+ break;
+ }
+ }
+ }
+
+ if (shouldWrite) {
+ scheduleWriteSettingsLocked();
+ }
+ }
+ }
+ }
+
+ /**
* Set a package as default handler for a accessory.
*
* @param accessory The accessory that should be handled by default
diff --git a/services/usb/java/com/android/server/usb/UsbSerialReader.java b/services/usb/java/com/android/server/usb/UsbSerialReader.java
index 077d6b9bd62d..095e8e9b7b5b 100644
--- a/services/usb/java/com/android/server/usb/UsbSerialReader.java
+++ b/services/usb/java/com/android/server/usb/UsbSerialReader.java
@@ -39,7 +39,7 @@ import com.android.internal.util.ArrayUtils;
class UsbSerialReader extends IUsbSerialReader.Stub {
private final @Nullable String mSerialNumber;
private final @NonNull Context mContext;
- private final @NonNull UsbSettingsManager mSettingsManager;
+ private final @NonNull UsbPermissionManager mPermissionManager;
private Object mDevice;
@@ -51,10 +51,10 @@ class UsbSerialReader extends IUsbSerialReader.Stub {
* @param settingsManager The USB settings manager
* @param serialNumber The serial number that might be read
*/
- UsbSerialReader(@NonNull Context context, @NonNull UsbSettingsManager settingsManager,
+ UsbSerialReader(@NonNull Context context, @NonNull UsbPermissionManager permissionManager,
@Nullable String serialNumber) {
mContext = context;
- mSettingsManager = settingsManager;
+ mPermissionManager = permissionManager;
mSerialNumber = serialNumber;
}
@@ -75,12 +75,14 @@ class UsbSerialReader extends IUsbSerialReader.Stub {
if (uid != Process.SYSTEM_UID) {
enforcePackageBelongsToUid(uid, packageName);
+ UserHandle user = Binder.getCallingUserHandle();
int packageTargetSdkVersion;
long token = Binder.clearCallingIdentity();
try {
PackageInfo pkg;
try {
- pkg = mContext.getPackageManager().getPackageInfo(packageName, 0);
+ pkg = mContext.getPackageManager()
+ .getPackageInfoAsUser(packageName, 0, user.getIdentifier());
} catch (PackageManager.NameNotFoundException e) {
throw new RemoteException("package " + packageName + " cannot be found");
}
@@ -89,13 +91,14 @@ class UsbSerialReader extends IUsbSerialReader.Stub {
if (packageTargetSdkVersion >= Build.VERSION_CODES.Q) {
if (mContext.checkPermission(android.Manifest.permission.MANAGE_USB, pid, uid)
== PackageManager.PERMISSION_DENIED) {
- UsbUserSettingsManager settings = mSettingsManager.getSettingsForUser(
- UserHandle.getUserId(uid));
+ int userId = UserHandle.getUserId(uid);
if (mDevice instanceof UsbDevice) {
- settings.checkPermission((UsbDevice) mDevice, packageName, pid, uid);
+ mPermissionManager.getPermissionsForUser(userId)
+ .checkPermission((UsbDevice) mDevice, packageName, pid, uid);
} else {
- settings.checkPermission((UsbAccessory) mDevice, uid);
+ mPermissionManager.getPermissionsForUser(userId)
+ .checkPermission((UsbAccessory) mDevice, uid);
}
}
}
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index 13275f34ee1a..14c7f04b9e82 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -56,6 +56,8 @@ import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.internal.util.dump.DualDumpOutputStream;
+import com.android.server.FgThread;
+import com.android.server.SystemServerInitThreadPool;
import com.android.server.SystemService;
import java.io.File;
@@ -64,6 +66,8 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.CompletableFuture;
/**
* UsbService manages all USB related state, including both host and device support.
@@ -74,6 +78,9 @@ public class UsbService extends IUsbManager.Stub {
public static class Lifecycle extends SystemService {
private UsbService mUsbService;
+ private final CompletableFuture<Void> mOnStartFinished = new CompletableFuture<>();
+ private final CompletableFuture<Void> mOnActivityManagerPhaseFinished =
+ new CompletableFuture<>();
public Lifecycle(Context context) {
super(context);
@@ -81,32 +88,41 @@ public class UsbService extends IUsbManager.Stub {
@Override
public void onStart() {
- mUsbService = new UsbService(getContext());
- publishBinderService(Context.USB_SERVICE, mUsbService);
+ SystemServerInitThreadPool.submit(() -> {
+ mUsbService = new UsbService(getContext());
+ publishBinderService(Context.USB_SERVICE, mUsbService);
+ mOnStartFinished.complete(null);
+ }, "UsbService$Lifecycle#onStart");
}
@Override
public void onBootPhase(int phase) {
if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
- mUsbService.systemReady();
+ SystemServerInitThreadPool.submit(() -> {
+ mOnStartFinished.join();
+ mUsbService.systemReady();
+ mOnActivityManagerPhaseFinished.complete(null);
+ }, "UsbService$Lifecycle#onBootPhase");
} else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
+ mOnActivityManagerPhaseFinished.join();
mUsbService.bootCompleted();
}
}
@Override
- public void onSwitchUser(int newUserId) {
- mUsbService.onSwitchUser(newUserId);
+ public void onUserSwitching(TargetUser from, TargetUser to) {
+ FgThread.getHandler()
+ .postAtFrontOfQueue(() -> mUsbService.onSwitchUser(to.getUserIdentifier()));
}
@Override
- public void onStopUser(int userHandle) {
- mUsbService.onStopUser(UserHandle.of(userHandle));
+ public void onUserStopping(TargetUser userInfo) {
+ mUsbService.onStopUser(userInfo.getUserHandle());
}
@Override
- public void onUnlockUser(int userHandle) {
- mUsbService.onUnlockUser(userHandle);
+ public void onUserUnlocking(TargetUser userInfo) {
+ mUsbService.onUnlockUser(userInfo.getUserIdentifier());
}
}
@@ -121,6 +137,7 @@ public class UsbService extends IUsbManager.Stub {
private final UsbAlsaManager mAlsaManager;
private final UsbSettingsManager mSettingsManager;
+ private final UsbPermissionManager mPermissionManager;
/**
* The user id of the current user. There might be several profiles (with separate user ids)
@@ -131,23 +148,35 @@ public class UsbService extends IUsbManager.Stub {
private final Object mLock = new Object();
- private UsbUserSettingsManager getSettingsForUser(@UserIdInt int userIdInt) {
- return mSettingsManager.getSettingsForUser(userIdInt);
+ /**
+ * @return the {@link UsbUserSettingsManager} for the given userId
+ */
+ UsbUserSettingsManager getSettingsForUser(@UserIdInt int userId) {
+ return mSettingsManager.getSettingsForUser(userId);
+ }
+
+ /**
+ * @return the {@link UsbUserPermissionManager} for the given userId
+ */
+ UsbUserPermissionManager getPermissionsForUser(@UserIdInt int userId) {
+ return mPermissionManager.getPermissionsForUser(userId);
}
public UsbService(Context context) {
mContext = context;
mUserManager = context.getSystemService(UserManager.class);
- mSettingsManager = new UsbSettingsManager(context);
+ mSettingsManager = new UsbSettingsManager(context, this);
+ mPermissionManager = new UsbPermissionManager(context, this);
mAlsaManager = new UsbAlsaManager(context);
final PackageManager pm = mContext.getPackageManager();
if (pm.hasSystemFeature(PackageManager.FEATURE_USB_HOST)) {
- mHostManager = new UsbHostManager(context, mAlsaManager, mSettingsManager);
+ mHostManager = new UsbHostManager(context, mAlsaManager, mPermissionManager);
}
if (new File("/sys/class/android_usb").exists()) {
- mDeviceManager = new UsbDeviceManager(context, mAlsaManager, mSettingsManager);
+ mDeviceManager = new UsbDeviceManager(context, mAlsaManager, mSettingsManager,
+ mPermissionManager);
}
if (mHostManager != null || mDeviceManager != null) {
mPortManager = new UsbPortManager(context);
@@ -256,7 +285,7 @@ public class UsbService extends IUsbManager.Stub {
try {
synchronized (mLock) {
if (mUserManager.isSameProfileGroup(user, mCurrentUserId)) {
- fd = mHostManager.openDevice(deviceName, getSettingsForUser(user),
+ fd = mHostManager.openDevice(deviceName, getPermissionsForUser(user),
packageName, pid, uid);
} else {
Slog.w(TAG, "Cannot open " + deviceName + " for user " + user
@@ -293,7 +322,7 @@ public class UsbService extends IUsbManager.Stub {
try {
synchronized (mLock) {
if (mUserManager.isSameProfileGroup(user, mCurrentUserId)) {
- return mDeviceManager.openAccessory(accessory, getSettingsForUser(user),
+ return mDeviceManager.openAccessory(accessory, getPermissionsForUser(user),
uid);
} else {
Slog.w(TAG, "Cannot open " + accessory + " for user " + user
@@ -317,7 +346,7 @@ public class UsbService extends IUsbManager.Stub {
@Override
public void setDevicePackage(UsbDevice device, String packageName, int userId) {
- device = Preconditions.checkNotNull(device);
+ Objects.requireNonNull(device);
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
@@ -333,7 +362,7 @@ public class UsbService extends IUsbManager.Stub {
@Override
public void setAccessoryPackage(UsbAccessory accessory, String packageName, int userId) {
- accessory = Preconditions.checkNotNull(accessory);
+ Objects.requireNonNull(accessory);
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
@@ -349,6 +378,112 @@ public class UsbService extends IUsbManager.Stub {
}
@Override
+ public void addDevicePackagesToPreferenceDenied(UsbDevice device, String[] packageNames,
+ UserHandle user) {
+ Objects.requireNonNull(device);
+ packageNames = Preconditions.checkArrayElementsNotNull(packageNames, "packageNames");
+ Objects.requireNonNull(user);
+
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mSettingsManager.getSettingsForProfileGroup(user)
+ .addDevicePackagesToDenied(device, packageNames, user);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void addAccessoryPackagesToPreferenceDenied(UsbAccessory accessory,
+ String[] packageNames, UserHandle user) {
+ Objects.requireNonNull(accessory);
+ packageNames = Preconditions.checkArrayElementsNotNull(packageNames, "packageNames");
+ Objects.requireNonNull(user);
+
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mSettingsManager.getSettingsForProfileGroup(user)
+ .addAccessoryPackagesToDenied(accessory, packageNames, user);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void removeDevicePackagesFromPreferenceDenied(UsbDevice device, String[] packageNames,
+ UserHandle user) {
+ Objects.requireNonNull(device);
+ packageNames = Preconditions.checkArrayElementsNotNull(packageNames, "packageNames");
+ Objects.requireNonNull(user);
+
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mSettingsManager.getSettingsForProfileGroup(user)
+ .removeDevicePackagesFromDenied(device, packageNames, user);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void removeAccessoryPackagesFromPreferenceDenied(UsbAccessory accessory,
+ String[] packageNames, UserHandle user) {
+ Objects.requireNonNull(accessory);
+ packageNames = Preconditions.checkArrayElementsNotNull(packageNames, "packageNames");
+ Objects.requireNonNull(user);
+
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mSettingsManager.getSettingsForProfileGroup(user)
+ .removeAccessoryPackagesFromDenied(accessory, packageNames, user);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void setDevicePersistentPermission(UsbDevice device, int uid, UserHandle user,
+ boolean shouldBeGranted) {
+ Objects.requireNonNull(device);
+ Objects.requireNonNull(user);
+
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mPermissionManager.getPermissionsForUser(user).setDevicePersistentPermission(device,
+ uid, shouldBeGranted);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void setAccessoryPersistentPermission(UsbAccessory accessory, int uid,
+ UserHandle user, boolean shouldBeGranted) {
+ Objects.requireNonNull(accessory);
+ Objects.requireNonNull(user);
+
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mPermissionManager.getPermissionsForUser(user).setAccessoryPersistentPermission(
+ accessory, uid, shouldBeGranted);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
public boolean hasDevicePermission(UsbDevice device, String packageName) {
final int uid = Binder.getCallingUid();
final int pid = Binder.getCallingPid();
@@ -356,7 +491,7 @@ public class UsbService extends IUsbManager.Stub {
final long token = Binder.clearCallingIdentity();
try {
- return getSettingsForUser(userId).hasPermission(device, packageName, pid, uid);
+ return getPermissionsForUser(userId).hasPermission(device, packageName, pid, uid);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -369,7 +504,7 @@ public class UsbService extends IUsbManager.Stub {
final long token = Binder.clearCallingIdentity();
try {
- return getSettingsForUser(userId).hasPermission(accessory, uid);
+ return getPermissionsForUser(userId).hasPermission(accessory, uid);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -383,7 +518,7 @@ public class UsbService extends IUsbManager.Stub {
final long token = Binder.clearCallingIdentity();
try {
- getSettingsForUser(userId).requestPermission(device, packageName, pi, pid, uid);
+ getPermissionsForUser(userId).requestPermission(device, packageName, pi, pid, uid);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -397,7 +532,7 @@ public class UsbService extends IUsbManager.Stub {
final long token = Binder.clearCallingIdentity();
try {
- getSettingsForUser(userId).requestPermission(accessory, packageName, pi, uid);
+ getPermissionsForUser(userId).requestPermission(accessory, packageName, pi, uid);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -410,7 +545,7 @@ public class UsbService extends IUsbManager.Stub {
final long token = Binder.clearCallingIdentity();
try {
- getSettingsForUser(userId).grantDevicePermission(device, uid);
+ getPermissionsForUser(userId).grantDevicePermission(device, uid);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -423,7 +558,7 @@ public class UsbService extends IUsbManager.Stub {
final long token = Binder.clearCallingIdentity();
try {
- getSettingsForUser(userId).grantAccessoryPermission(accessory, uid);
+ getPermissionsForUser(userId).grantAccessoryPermission(accessory, uid);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -503,6 +638,19 @@ public class UsbService extends IUsbManager.Stub {
}
@Override
+ public void resetUsbGadget() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
+ Preconditions.checkNotNull(mDeviceManager, "DeviceManager must not be null");
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mDeviceManager.resetUsbGadget();
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ @Override
public List<ParcelableUsbPort> getPorts() {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
@@ -529,7 +677,7 @@ public class UsbService extends IUsbManager.Stub {
@Override
public UsbPortStatus getPortStatus(String portId) {
- Preconditions.checkNotNull(portId, "portId must not be null");
+ Objects.requireNonNull(portId, "portId must not be null");
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
final long ident = Binder.clearCallingIdentity();
@@ -542,7 +690,7 @@ public class UsbService extends IUsbManager.Stub {
@Override
public void setPortRoles(String portId, int powerRole, int dataRole) {
- Preconditions.checkNotNull(portId, "portId must not be null");
+ Objects.requireNonNull(portId, "portId must not be null");
UsbPort.checkRoles(powerRole, dataRole);
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
@@ -558,7 +706,7 @@ public class UsbService extends IUsbManager.Stub {
@Override
public void enableContaminantDetection(String portId, boolean enable) {
- Preconditions.checkNotNull(portId, "portId must not be null");
+ Objects.requireNonNull(portId, "portId must not be null");
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
final long ident = Binder.clearCallingIdentity();
@@ -624,6 +772,8 @@ public class UsbService extends IUsbManager.Stub {
mSettingsManager.dump(dump, "settings_manager",
UsbServiceDumpProto.SETTINGS_MANAGER);
+ mPermissionManager.dump(dump, "permissions_manager",
+ UsbServiceDumpProto.PERMISSIONS_MANAGER);
dump.flush();
} else if ("set-port-roles".equals(args[0]) && args.length == 4) {
final String portId = args[1];
diff --git a/services/usb/java/com/android/server/usb/UsbSettingsManager.java b/services/usb/java/com/android/server/usb/UsbSettingsManager.java
index 27566f04c280..7b677eea6b8f 100644
--- a/services/usb/java/com/android/server/usb/UsbSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbSettingsManager.java
@@ -19,15 +19,10 @@ package com.android.server.usb;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.content.Context;
-import android.content.Intent;
import android.content.pm.UserInfo;
-import android.hardware.usb.UsbAccessory;
-import android.hardware.usb.UsbDevice;
-import android.hardware.usb.UsbManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.service.usb.UsbSettingsManagerProto;
-import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
@@ -59,8 +54,11 @@ class UsbSettingsManager {
private UserManager mUserManager;
private UsbHandlerManager mUsbHandlerManager;
- public UsbSettingsManager(@NonNull Context context) {
+ final UsbService mUsbService;
+
+ UsbSettingsManager(@NonNull Context context, UsbService usbService) {
mContext = context;
+ mUsbService = usbService;
mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
mUsbHandlerManager = new UsbHandlerManager(context);
}
@@ -76,8 +74,7 @@ class UsbSettingsManager {
synchronized (mSettingsByUser) {
UsbUserSettingsManager settings = mSettingsByUser.get(userId);
if (settings == null) {
- settings = new UsbUserSettingsManager(mContext, UserHandle.of(userId),
- new UsbPermissionManager(mContext, UserHandle.of(userId)));
+ settings = new UsbUserSettingsManager(mContext, UserHandle.of(userId));
mSettingsByUser.put(userId, settings);
}
return settings;
@@ -133,7 +130,7 @@ class UsbSettingsManager {
// it from all profile groups.
int numProfileGroups = mSettingsByProfileGroup.size();
for (int i = 0; i < numProfileGroups; i++) {
- mSettingsByProfileGroup.valueAt(i).removeAllDefaultsForUser(userToRemove);
+ mSettingsByProfileGroup.valueAt(i).removeUser(userToRemove);
}
}
}
@@ -164,46 +161,4 @@ class UsbSettingsManager {
dump.end(token);
}
-
- /**
- * Remove temporary access permission and broadcast that a device was removed.
- *
- * @param device The device that is removed
- */
- void usbDeviceRemoved(@NonNull UsbDevice device) {
- synchronized (mSettingsByUser) {
- for (int i = 0; i < mSettingsByUser.size(); i++) {
- // clear temporary permissions for the device
- mSettingsByUser.valueAt(i).removeDevicePermissions(device);
- }
- }
-
- Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_DETACHED);
- intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
- intent.putExtra(UsbManager.EXTRA_DEVICE, device);
-
- if (DEBUG) {
- Slog.d(LOG_TAG, "usbDeviceRemoved, sending " + intent);
- }
- mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
- }
-
- /**
- * Remove temporary access permission and broadcast that a accessory was removed.
- *
- * @param accessory The accessory that is removed
- */
- void usbAccessoryRemoved(@NonNull UsbAccessory accessory) {
- synchronized (mSettingsByUser) {
- for (int i = 0; i < mSettingsByUser.size(); i++) {
- // clear temporary permissions for the accessory
- mSettingsByUser.valueAt(i).removeAccessoryPermissions(accessory);
- }
- }
-
- Intent intent = new Intent(UsbManager.ACTION_USB_ACCESSORY_DETACHED);
- intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
- intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
- mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
- }
}
diff --git a/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java b/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java
new file mode 100644
index 000000000000..333edfd91b16
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/UsbUserPermissionManager.java
@@ -0,0 +1,758 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.usb;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.PendingIntent;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.hardware.usb.AccessoryFilter;
+import android.hardware.usb.DeviceFilter;
+import android.hardware.usb.UsbAccessory;
+import android.hardware.usb.UsbConstants;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbInterface;
+import android.hardware.usb.UsbManager;
+import android.os.AsyncTask;
+import android.os.Binder;
+import android.os.Environment;
+import android.os.Process;
+import android.os.UserHandle;
+import android.service.usb.UsbAccessoryPermissionProto;
+import android.service.usb.UsbAccessoryPersistentPermissionProto;
+import android.service.usb.UsbDevicePermissionProto;
+import android.service.usb.UsbDevicePersistentPermissionProto;
+import android.service.usb.UsbUidPermissionProto;
+import android.service.usb.UsbUserPermissionsManagerProto;
+import android.util.ArrayMap;
+import android.util.AtomicFile;
+import android.util.Slog;
+import android.util.SparseBooleanArray;
+import android.util.Xml;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.XmlUtils;
+import com.android.internal.util.dump.DualDumpOutputStream;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * UsbUserPermissionManager manages usb device or accessory access permissions.
+ *
+ * @hide
+ */
+class UsbUserPermissionManager {
+ private static final String TAG = UsbUserPermissionManager.class.getSimpleName();
+ private static final boolean DEBUG = false;
+
+ @GuardedBy("mLock")
+ /** Mapping of USB device name to list of UIDs with permissions for the device
+ * Each entry lasts until device is disconnected*/
+ private final ArrayMap<String, SparseBooleanArray> mDevicePermissionMap =
+ new ArrayMap<>();
+ @GuardedBy("mLock")
+ /** Temporary mapping UsbAccessory to list of UIDs with permissions for the accessory
+ * Each entry lasts until accessory is disconnected*/
+ private final ArrayMap<UsbAccessory, SparseBooleanArray> mAccessoryPermissionMap =
+ new ArrayMap<>();
+
+ @GuardedBy("mLock")
+ /** Maps USB device to list of UIDs with persistent permissions for the device*/
+ private final ArrayMap<DeviceFilter, SparseBooleanArray>
+ mDevicePersistentPermissionMap = new ArrayMap<>();
+ @GuardedBy("mLock")
+ /** Maps Usb Accessory to list of UIDs with persistent permissions for the accessory*/
+ private final ArrayMap<AccessoryFilter, SparseBooleanArray>
+ mAccessoryPersistentPermissionMap = new ArrayMap<>();
+
+ private final Context mContext;
+ private final UserHandle mUser;
+ private final UsbUserSettingsManager mUsbUserSettingsManager;
+ private final boolean mDisablePermissionDialogs;
+
+ private final @NonNull AtomicFile mPermissionsFile;
+
+ private final Object mLock = new Object();
+
+ /**
+ * If a async task to persist the mDevicePersistentPreferenceMap and
+ * mAccessoryPersistentPreferenceMap is currently scheduled.
+ */
+ @GuardedBy("mLock")
+ private boolean mIsCopyPermissionsScheduled;
+
+ UsbUserPermissionManager(@NonNull Context context,
+ @NonNull UsbUserSettingsManager usbUserSettingsManager) {
+ mContext = context;
+ mUser = context.getUser();
+ mUsbUserSettingsManager = usbUserSettingsManager;
+ mDisablePermissionDialogs = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_disableUsbPermissionDialogs);
+
+ mPermissionsFile = new AtomicFile(new File(
+ Environment.getUserSystemDirectory(mUser.getIdentifier()),
+ "usb_permissions.xml"), "usb-permissions");
+
+ synchronized (mLock) {
+ readPermissionsLocked();
+ }
+ }
+
+ /**
+ * Removes access permissions of all packages for the USB accessory.
+ *
+ * @param accessory to remove permissions for
+ */
+ void removeAccessoryPermissions(@NonNull UsbAccessory accessory) {
+ synchronized (mLock) {
+ mAccessoryPermissionMap.remove(accessory);
+ }
+ }
+
+ /**
+ * Removes access permissions of all packages for the USB device.
+ *
+ * @param device to remove permissions for
+ */
+ void removeDevicePermissions(@NonNull UsbDevice device) {
+ synchronized (mLock) {
+ mDevicePermissionMap.remove(device.getDeviceName());
+ }
+ }
+
+ /**
+ * Grants permission for USB device without showing system dialog for package with uid.
+ *
+ * @param device to grant permission for
+ * @param uid to grant permission for
+ */
+ void grantDevicePermission(@NonNull UsbDevice device, int uid) {
+ synchronized (mLock) {
+ String deviceName = device.getDeviceName();
+ SparseBooleanArray uidList = mDevicePermissionMap.get(deviceName);
+ if (uidList == null) {
+ uidList = new SparseBooleanArray(1);
+ mDevicePermissionMap.put(deviceName, uidList);
+ }
+ uidList.put(uid, true);
+ }
+ }
+
+ /**
+ * Grants permission for USB accessory without showing system dialog for package with uid.
+ *
+ * @param accessory to grant permission for
+ * @param uid to grant permission for
+ */
+ void grantAccessoryPermission(@NonNull UsbAccessory accessory, int uid) {
+ synchronized (mLock) {
+ SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
+ if (uidList == null) {
+ uidList = new SparseBooleanArray(1);
+ mAccessoryPermissionMap.put(accessory, uidList);
+ }
+ uidList.put(uid, true);
+ }
+ }
+
+ /**
+ * Returns true if package with uid has permission to access the device.
+ *
+ * @param device to check permission for
+ * @param pid to check permission for
+ * @param uid to check permission for
+ * @return {@code true} if package with uid has permission
+ */
+ boolean hasPermission(@NonNull UsbDevice device, @NonNull String packageName, int pid,
+ int uid) {
+ if (isCameraDevicePresent(device)) {
+ if (!isCameraPermissionGranted(packageName, pid, uid)) {
+ return false;
+ }
+ }
+ synchronized (mLock) {
+ if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) {
+ return true;
+ }
+ DeviceFilter filter = new DeviceFilter(device);
+ SparseBooleanArray permissionsForDevice = mDevicePersistentPermissionMap.get(filter);
+ if (permissionsForDevice != null) {
+ int idx = permissionsForDevice.indexOfKey(uid);
+ if (idx >= 0) {
+ return permissionsForDevice.valueAt(idx);
+ }
+ }
+ SparseBooleanArray uidList = mDevicePermissionMap.get(device.getDeviceName());
+ if (uidList == null) {
+ return false;
+ }
+ return uidList.get(uid);
+ }
+ }
+
+ /**
+ * Returns true if caller has permission to access the accessory.
+ *
+ * @param accessory to check permission for
+ * @param uid to check permission for
+ * @return {@code true} if caller has permssion
+ */
+ boolean hasPermission(@NonNull UsbAccessory accessory, int uid) {
+ synchronized (mLock) {
+ if (uid == Process.SYSTEM_UID || mDisablePermissionDialogs) {
+ return true;
+ }
+ AccessoryFilter filter = new AccessoryFilter(accessory);
+ SparseBooleanArray permissionsForAccessory =
+ mAccessoryPersistentPermissionMap.get(filter);
+ if (permissionsForAccessory != null) {
+ int idx = permissionsForAccessory.indexOfKey(uid);
+ if (idx >= 0) {
+ return permissionsForAccessory.valueAt(idx);
+ }
+ }
+ SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
+ if (uidList == null) {
+ return false;
+ }
+ return uidList.get(uid);
+ }
+ }
+
+ void setDevicePersistentPermission(@NonNull UsbDevice device, int uid, boolean isGranted) {
+
+ boolean isChanged;
+ DeviceFilter filter = new DeviceFilter(device);
+ synchronized (mLock) {
+ SparseBooleanArray permissionsForDevice = mDevicePersistentPermissionMap.get(filter);
+ if (permissionsForDevice == null) {
+ permissionsForDevice = new SparseBooleanArray();
+ mDevicePersistentPermissionMap.put(filter, permissionsForDevice);
+ }
+ int idx = permissionsForDevice.indexOfKey(uid);
+ if (idx >= 0) {
+ isChanged = permissionsForDevice.valueAt(idx) != isGranted;
+ permissionsForDevice.setValueAt(idx, isGranted);
+ } else {
+ isChanged = true;
+ permissionsForDevice.put(uid, isGranted);
+ }
+
+ if (isChanged) {
+ scheduleWritePermissionsLocked();
+ }
+ }
+ }
+
+ void setAccessoryPersistentPermission(@NonNull UsbAccessory accessory, int uid,
+ boolean isGranted) {
+
+ boolean isChanged;
+ AccessoryFilter filter = new AccessoryFilter(accessory);
+ synchronized (mLock) {
+ SparseBooleanArray permissionsForAccessory =
+ mAccessoryPersistentPermissionMap.get(filter);
+ if (permissionsForAccessory == null) {
+ permissionsForAccessory = new SparseBooleanArray();
+ mAccessoryPersistentPermissionMap.put(filter, permissionsForAccessory);
+ }
+ int idx = permissionsForAccessory.indexOfKey(uid);
+ if (idx >= 0) {
+ isChanged = permissionsForAccessory.valueAt(idx) != isGranted;
+ permissionsForAccessory.setValueAt(idx, isGranted);
+ } else {
+ isChanged = true;
+ permissionsForAccessory.put(uid, isGranted);
+ }
+
+ if (isChanged) {
+ scheduleWritePermissionsLocked();
+ }
+ }
+ }
+
+ private void readPermission(@NonNull XmlPullParser parser) throws XmlPullParserException,
+ IOException {
+ int uid;
+ boolean isGranted;
+
+ try {
+ uid = XmlUtils.readIntAttribute(parser, "uid");
+ } catch (NumberFormatException e) {
+ Slog.e(TAG, "error reading usb permission uid", e);
+ XmlUtils.skipCurrentTag(parser);
+ return;
+ }
+
+ // only use "true"/"false" as valid values
+ String isGrantedString = parser.getAttributeValue(null, "granted");
+ if (isGrantedString == null || !(isGrantedString.equals(Boolean.TRUE.toString())
+ || isGrantedString.equals(Boolean.FALSE.toString()))) {
+ Slog.e(TAG, "error reading usb permission granted state");
+ XmlUtils.skipCurrentTag(parser);
+ return;
+ }
+ isGranted = isGrantedString.equals(Boolean.TRUE.toString());
+ XmlUtils.nextElement(parser);
+ if ("usb-device".equals(parser.getName())) {
+ DeviceFilter filter = DeviceFilter.read(parser);
+ int idx = mDevicePersistentPermissionMap.indexOfKey(filter);
+ if (idx >= 0) {
+ SparseBooleanArray permissionsForDevice =
+ mDevicePersistentPermissionMap.valueAt(idx);
+ permissionsForDevice.put(uid, isGranted);
+ } else {
+ SparseBooleanArray permissionsForDevice = new SparseBooleanArray();
+ mDevicePersistentPermissionMap.put(filter, permissionsForDevice);
+ permissionsForDevice.put(uid, isGranted);
+ }
+ } else if ("usb-accessory".equals(parser.getName())) {
+ AccessoryFilter filter = AccessoryFilter.read(parser);
+ int idx = mAccessoryPersistentPermissionMap.indexOfKey(filter);
+ if (idx >= 0) {
+ SparseBooleanArray permissionsForAccessory =
+ mAccessoryPersistentPermissionMap.valueAt(idx);
+ permissionsForAccessory.put(uid, isGranted);
+ } else {
+ SparseBooleanArray permissionsForAccessory = new SparseBooleanArray();
+ mAccessoryPersistentPermissionMap.put(filter, permissionsForAccessory);
+ permissionsForAccessory.put(uid, isGranted);
+ }
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void readPermissionsLocked() {
+ mDevicePersistentPermissionMap.clear();
+ mAccessoryPersistentPermissionMap.clear();
+
+ try (FileInputStream in = mPermissionsFile.openRead()) {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(in, StandardCharsets.UTF_8.name());
+
+ XmlUtils.nextElement(parser);
+ while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+ String tagName = parser.getName();
+ if ("permission".equals(tagName)) {
+ readPermission(parser);
+ } else {
+ XmlUtils.nextElement(parser);
+ }
+ }
+ } catch (FileNotFoundException e) {
+ if (DEBUG) Slog.d(TAG, "usb permissions file not found");
+ } catch (Exception e) {
+ Slog.e(TAG, "error reading usb permissions file, deleting to start fresh", e);
+ mPermissionsFile.delete();
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void scheduleWritePermissionsLocked() {
+ if (mIsCopyPermissionsScheduled) {
+ return;
+ }
+ mIsCopyPermissionsScheduled = true;
+
+ AsyncTask.execute(() -> {
+ int numDevices;
+ DeviceFilter[] devices;
+ int[][] uidsForDevices;
+ boolean[][] grantedValuesForDevices;
+
+ int numAccessories;
+ AccessoryFilter[] accessories;
+ int[][] uidsForAccessories;
+ boolean[][] grantedValuesForAccessories;
+
+ synchronized (mLock) {
+ // Copy the permission state so we can write outside of lock
+ numDevices = mDevicePersistentPermissionMap.size();
+ devices = new DeviceFilter[numDevices];
+ uidsForDevices = new int[numDevices][];
+ grantedValuesForDevices = new boolean[numDevices][];
+ for (int deviceIdx = 0; deviceIdx < numDevices; deviceIdx++) {
+ devices[deviceIdx] =
+ new DeviceFilter(mDevicePersistentPermissionMap.keyAt(deviceIdx));
+ SparseBooleanArray permissions =
+ mDevicePersistentPermissionMap.valueAt(deviceIdx);
+ int numPermissions = permissions.size();
+ uidsForDevices[deviceIdx] = new int[numPermissions];
+ grantedValuesForDevices[deviceIdx] = new boolean[numPermissions];
+ for (int permissionIdx = 0; permissionIdx < numPermissions; permissionIdx++) {
+ uidsForDevices[deviceIdx][permissionIdx] = permissions.keyAt(permissionIdx);
+ grantedValuesForDevices[deviceIdx][permissionIdx] =
+ permissions.valueAt(permissionIdx);
+ }
+ }
+
+ numAccessories = mAccessoryPersistentPermissionMap.size();
+ accessories = new AccessoryFilter[numAccessories];
+ uidsForAccessories = new int[numAccessories][];
+ grantedValuesForAccessories = new boolean[numAccessories][];
+ for (int accessoryIdx = 0; accessoryIdx < numAccessories; accessoryIdx++) {
+ accessories[accessoryIdx] = new AccessoryFilter(
+ mAccessoryPersistentPermissionMap.keyAt(accessoryIdx));
+ SparseBooleanArray permissions =
+ mAccessoryPersistentPermissionMap.valueAt(accessoryIdx);
+ int numPermissions = permissions.size();
+ uidsForAccessories[accessoryIdx] = new int[numPermissions];
+ grantedValuesForAccessories[accessoryIdx] = new boolean[numPermissions];
+ for (int permissionIdx = 0; permissionIdx < numPermissions; permissionIdx++) {
+ uidsForAccessories[accessoryIdx][permissionIdx] =
+ permissions.keyAt(permissionIdx);
+ grantedValuesForAccessories[accessoryIdx][permissionIdx] =
+ permissions.valueAt(permissionIdx);
+ }
+ }
+ mIsCopyPermissionsScheduled = false;
+ }
+
+ synchronized (mPermissionsFile) {
+ FileOutputStream out = null;
+ try {
+ out = mPermissionsFile.startWrite();
+ FastXmlSerializer serializer = new FastXmlSerializer();
+ serializer.setOutput(out, StandardCharsets.UTF_8.name());
+ serializer.startDocument(null, true);
+ serializer.startTag(null, "permissions");
+
+ for (int i = 0; i < numDevices; i++) {
+ int numPermissions = uidsForDevices[i].length;
+ for (int j = 0; j < numPermissions; j++) {
+ serializer.startTag(null, "permission");
+ serializer.attribute(null, "uid",
+ Integer.toString(uidsForDevices[i][j]));
+ serializer.attribute(null, "granted",
+ Boolean.toString(grantedValuesForDevices[i][j]));
+ devices[i].write(serializer);
+ serializer.endTag(null, "permission");
+ }
+ }
+
+ for (int i = 0; i < numAccessories; i++) {
+ int numPermissions = uidsForAccessories[i].length;
+ for (int j = 0; j < numPermissions; j++) {
+ serializer.startTag(null, "permission");
+ serializer.attribute(null, "uid",
+ Integer.toString(uidsForAccessories[i][j]));
+ serializer.attribute(null, "granted",
+ Boolean.toString(grantedValuesForDevices[i][j]));
+ accessories[i].write(serializer);
+ serializer.endTag(null, "permission");
+ }
+ }
+
+ serializer.endTag(null, "permissions");
+ serializer.endDocument();
+
+ mPermissionsFile.finishWrite(out);
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to write permissions", e);
+ if (out != null) {
+ mPermissionsFile.failWrite(out);
+ }
+ }
+ }
+ });
+ }
+
+ /**
+ * Creates UI dialog to request permission for the given package to access the device
+ * or accessory.
+ *
+ * @param device The USB device attached
+ * @param accessory The USB accessory attached
+ * @param canBeDefault Whether the calling pacakge can set as default handler
+ * of the USB device or accessory
+ * @param packageName The package name of the calling package
+ * @param uid The uid of the calling package
+ * @param userContext The context to start the UI dialog
+ * @param pi PendingIntent for returning result
+ */
+ void requestPermissionDialog(@Nullable UsbDevice device,
+ @Nullable UsbAccessory accessory,
+ boolean canBeDefault,
+ @NonNull String packageName,
+ int uid,
+ @NonNull Context userContext,
+ @NonNull PendingIntent pi) {
+ long identity = Binder.clearCallingIdentity();
+ Intent intent = new Intent();
+ if (device != null) {
+ intent.putExtra(UsbManager.EXTRA_DEVICE, device);
+ } else {
+ intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
+ }
+ intent.putExtra(Intent.EXTRA_INTENT, pi);
+ intent.putExtra(Intent.EXTRA_UID, uid);
+ intent.putExtra(UsbManager.EXTRA_CAN_BE_DEFAULT, canBeDefault);
+ intent.putExtra(UsbManager.EXTRA_PACKAGE, packageName);
+ intent.setComponent(ComponentName.unflattenFromString(userContext.getResources().getString(
+ com.android.internal.R.string.config_usbPermissionActivity)));
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ try {
+ userContext.startActivityAsUser(intent, mUser);
+ } catch (ActivityNotFoundException e) {
+ Slog.e(TAG, "unable to start UsbPermissionActivity");
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ void dump(@NonNull DualDumpOutputStream dump, String idName, long id) {
+ long token = dump.start(idName, id);
+ synchronized (mLock) {
+ dump.write("user_id", UsbUserPermissionsManagerProto.USER_ID, mUser.getIdentifier());
+ int numMappings = mDevicePermissionMap.size();
+ for (int mappingsIdx = 0; mappingsIdx < numMappings; mappingsIdx++) {
+ String deviceName = mDevicePermissionMap.keyAt(mappingsIdx);
+ long devicePermissionToken = dump.start("device_permissions",
+ UsbUserPermissionsManagerProto.DEVICE_PERMISSIONS);
+
+ dump.write("device_name", UsbDevicePermissionProto.DEVICE_NAME, deviceName);
+
+ SparseBooleanArray uidList = mDevicePermissionMap.valueAt(mappingsIdx);
+ int numUids = uidList.size();
+ for (int uidsIdx = 0; uidsIdx < numUids; uidsIdx++) {
+ dump.write("uids", UsbDevicePermissionProto.UIDS, uidList.keyAt(uidsIdx));
+ }
+
+ dump.end(devicePermissionToken);
+ }
+
+ numMappings = mAccessoryPermissionMap.size();
+ for (int mappingsIdx = 0; mappingsIdx < numMappings; ++mappingsIdx) {
+ UsbAccessory accessory = mAccessoryPermissionMap.keyAt(mappingsIdx);
+ long accessoryPermissionToken = dump.start("accessory_permissions",
+ UsbUserPermissionsManagerProto.ACCESSORY_PERMISSIONS);
+
+ dump.write("accessory_description",
+ UsbAccessoryPermissionProto.ACCESSORY_DESCRIPTION,
+ accessory.getDescription());
+
+ SparseBooleanArray uidList = mAccessoryPermissionMap.valueAt(mappingsIdx);
+ int numUids = uidList.size();
+ for (int uidsIdx = 0; uidsIdx < numUids; uidsIdx++) {
+ dump.write("uids", UsbAccessoryPermissionProto.UIDS, uidList.keyAt(uidsIdx));
+ }
+
+ dump.end(accessoryPermissionToken);
+ }
+
+ numMappings = mDevicePersistentPermissionMap.size();
+ for (int mappingsIdx = 0; mappingsIdx < numMappings; mappingsIdx++) {
+ DeviceFilter filter = mDevicePersistentPermissionMap.keyAt(mappingsIdx);
+ long devicePermissionToken = dump.start("device_persistent_permissions",
+ UsbUserPermissionsManagerProto.DEVICE_PERSISTENT_PERMISSIONS);
+ filter.dump(dump, "device",
+ UsbDevicePersistentPermissionProto.DEVICE_FILTER);
+ SparseBooleanArray permissions =
+ mDevicePersistentPermissionMap.valueAt(mappingsIdx);
+ int numPermissions = permissions.size();
+ for (int permissionsIdx = 0; permissionsIdx < numPermissions; permissionsIdx++) {
+ long uidPermissionToken = dump.start("uid_permission",
+ UsbDevicePersistentPermissionProto.PERMISSION_VALUES);
+ dump.write("uid", UsbUidPermissionProto.UID, permissions.keyAt(permissionsIdx));
+ dump.write("is_granted",
+ UsbUidPermissionProto.IS_GRANTED, permissions.valueAt(permissionsIdx));
+ dump.end(uidPermissionToken);
+ }
+ dump.end(devicePermissionToken);
+ }
+
+ numMappings = mAccessoryPersistentPermissionMap.size();
+ for (int mappingsIdx = 0; mappingsIdx < numMappings; mappingsIdx++) {
+ AccessoryFilter filter = mAccessoryPersistentPermissionMap.keyAt(mappingsIdx);
+ long accessoryPermissionToken = dump.start("accessory_persistent_permissions",
+ UsbUserPermissionsManagerProto.ACCESSORY_PERSISTENT_PERMISSIONS);
+ filter.dump(dump, "accessory",
+ UsbAccessoryPersistentPermissionProto.ACCESSORY_FILTER);
+ SparseBooleanArray permissions =
+ mAccessoryPersistentPermissionMap.valueAt(mappingsIdx);
+ int numPermissions = permissions.size();
+ for (int permissionsIdx = 0; permissionsIdx < numPermissions; permissionsIdx++) {
+ long uidPermissionToken = dump.start("uid_permission",
+ UsbAccessoryPersistentPermissionProto.PERMISSION_VALUES);
+ dump.write("uid", UsbUidPermissionProto.UID, permissions.keyAt(permissionsIdx));
+ dump.write("is_granted",
+ UsbUidPermissionProto.IS_GRANTED, permissions.valueAt(permissionsIdx));
+ dump.end(uidPermissionToken);
+ }
+ dump.end(accessoryPermissionToken);
+ }
+ }
+ dump.end(token);
+ }
+
+ /**
+ * Check for camera permission of the calling process.
+ *
+ * @param packageName Package name of the caller.
+ * @param pid Linux pid of the calling process.
+ * @param uid Linux uid of the calling process.
+ * @return True in case camera permission is available, False otherwise.
+ */
+ private boolean isCameraPermissionGranted(String packageName, int pid, int uid) {
+ int targetSdkVersion = android.os.Build.VERSION_CODES.P;
+ try {
+ ApplicationInfo aInfo = mContext.getPackageManager().getApplicationInfo(packageName, 0);
+ // compare uid with packageName to foil apps pretending to be someone else
+ if (aInfo.uid != uid) {
+ Slog.i(TAG, "Package " + packageName + " does not match caller's uid " + uid);
+ return false;
+ }
+ targetSdkVersion = aInfo.targetSdkVersion;
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.i(TAG, "Package not found, likely due to invalid package name!");
+ return false;
+ }
+
+ if (targetSdkVersion >= android.os.Build.VERSION_CODES.P) {
+ int allowed = mContext.checkPermission(android.Manifest.permission.CAMERA, pid, uid);
+ if (android.content.pm.PackageManager.PERMISSION_DENIED == allowed) {
+ Slog.i(TAG, "Camera permission required for USB video class devices");
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public void checkPermission(UsbDevice device, String packageName, int pid, int uid) {
+ if (!hasPermission(device, packageName, pid, uid)) {
+ throw new SecurityException("User has not given " + uid + "/" + packageName
+ + " permission to access device " + device.getDeviceName());
+ }
+ }
+
+ public void checkPermission(UsbAccessory accessory, int uid) {
+ if (!hasPermission(accessory, uid)) {
+ throw new SecurityException("User has not given " + uid + " permission to accessory "
+ + accessory);
+ }
+ }
+
+ private void requestPermissionDialog(@Nullable UsbDevice device,
+ @Nullable UsbAccessory accessory,
+ boolean canBeDefault,
+ String packageName,
+ PendingIntent pi,
+ int uid) {
+ // compare uid with packageName to foil apps pretending to be someone else
+ try {
+ ApplicationInfo aInfo = mContext.getPackageManager().getApplicationInfo(packageName, 0);
+ if (aInfo.uid != uid) {
+ throw new IllegalArgumentException("package " + packageName
+ + " does not match caller's uid " + uid);
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new IllegalArgumentException("package " + packageName + " not found");
+ }
+
+ requestPermissionDialog(device, accessory, canBeDefault, packageName, uid, mContext, pi);
+ }
+
+ public void requestPermission(UsbDevice device, String packageName, PendingIntent pi, int pid,
+ int uid) {
+ Intent intent = new Intent();
+
+ // respond immediately if permission has already been granted
+ if (hasPermission(device, packageName, pid, uid)) {
+ intent.putExtra(UsbManager.EXTRA_DEVICE, device);
+ intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true);
+ try {
+ pi.send(mContext, 0, intent);
+ } catch (PendingIntent.CanceledException e) {
+ if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled");
+ }
+ return;
+ }
+ if (isCameraDevicePresent(device)) {
+ if (!isCameraPermissionGranted(packageName, pid, uid)) {
+ intent.putExtra(UsbManager.EXTRA_DEVICE, device);
+ intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false);
+ try {
+ pi.send(mContext, 0, intent);
+ } catch (PendingIntent.CanceledException e) {
+ if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled");
+ }
+ return;
+ }
+ }
+
+ requestPermissionDialog(device, null,
+ mUsbUserSettingsManager.canBeDefault(device, packageName), packageName, pi, uid);
+ }
+
+ public void requestPermission(UsbAccessory accessory, String packageName, PendingIntent pi,
+ int uid) {
+ // respond immediately if permission has already been granted
+ if (hasPermission(accessory, uid)) {
+ Intent intent = new Intent();
+ intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
+ intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true);
+ try {
+ pi.send(mContext, 0, intent);
+ } catch (PendingIntent.CanceledException e) {
+ if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled");
+ }
+ return;
+ }
+
+ requestPermissionDialog(null, accessory,
+ mUsbUserSettingsManager.canBeDefault(accessory, packageName), packageName, pi, uid);
+ }
+
+ /**
+ * Check whether a particular device or any of its interfaces
+ * is of class VIDEO.
+ *
+ * @param device The device that needs to get scanned
+ * @return True in case a VIDEO device or interface is present,
+ * False otherwise.
+ */
+ private boolean isCameraDevicePresent(UsbDevice device) {
+ if (device.getDeviceClass() == UsbConstants.USB_CLASS_VIDEO) {
+ return true;
+ }
+
+ for (int i = 0; i < device.getInterfaceCount(); i++) {
+ UsbInterface iface = device.getInterface(i);
+ if (iface.getInterfaceClass() == UsbConstants.USB_CLASS_VIDEO) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java b/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java
index e1bfb8a7c6d0..c2b8d0109e68 100644
--- a/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java
+++ b/services/usb/java/com/android/server/usb/UsbUserSettingsManager.java
@@ -21,13 +21,10 @@ import static com.android.server.usb.UsbProfileGroupSettingsManager.getAccessory
import static com.android.server.usb.UsbProfileGroupSettingsManager.getDeviceFilters;
import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
@@ -36,9 +33,7 @@ import android.content.res.XmlResourceParser;
import android.hardware.usb.AccessoryFilter;
import android.hardware.usb.DeviceFilter;
import android.hardware.usb.UsbAccessory;
-import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
-import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbManager;
import android.os.UserHandle;
import android.service.usb.UsbAccessoryAttachedActivities;
@@ -62,12 +57,10 @@ class UsbUserSettingsManager {
private final Context mUserContext;
private final PackageManager mPackageManager;
- private final UsbPermissionManager mUsbPermissionManager;
private final Object mLock = new Object();
- UsbUserSettingsManager(Context context, UserHandle user,
- @NonNull UsbPermissionManager usbPermissionManager) {
+ UsbUserSettingsManager(Context context, UserHandle user) {
if (DEBUG) Slog.v(TAG, "Creating settings for " + user);
try {
@@ -79,192 +72,6 @@ class UsbUserSettingsManager {
mPackageManager = mUserContext.getPackageManager();
mUser = user;
- mUsbPermissionManager = usbPermissionManager;
- }
-
- /**
- * Remove all access permission for a device.
- *
- * @param device The device the permissions are for
- */
- void removeDevicePermissions(@NonNull UsbDevice device) {
- mUsbPermissionManager.removeDevicePermissions(device);
- }
-
- /**
- * Remove all access permission for a accessory.
- *
- * @param accessory The accessory the permissions are for
- */
- void removeAccessoryPermissions(@NonNull UsbAccessory accessory) {
- mUsbPermissionManager.removeAccessoryPermissions(accessory);
- }
-
- /**
- * Check whether a particular device or any of its interfaces
- * is of class VIDEO.
- *
- * @param device The device that needs to get scanned
- * @return True in case a VIDEO device or interface is present,
- * False otherwise.
- */
- private boolean isCameraDevicePresent(UsbDevice device) {
- if (device.getDeviceClass() == UsbConstants.USB_CLASS_VIDEO) {
- return true;
- }
-
- for (int i = 0; i < device.getInterfaceCount(); i++) {
- UsbInterface iface = device.getInterface(i);
- if (iface.getInterfaceClass() == UsbConstants.USB_CLASS_VIDEO) {
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * Check for camera permission of the calling process.
- *
- * @param packageName Package name of the caller.
- * @param pid Linux pid of the calling process.
- * @param uid Linux uid of the calling process.
- *
- * @return True in case camera permission is available, False otherwise.
- */
- private boolean isCameraPermissionGranted(String packageName, int pid, int uid) {
- int targetSdkVersion = android.os.Build.VERSION_CODES.P;
- try {
- ApplicationInfo aInfo = mPackageManager.getApplicationInfo(packageName, 0);
- // compare uid with packageName to foil apps pretending to be someone else
- if (aInfo.uid != uid) {
- Slog.i(TAG, "Package " + packageName + " does not match caller's uid " + uid);
- return false;
- }
- targetSdkVersion = aInfo.targetSdkVersion;
- } catch (PackageManager.NameNotFoundException e) {
- Slog.i(TAG, "Package not found, likely due to invalid package name!");
- return false;
- }
-
- if (targetSdkVersion >= android.os.Build.VERSION_CODES.P) {
- int allowed = mUserContext.checkPermission(android.Manifest.permission.CAMERA, pid,
- uid);
- if (android.content.pm.PackageManager.PERMISSION_DENIED == allowed) {
- Slog.i(TAG, "Camera permission required for USB video class devices");
- return false;
- }
- }
-
- return true;
- }
-
- public boolean hasPermission(UsbDevice device, String packageName, int pid, int uid) {
- if (isCameraDevicePresent(device)) {
- if (!isCameraPermissionGranted(packageName, pid, uid)) {
- return false;
- }
- }
-
- return mUsbPermissionManager.hasPermission(device, uid);
- }
-
- public boolean hasPermission(UsbAccessory accessory, int uid) {
- return mUsbPermissionManager.hasPermission(accessory, uid);
- }
-
- public void checkPermission(UsbDevice device, String packageName, int pid, int uid) {
- if (!hasPermission(device, packageName, pid, uid)) {
- throw new SecurityException("User has not given " + uid + "/" + packageName
- + " permission to access device " + device.getDeviceName());
- }
- }
-
- public void checkPermission(UsbAccessory accessory, int uid) {
- if (!hasPermission(accessory, uid)) {
- throw new SecurityException("User has not given " + uid + " permission to accessory "
- + accessory);
- }
- }
-
- private void requestPermissionDialog(@Nullable UsbDevice device,
- @Nullable UsbAccessory accessory,
- boolean canBeDefault,
- String packageName,
- PendingIntent pi,
- int uid) {
- // compare uid with packageName to foil apps pretending to be someone else
- try {
- ApplicationInfo aInfo = mPackageManager.getApplicationInfo(packageName, 0);
- if (aInfo.uid != uid) {
- throw new IllegalArgumentException("package " + packageName +
- " does not match caller's uid " + uid);
- }
- } catch (PackageManager.NameNotFoundException e) {
- throw new IllegalArgumentException("package " + packageName + " not found");
- }
-
- mUsbPermissionManager.requestPermissionDialog(device,
- accessory, canBeDefault, packageName, uid, mUserContext, pi);
- }
-
- public void requestPermission(UsbDevice device, String packageName, PendingIntent pi, int pid,
- int uid) {
- Intent intent = new Intent();
-
- // respond immediately if permission has already been granted
- if (hasPermission(device, packageName, pid, uid)) {
- intent.putExtra(UsbManager.EXTRA_DEVICE, device);
- intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true);
- try {
- pi.send(mUserContext, 0, intent);
- } catch (PendingIntent.CanceledException e) {
- if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled");
- }
- return;
- }
- if (isCameraDevicePresent(device)) {
- if (!isCameraPermissionGranted(packageName, pid, uid)) {
- intent.putExtra(UsbManager.EXTRA_DEVICE, device);
- intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false);
- try {
- pi.send(mUserContext, 0, intent);
- } catch (PendingIntent.CanceledException e) {
- if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled");
- }
- return;
- }
- }
-
- requestPermissionDialog(device, null, canBeDefault(device, packageName), packageName, pi,
- uid);
- }
-
- public void requestPermission(UsbAccessory accessory, String packageName, PendingIntent pi,
- int uid) {
- // respond immediately if permission has already been granted
- if (hasPermission(accessory, uid)) {
- Intent intent = new Intent();
- intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
- intent.putExtra(UsbManager.EXTRA_PERMISSION_GRANTED, true);
- try {
- pi.send(mUserContext, 0, intent);
- } catch (PendingIntent.CanceledException e) {
- if (DEBUG) Slog.d(TAG, "requestPermission PendingIntent was cancelled");
- }
- return;
- }
-
- requestPermissionDialog(null, accessory, canBeDefault(accessory, packageName), packageName,
- pi, uid);
- }
-
- public void grantDevicePermission(UsbDevice device, int uid) {
- mUsbPermissionManager.grantDevicePermission(device, uid);
- }
-
- public void grantAccessoryPermission(UsbAccessory accessory, int uid) {
- mUsbPermissionManager.grantAccessoryPermission(accessory, uid);
}
/**
@@ -288,7 +95,7 @@ class UsbUserSettingsManager {
*
* @return {@code true} if the app can be default
*/
- private boolean canBeDefault(@NonNull UsbDevice device, String packageName) {
+ boolean canBeDefault(@NonNull UsbDevice device, String packageName) {
ActivityInfo[] activities = getPackageActivities(packageName);
if (activities != null) {
int numActivities = activities.length;
@@ -330,7 +137,7 @@ class UsbUserSettingsManager {
*
* @return {@code true} if the app can be default
*/
- private boolean canBeDefault(@NonNull UsbAccessory accessory, String packageName) {
+ boolean canBeDefault(@NonNull UsbAccessory accessory, String packageName) {
ActivityInfo[] activities = getPackageActivities(packageName);
if (activities != null) {
int numActivities = activities.length;
@@ -380,8 +187,6 @@ class UsbUserSettingsManager {
synchronized (mLock) {
dump.write("user_id", UsbUserSettingsManagerProto.USER_ID, mUser.getIdentifier());
- mUsbPermissionManager.dump(dump);
-
List<ResolveInfo> deviceAttachedActivities = queryIntentActivities(
new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED));
int numDeviceAttachedActivities = deviceAttachedActivities.size();
diff --git a/services/usb/java/com/android/server/usb/descriptors/ByteStream.java b/services/usb/java/com/android/server/usb/descriptors/ByteStream.java
index 1e823b63d5b2..56dc3e05a240 100644
--- a/services/usb/java/com/android/server/usb/descriptors/ByteStream.java
+++ b/services/usb/java/com/android/server/usb/descriptors/ByteStream.java
@@ -185,12 +185,14 @@ public final class ByteStream {
// Positive offsets only
throw new IllegalArgumentException();
}
- // do arithmetic and comparison in long to ovoid potention integer overflow
+ // do arithmetic and comparison in long to avoid potential integer overflow
long longNewIndex = (long) mIndex + (long) numBytes;
- if (longNewIndex < (long) mBytes.length) {
+ if (longNewIndex <= (long) mBytes.length) {
mReadCount += numBytes;
mIndex += numBytes;
} else {
+ // Position the stream to the end so available() will return 0
+ mIndex = mBytes.length;
throw new IndexOutOfBoundsException();
}
}
@@ -210,6 +212,7 @@ public final class ByteStream {
mReadCount -= numBytes;
mIndex -= numBytes;
} else {
+ mIndex = 0;
throw new IndexOutOfBoundsException();
}
}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACEndpoint.java b/services/usb/java/com/android/server/usb/descriptors/UsbACEndpoint.java
index 7ebccf39868c..ff7f3934086c 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbACEndpoint.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACEndpoint.java
@@ -45,7 +45,6 @@ abstract class UsbACEndpoint extends UsbDescriptor {
@Override
public int parseRawDescriptors(ByteStream stream) {
mSubtype = stream.getByte();
-
return mLength;
}
@@ -53,14 +52,24 @@ abstract class UsbACEndpoint extends UsbDescriptor {
int length, byte type) {
UsbInterfaceDescriptor interfaceDesc = parser.getCurInterface();
int subClass = interfaceDesc.getUsbSubclass();
+ // TODO shouldn't this switch on subtype?
switch (subClass) {
case AUDIO_AUDIOCONTROL:
+ if (UsbDescriptorParser.DEBUG) {
+ Log.d(TAG, "---> AUDIO_AUDIOCONTROL");
+ }
return new UsbACAudioControlEndpoint(length, type, subClass);
case AUDIO_AUDIOSTREAMING:
+ if (UsbDescriptorParser.DEBUG) {
+ Log.d(TAG, "---> AUDIO_AUDIOSTREAMING");
+ }
return new UsbACAudioStreamEndpoint(length, type, subClass);
case AUDIO_MIDISTREAMING:
+ if (UsbDescriptorParser.DEBUG) {
+ Log.d(TAG, "---> AUDIO_MIDISTREAMING");
+ }
return new UsbACMidiEndpoint(length, type, subClass);
default:
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACInterface.java b/services/usb/java/com/android/server/usb/descriptors/UsbACInterface.java
index 38c12a1f6c16..82fbfb89c498 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbACInterface.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACInterface.java
@@ -100,8 +100,14 @@ public abstract class UsbACInterface extends UsbDescriptor {
switch (subtype) {
case ACI_HEADER:
{
+ if (UsbDescriptorParser.DEBUG) {
+ Log.d(TAG, " ---> ACI_HEADER");
+ }
int acInterfaceSpec = stream.unpackUsbShort();
parser.setACInterfaceSpec(acInterfaceSpec);
+ if (UsbDescriptorParser.DEBUG) {
+ Log.d(TAG, " acInterfaceSpec:0x" + Integer.toHexString(acInterfaceSpec));
+ }
if (acInterfaceSpec == UsbDeviceDescriptor.USBSPEC_2_0) {
return new Usb20ACHeader(length, type, subtype, subClass, acInterfaceSpec);
} else {
@@ -111,7 +117,13 @@ public abstract class UsbACInterface extends UsbDescriptor {
case ACI_INPUT_TERMINAL:
{
+ if (UsbDescriptorParser.DEBUG) {
+ Log.d(TAG, " ---> ACI_INPUT_TERMINAL");
+ }
int acInterfaceSpec = parser.getACInterfaceSpec();
+ if (UsbDescriptorParser.DEBUG) {
+ Log.d(TAG, " acInterfaceSpec:0x" + Integer.toHexString(acInterfaceSpec));
+ }
if (acInterfaceSpec == UsbDeviceDescriptor.USBSPEC_2_0) {
return new Usb20ACInputTerminal(length, type, subtype, subClass);
} else {
@@ -121,7 +133,13 @@ public abstract class UsbACInterface extends UsbDescriptor {
case ACI_OUTPUT_TERMINAL:
{
+ if (UsbDescriptorParser.DEBUG) {
+ Log.d(TAG, " ---> ACI_OUTPUT_TERMINAL");
+ }
int acInterfaceSpec = parser.getACInterfaceSpec();
+ if (UsbDescriptorParser.DEBUG) {
+ Log.d(TAG, " acInterfaceSpec:0x" + Integer.toHexString(acInterfaceSpec));
+ }
if (acInterfaceSpec == UsbDeviceDescriptor.USBSPEC_2_0) {
return new Usb20ACOutputTerminal(length, type, subtype, subClass);
} else {
@@ -130,14 +148,26 @@ public abstract class UsbACInterface extends UsbDescriptor {
}
case ACI_SELECTOR_UNIT:
+ if (UsbDescriptorParser.DEBUG) {
+ Log.d(TAG, " ---> ACI_SELECTOR_UNIT");
+ }
return new UsbACSelectorUnit(length, type, subtype, subClass);
case ACI_FEATURE_UNIT:
+ if (UsbDescriptorParser.DEBUG) {
+ Log.d(TAG, " ---> ACI_FEATURE_UNIT");
+ }
return new UsbACFeatureUnit(length, type, subtype, subClass);
case ACI_MIXER_UNIT:
{
+ if (UsbDescriptorParser.DEBUG) {
+ Log.d(TAG, " ---> ACI_MIXER_UNIT");
+ }
int acInterfaceSpec = parser.getACInterfaceSpec();
+ if (UsbDescriptorParser.DEBUG) {
+ Log.d(TAG, " acInterfaceSpec:0x" + Integer.toHexString(acInterfaceSpec));
+ }
if (acInterfaceSpec == UsbDeviceDescriptor.USBSPEC_2_0) {
return new Usb20ACMixerUnit(length, type, subtype, subClass);
} else {
@@ -215,14 +245,23 @@ public abstract class UsbACInterface extends UsbDescriptor {
int subClass = interfaceDesc.getUsbSubclass();
switch (subClass) {
case AUDIO_AUDIOCONTROL:
+ if (UsbDescriptorParser.DEBUG) {
+ Log.d(TAG, " AUDIO_AUDIOCONTROL");
+ }
return allocAudioControlDescriptor(
parser, stream, length, type, subtype, subClass);
case AUDIO_AUDIOSTREAMING:
+ if (UsbDescriptorParser.DEBUG) {
+ Log.d(TAG, " AUDIO_AUDIOSTREAMING");
+ }
return allocAudioStreamingDescriptor(
parser, stream, length, type, subtype, subClass);
case AUDIO_MIDISTREAMING:
+ if (UsbDescriptorParser.DEBUG) {
+ Log.d(TAG, " AUDIO_MIDISTREAMING");
+ }
return allocMidiStreamingDescriptor(length, type, subtype, subClass);
default:
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java
index 639aa4e03849..3f2d8c8ef47c 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java
@@ -30,7 +30,6 @@ import java.util.ArrayList;
*/
public final class UsbConfigDescriptor extends UsbDescriptor {
private static final String TAG = "UsbConfigDescriptor";
- private static final boolean DEBUG = false;
private int mTotalLength; // 2:2 Total length in bytes of data returned
private byte mNumInterfaces; // 4:1 Number of Interfaces
@@ -42,6 +41,8 @@ public final class UsbConfigDescriptor extends UsbDescriptor {
// D4..0 Reserved, set to 0.
private int mMaxPower; // 8:1 Maximum Power Consumption in 2mA units
+ private boolean mBlockAudio; // leave it off for now. We be replace with a "Developer Option"
+
private ArrayList<UsbInterfaceDescriptor> mInterfaceDescriptors =
new ArrayList<UsbInterfaceDescriptor>();
@@ -78,21 +79,35 @@ public final class UsbConfigDescriptor extends UsbDescriptor {
mInterfaceDescriptors.add(interfaceDesc);
}
+ private boolean isAudioInterface(UsbInterfaceDescriptor descriptor) {
+ return descriptor.getUsbClass() == UsbDescriptor.CLASSID_AUDIO
+ && descriptor.getUsbSubclass() == UsbDescriptor.AUDIO_AUDIOSTREAMING;
+ }
+
UsbConfiguration toAndroid(UsbDescriptorParser parser) {
- if (DEBUG) {
+ if (UsbDescriptorParser.DEBUG) {
Log.d(TAG, " toAndroid()");
}
+
+ // NOTE - This code running in the server process.
+ //TODO (pmclean@) - remove this
+// int pid = android.os.Process.myPid();
+// int uid = android.os.Process.myUid();
+// Log.d(TAG, " ---- pid:" + pid + " uid:" + uid);
+
String name = parser.getDescriptorString(mConfigIndex);
UsbConfiguration config = new
UsbConfiguration(mConfigValue, name, mAttribs, mMaxPower);
- UsbInterface[] interfaces = new UsbInterface[mInterfaceDescriptors.size()];
- if (DEBUG) {
- Log.d(TAG, " " + mInterfaceDescriptors.size() + " interfaces.");
- }
- for (int index = 0; index < mInterfaceDescriptors.size(); index++) {
- interfaces[index] = mInterfaceDescriptors.get(index).toAndroid(parser);
+
+ ArrayList<UsbInterface> filteredInterfaces = new ArrayList<UsbInterface>();
+ for (UsbInterfaceDescriptor descriptor : mInterfaceDescriptors) {
+ if (!mBlockAudio || !isAudioInterface(descriptor)) {
+ filteredInterfaces.add(descriptor.toAndroid(parser));
+ }
}
- config.setInterfaces(interfaces);
+ UsbInterface[] interfaceArray = new UsbInterface[0];
+ interfaceArray = filteredInterfaces.toArray(interfaceArray);
+ config.setInterfaces(interfaceArray);
return config;
}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptor.java
index ff67667e848d..44422a2d4603 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptor.java
@@ -43,7 +43,7 @@ public abstract class UsbDescriptor implements Reporting {
protected int mHierarchyLevel;
protected final int mLength; // 0:1 bLength Number Size of the Descriptor in Bytes (18 bytes)
- // we store this as an int because Java bytes are SIGNED.
+ // we store this as an int because Java bytes are SIGNED.
protected final byte mType; // 1:1 bDescriptorType Constant Device Descriptor (0x01)
private byte[] mRawData;
@@ -52,11 +52,11 @@ public abstract class UsbDescriptor implements Reporting {
private static byte[] sStringBuffer = new byte[SIZE_STRINGBUFFER];
// Status
- public static final int STATUS_UNPARSED = 0;
- public static final int STATUS_PARSED_OK = 1;
- public static final int STATUS_PARSED_UNDERRUN = 2;
- public static final int STATUS_PARSED_OVERRUN = 3;
- public static final int STATUS_PARSE_EXCEPTION = 4;
+ public static final int STATUS_UNPARSED = 0;
+ public static final int STATUS_PARSED_OK = 1;
+ public static final int STATUS_PARSED_UNDERRUN = 2;
+ public static final int STATUS_PARSED_OVERRUN = 3;
+ public static final int STATUS_PARSE_EXCEPTION = 4;
private int mStatus = STATUS_UNPARSED;
@@ -78,53 +78,53 @@ public abstract class UsbDescriptor implements Reporting {
public static final byte DESCRIPTORTYPE_HID = 0x21; // 33
public static final byte DESCRIPTORTYPE_REPORT = 0x22; // 34
public static final byte DESCRIPTORTYPE_PHYSICAL = 0x23; // 35
- public static final byte DESCRIPTORTYPE_AUDIO_INTERFACE = 0x24; // 36
- public static final byte DESCRIPTORTYPE_AUDIO_ENDPOINT = 0x25; // 37
+ public static final byte DESCRIPTORTYPE_CLASSSPECIFIC_INTERFACE = 0x24; // 36
+ public static final byte DESCRIPTORTYPE_CLASSSPECIFIC_ENDPOINT = 0x25; // 37
public static final byte DESCRIPTORTYPE_HUB = 0x29; // 41
public static final byte DESCRIPTORTYPE_SUPERSPEED_HUB = 0x2A; // 42
public static final byte DESCRIPTORTYPE_ENDPOINT_COMPANION = 0x30; // 48
// Class IDs
- public static final int CLASSID_DEVICE = 0x00;
- public static final int CLASSID_AUDIO = 0x01;
- public static final int CLASSID_COM = 0x02;
- public static final int CLASSID_HID = 0x03;
+ public static final int CLASSID_DEVICE = 0x00;
+ public static final int CLASSID_AUDIO = 0x01;
+ public static final int CLASSID_COM = 0x02;
+ public static final int CLASSID_HID = 0x03;
// public static final int CLASSID_??? = 0x04;
- public static final int CLASSID_PHYSICAL = 0x05;
- public static final int CLASSID_IMAGE = 0x06;
- public static final int CLASSID_PRINTER = 0x07;
- public static final int CLASSID_STORAGE = 0x08;
- public static final int CLASSID_HUB = 0x09;
- public static final int CLASSID_CDC_CONTROL = 0x0A;
- public static final int CLASSID_SMART_CARD = 0x0B;
+ public static final int CLASSID_PHYSICAL = 0x05;
+ public static final int CLASSID_IMAGE = 0x06;
+ public static final int CLASSID_PRINTER = 0x07;
+ public static final int CLASSID_STORAGE = 0x08;
+ public static final int CLASSID_HUB = 0x09;
+ public static final int CLASSID_CDC_CONTROL = 0x0A;
+ public static final int CLASSID_SMART_CARD = 0x0B;
//public static final int CLASSID_??? = 0x0C;
- public static final int CLASSID_SECURITY = 0x0D;
- public static final int CLASSID_VIDEO = 0x0E;
- public static final int CLASSID_HEALTHCARE = 0x0F;
- public static final int CLASSID_AUDIOVIDEO = 0x10;
- public static final int CLASSID_BILLBOARD = 0x11;
- public static final int CLASSID_TYPECBRIDGE = 0x12;
- public static final int CLASSID_DIAGNOSTIC = 0xDC;
- public static final int CLASSID_WIRELESS = 0xE0;
- public static final int CLASSID_MISC = 0xEF;
- public static final int CLASSID_APPSPECIFIC = 0xFE;
+ public static final int CLASSID_SECURITY = 0x0D;
+ public static final int CLASSID_VIDEO = 0x0E;
+ public static final int CLASSID_HEALTHCARE = 0x0F;
+ public static final int CLASSID_AUDIOVIDEO = 0x10;
+ public static final int CLASSID_BILLBOARD = 0x11;
+ public static final int CLASSID_TYPECBRIDGE = 0x12;
+ public static final int CLASSID_DIAGNOSTIC = 0xDC;
+ public static final int CLASSID_WIRELESS = 0xE0;
+ public static final int CLASSID_MISC = 0xEF;
+ public static final int CLASSID_APPSPECIFIC = 0xFE;
public static final int CLASSID_VENDSPECIFIC = 0xFF;
// Audio Subclass codes
- public static final int AUDIO_SUBCLASS_UNDEFINED = 0x00;
- public static final int AUDIO_AUDIOCONTROL = 0x01;
- public static final int AUDIO_AUDIOSTREAMING = 0x02;
- public static final int AUDIO_MIDISTREAMING = 0x03;
+ public static final int AUDIO_SUBCLASS_UNDEFINED = 0x00;
+ public static final int AUDIO_AUDIOCONTROL = 0x01;
+ public static final int AUDIO_AUDIOSTREAMING = 0x02;
+ public static final int AUDIO_MIDISTREAMING = 0x03;
// Request IDs
- public static final int REQUEST_GET_STATUS = 0x00;
- public static final int REQUEST_CLEAR_FEATURE = 0x01;
- public static final int REQUEST_SET_FEATURE = 0x03;
- public static final int REQUEST_GET_ADDRESS = 0x05;
- public static final int REQUEST_GET_DESCRIPTOR = 0x06;
- public static final int REQUEST_SET_DESCRIPTOR = 0x07;
- public static final int REQUEST_GET_CONFIGURATION = 0x08;
- public static final int REQUEST_SET_CONFIGURATION = 0x09;
+ public static final int REQUEST_GET_STATUS = 0x00;
+ public static final int REQUEST_CLEAR_FEATURE = 0x01;
+ public static final int REQUEST_SET_FEATURE = 0x03;
+ public static final int REQUEST_GET_ADDRESS = 0x05;
+ public static final int REQUEST_GET_DESCRIPTOR = 0x06;
+ public static final int REQUEST_SET_DESCRIPTOR = 0x07;
+ public static final int REQUEST_GET_CONFIGURATION = 0x08;
+ public static final int REQUEST_SET_CONFIGURATION = 0x09;
// USB control transfer timeout
public static final int USB_CONTROL_TRANSFER_TIMEOUT_MS = 200;
@@ -163,7 +163,6 @@ public abstract class UsbDescriptor implements Reporting {
public int getOverUnderRunCount() {
return mOverUnderRunCount;
}
-
public String getStatusString() {
return sStatusStrings[mStatus];
}
@@ -278,4 +277,24 @@ public abstract class UsbDescriptor implements Reporting {
+ " Len: " + getLength();
canvas.writeParagraph(text, false);
}
+
+ /*
+ * Logging Helpers
+ */
+ static String getDescriptorName(byte descriptorType, int descriptorLength) {
+ String name = UsbStrings.getDescriptorName(descriptorType);
+ if (name != null) {
+ return name;
+ } else {
+ return "Unknown Descriptor Type " + descriptorType
+ + " 0x" + Integer.toHexString(descriptorType)
+ + " length:" + descriptorLength;
+ }
+ }
+
+ static void logDescriptorName(byte descriptorType, int descriptorLength) {
+ if (UsbDescriptorParser.DEBUG) {
+ Log.d(TAG, "----> " + getDescriptorName(descriptorType, descriptorLength));
+ }
+ }
}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
index c02110186d8d..43d5bf323abb 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
@@ -26,7 +26,7 @@ import java.util.ArrayList;
*/
public final class UsbDescriptorParser {
private static final String TAG = "UsbDescriptorParser";
- private static final boolean DEBUG = false;
+ public static final boolean DEBUG = false;
private final String mDeviceAddr;
@@ -43,6 +43,11 @@ public final class UsbDescriptorParser {
// Obtained from the first AudioClass Header descriptor.
private int mACInterfacesSpec = UsbDeviceDescriptor.USBSPEC_1_0;
+ // The VideoClass spec implemented by the VideoClass Interfaces
+ // This may well be different than the overall USB Spec.
+ // Obtained from the first VidieoClass Header descriptor.
+ private int mVCInterfacesSpec = UsbDeviceDescriptor.USBSPEC_1_0;
+
/**
* Connect this parser to an existing set of already parsed descriptors.
* This is useful for reporting.
@@ -90,6 +95,14 @@ public final class UsbDescriptorParser {
return mACInterfacesSpec;
}
+ public void setVCInterfaceSpec(int spec) {
+ mVCInterfacesSpec = spec;
+ }
+
+ public int getVCInterfaceSpec() {
+ return mVCInterfacesSpec;
+ }
+
private class UsbDescriptorsStreamFormatException extends Exception {
String mMessage;
UsbDescriptorsStreamFormatException(String message) {
@@ -115,6 +128,8 @@ public final class UsbDescriptorParser {
int length = stream.getUnsignedByte();
byte type = stream.getByte();
+ UsbDescriptor.logDescriptorName(type, length);
+
UsbDescriptor descriptor = null;
switch (type) {
/*
@@ -174,14 +189,66 @@ public final class UsbDescriptorParser {
break;
/*
- * Audio Class Specific
+ * Various Class Specific
*/
- case UsbDescriptor.DESCRIPTORTYPE_AUDIO_INTERFACE:
- descriptor = UsbACInterface.allocDescriptor(this, stream, length, type);
+ case UsbDescriptor.DESCRIPTORTYPE_CLASSSPECIFIC_INTERFACE:
+ if (mCurInterfaceDescriptor != null) {
+ switch (mCurInterfaceDescriptor.getUsbClass()) {
+ case UsbDescriptor.CLASSID_AUDIO:
+ descriptor = UsbACInterface.allocDescriptor(this, stream, length, type);
+ break;
+
+ case UsbDescriptor.CLASSID_VIDEO:
+ if (DEBUG) {
+ Log.d(TAG, " UsbDescriptor.CLASSID_VIDEO");
+ }
+ descriptor = UsbVCInterface.allocDescriptor(this, stream, length, type);
+ break;
+
+ case UsbDescriptor.CLASSID_AUDIOVIDEO:
+ if (DEBUG) {
+ Log.d(TAG, " UsbDescriptor.CLASSID_AUDIOVIDEO");
+ }
+ break;
+
+ default:
+ Log.w(TAG, " Unparsed Class-specific");
+ break;
+ }
+ }
break;
- case UsbDescriptor.DESCRIPTORTYPE_AUDIO_ENDPOINT:
- descriptor = UsbACEndpoint.allocDescriptor(this, length, type);
+ case UsbDescriptor.DESCRIPTORTYPE_CLASSSPECIFIC_ENDPOINT:
+ if (mCurInterfaceDescriptor != null) {
+ int subClass = mCurInterfaceDescriptor.getUsbClass();
+ switch (subClass) {
+ case UsbDescriptor.CLASSID_AUDIO:
+ descriptor = UsbACEndpoint.allocDescriptor(this, length, type);
+ break;
+
+ case UsbDescriptor.CLASSID_VIDEO: {
+ Byte subtype = stream.getByte();
+ if (DEBUG) {
+ Log.d(TAG, "UsbDescriptor.CLASSID_VIDEO type:0x"
+ + Integer.toHexString(type));
+ }
+ descriptor = UsbVCEndpoint.allocDescriptor(this, length, type, subtype);
+ }
+ break;
+
+ case UsbDescriptor.CLASSID_AUDIOVIDEO:
+ if (DEBUG) {
+ Log.d(TAG, "UsbDescriptor.CLASSID_AUDIOVIDEO type:0x"
+ + Integer.toHexString(type));
+ }
+ break;
+
+ default:
+ Log.w(TAG, " Unparsed Class-specific Endpoint:0x"
+ + Integer.toHexString(subClass));
+ break;
+ }
+ }
break;
default:
@@ -190,8 +257,6 @@ public final class UsbDescriptorParser {
if (descriptor == null) {
// Unknown Descriptor
- Log.i(TAG, "Unknown Descriptor len: " + length + " type:0x"
- + Integer.toHexString(type));
descriptor = new UsbUnknown(length, type);
}
@@ -210,10 +275,6 @@ public final class UsbDescriptorParser {
* @hide
*/
public void parseDescriptors(byte[] descriptors) {
- if (DEBUG) {
- Log.d(TAG, "parseDescriptors() - start");
- }
-
ByteStream stream = new ByteStream(descriptors);
while (stream.available() > 0) {
UsbDescriptor descriptor = null;
@@ -231,9 +292,27 @@ public final class UsbDescriptorParser {
// Clean up
descriptor.postParse(stream);
} catch (Exception ex) {
- Log.e(TAG, "Exception parsing USB descriptors.", ex);
+ // Clean up, compute error status
+ descriptor.postParse(stream);
- // Clean up
+ // Report
+ Log.w(TAG, "Exception parsing USB descriptors. type:0x" + descriptor.getType()
+ + " status:" + descriptor.getStatus());
+ if (DEBUG) {
+ // Show full stack trace if debugging
+ Log.e(TAG, "Exception parsing USB descriptors.", ex);
+ }
+ StackTraceElement[] stackElems = ex.getStackTrace();
+ if (stackElems.length > 0) {
+ Log.i(TAG, " class:" + stackElems[0].getClassName()
+ + " @ " + stackElems[0].getLineNumber());
+ }
+ if (stackElems.length > 1) {
+ Log.i(TAG, " class:" + stackElems[1].getClassName()
+ + " @ " + stackElems[1].getLineNumber());
+ }
+
+ // Finish up
descriptor.setStatus(UsbDescriptor.STATUS_PARSE_EXCEPTION);
} finally {
mDescriptors.add(descriptor);
@@ -271,17 +350,17 @@ public final class UsbDescriptorParser {
/**
* @hide
*/
- public UsbDevice.Builder toAndroidUsbDevice() {
+ public UsbDevice.Builder toAndroidUsbDeviceBuilder() {
if (mDeviceDescriptor == null) {
Log.e(TAG, "toAndroidUsbDevice() ERROR - No Device Descriptor");
return null;
}
- UsbDevice.Builder device = mDeviceDescriptor.toAndroid(this);
- if (device == null) {
+ UsbDevice.Builder builder = mDeviceDescriptor.toAndroid(this);
+ if (builder == null) {
Log.e(TAG, "toAndroidUsbDevice() ERROR Creating Device");
}
- return device;
+ return builder;
}
/**
@@ -325,8 +404,8 @@ public final class UsbDescriptorParser {
public ArrayList<UsbDescriptor> getACInterfaceDescriptors(byte subtype, int subclass) {
ArrayList<UsbDescriptor> list = new ArrayList<UsbDescriptor>();
for (UsbDescriptor descriptor : mDescriptors) {
- if (descriptor.getType() == UsbDescriptor.DESCRIPTORTYPE_AUDIO_INTERFACE) {
- // ensure that this isn't an unrecognized DESCRIPTORTYPE_AUDIO_INTERFACE
+ if (descriptor.getType() == UsbDescriptor.DESCRIPTORTYPE_CLASSSPECIFIC_INTERFACE) {
+ // ensure that this isn't an unrecognized DESCRIPTORTYPE_CLASSSPECIFIC_INTERFACE
if (descriptor instanceof UsbACInterface) {
UsbACInterface acDescriptor = (UsbACInterface) descriptor;
if (acDescriptor.getSubtype() == subtype
@@ -334,8 +413,8 @@ public final class UsbDescriptorParser {
list.add(descriptor);
}
} else {
- Log.w(TAG, "Unrecognized Audio Interface l: " + descriptor.getLength()
- + " t:0x" + Integer.toHexString(descriptor.getType()));
+ Log.w(TAG, "Unrecognized Audio Interface len: " + descriptor.getLength()
+ + " type:0x" + Integer.toHexString(descriptor.getType()));
}
}
}
@@ -486,6 +565,61 @@ public final class UsbDescriptorParser {
/**
* @hide
*/
+ public boolean hasAudioTerminal(int subType) {
+ for (UsbDescriptor descriptor : mDescriptors) {
+ if (descriptor instanceof UsbACInterface) {
+ if (((UsbACInterface) descriptor).getSubclass()
+ == UsbDescriptor.AUDIO_AUDIOCONTROL
+ && ((UsbACInterface) descriptor).getSubtype()
+ == subType) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @hide
+ */
+ public boolean hasAudioPlayback() {
+ return hasAudioTerminal(UsbACInterface.ACI_OUTPUT_TERMINAL);
+ }
+
+ /**
+ * @hide
+ */
+ public boolean hasAudioCapture() {
+ return hasAudioTerminal(UsbACInterface.ACI_INPUT_TERMINAL);
+ }
+
+ /**
+ * @hide
+ */
+ public boolean hasVideoCapture() {
+ for (UsbDescriptor descriptor : mDescriptors) {
+ if (descriptor instanceof UsbVCInputTerminal) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @hide
+ */
+ public boolean hasVideoPlayback() {
+ for (UsbDescriptor descriptor : mDescriptors) {
+ if (descriptor instanceof UsbVCOutputTerminal) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @hide
+ */
public boolean hasHIDInterface() {
ArrayList<UsbDescriptor> descriptors =
getInterfaceDescriptorsForClass(UsbDescriptor.CLASSID_HID);
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java
index f50b9cbc5e3d..b1cbbafb9ccd 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java
@@ -31,7 +31,6 @@ import java.util.ArrayList;
*/
public final class UsbDeviceDescriptor extends UsbDescriptor {
private static final String TAG = "UsbDeviceDescriptor";
- private static final boolean DEBUG = false;
public static final int USBSPEC_1_0 = 0x0100;
public static final int USBSPEC_1_1 = 0x0110;
@@ -136,19 +135,19 @@ public final class UsbDeviceDescriptor extends UsbDescriptor {
* @hide
*/
public UsbDevice.Builder toAndroid(UsbDescriptorParser parser) {
- if (DEBUG) {
+ if (UsbDescriptorParser.DEBUG) {
Log.d(TAG, "toAndroid()");
}
String mfgName = getMfgString(parser);
String prodName = getProductString(parser);
- if (DEBUG) {
+ if (UsbDescriptorParser.DEBUG) {
Log.d(TAG, " mfgName:" + mfgName + " prodName:" + prodName);
}
String versionString = getDeviceReleaseString();
String serialStr = getSerialString(parser);
- if (DEBUG) {
+ if (UsbDescriptorParser.DEBUG) {
Log.d(TAG, " versionString:" + versionString + " serialStr:" + serialStr);
}
@@ -157,11 +156,12 @@ public final class UsbDeviceDescriptor extends UsbDescriptor {
for (int index = 0; index < mConfigDescriptors.size(); index++) {
configs[index] = mConfigDescriptors.get(index).toAndroid(parser);
}
- UsbDevice.Builder device = new UsbDevice.Builder(parser.getDeviceAddr(), mVendorID,
- mProductID, mDevClass, mDevSubClass, mProtocol, mfgName, prodName, versionString,
- configs, serialStr);
- return device;
+ return new UsbDevice.Builder(parser.getDeviceAddr(), mVendorID,
+ mProductID, mDevClass, mDevSubClass, mProtocol, mfgName, prodName, versionString,
+ configs, serialStr, parser.hasAudioPlayback(), parser.hasAudioCapture(),
+ parser.hasMIDIInterface(),
+ parser.hasVideoPlayback(), parser.hasVideoCapture());
}
@Override
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java
index 4da31ea469c3..4d0cfea98630 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java
@@ -27,12 +27,11 @@ import com.android.server.usb.descriptors.report.ReportCanvas;
*/
public class UsbEndpointDescriptor extends UsbDescriptor {
private static final String TAG = "UsbEndpointDescriptor";
- private static final boolean DEBUG = false;
public static final int MASK_ENDPOINT_ADDRESS = 0b000000000001111;
public static final int MASK_ENDPOINT_DIRECTION = (byte) 0b0000000010000000;
public static final int DIRECTION_OUTPUT = 0x0000;
- public static final int DIRECTION_INPUT = (byte) 0x0080;
+ public static final int DIRECTION_INPUT = 0x0080;
public static final int MASK_ATTRIBS_TRANSTYPE = 0b00000011;
public static final int TRANSTYPE_CONTROL = 0x00;
@@ -86,7 +85,7 @@ public class UsbEndpointDescriptor extends UsbDescriptor {
}
public int getEndpointAddress() {
- return mEndpointAddress;
+ return mEndpointAddress & MASK_ENDPOINT_ADDRESS;
}
public int getAttributes() {
@@ -109,8 +108,12 @@ public class UsbEndpointDescriptor extends UsbDescriptor {
return mSyncAddress;
}
+ public int getDirection() {
+ return mEndpointAddress & UsbEndpointDescriptor.MASK_ENDPOINT_DIRECTION;
+ }
+
/* package */ UsbEndpoint toAndroid(UsbDescriptorParser parser) {
- if (DEBUG) {
+ if (UsbDescriptorParser.DEBUG) {
Log.d(TAG, "toAndroid() type:"
+ Integer.toHexString(mAttributes & MASK_ATTRIBS_TRANSTYPE)
+ " sync:" + Integer.toHexString(mAttributes & MASK_ATTRIBS_SYNCTYPE)
@@ -138,11 +141,9 @@ public class UsbEndpointDescriptor extends UsbDescriptor {
canvas.openList();
- int address = getEndpointAddress();
canvas.writeListItem("Address: "
- + ReportCanvas.getHexString(address & UsbEndpointDescriptor.MASK_ENDPOINT_ADDRESS)
- + ((address & UsbEndpointDescriptor.MASK_ENDPOINT_DIRECTION)
- == UsbEndpointDescriptor.DIRECTION_OUTPUT ? " [out]" : " [in]"));
+ + ReportCanvas.getHexString(getEndpointAddress())
+ + (getDirection() == UsbEndpointDescriptor.DIRECTION_OUTPUT ? " [out]" : " [in]"));
int attributes = getAttributes();
canvas.openListItem();
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
index 632e3dc500fa..64dbd971cc67 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
@@ -31,8 +31,6 @@ import java.util.ArrayList;
*/
public class UsbInterfaceDescriptor extends UsbDescriptor {
private static final String TAG = "UsbInterfaceDescriptor";
- private static final boolean DEBUG = false;
-
protected int mInterfaceNumber; // 2:1 Number of Interface
protected byte mAlternateSetting; // 3:1 Value used to select alternative setting
protected byte mNumEndpoints; // 4:1 Number of Endpoints used for this interface
@@ -74,6 +72,19 @@ public class UsbInterfaceDescriptor extends UsbDescriptor {
return mNumEndpoints;
}
+ /**
+ * @param index Index of desired UsbEndpointDescriptor.
+ * @return the UsbEndpointDescriptor descriptor at the specified index, or
+ * null if an invalid index.
+ */
+ public UsbEndpointDescriptor getEndpointDescriptor(int index) {
+ if (index < 0 || index >= mEndpointDescriptors.size()) {
+ return null;
+ }
+
+ return mEndpointDescriptors.get(index);
+ }
+
public int getUsbClass() {
return mUsbClass;
}
@@ -95,7 +106,7 @@ public class UsbInterfaceDescriptor extends UsbDescriptor {
}
UsbInterface toAndroid(UsbDescriptorParser parser) {
- if (DEBUG) {
+ if (UsbDescriptorParser.DEBUG) {
Log.d(TAG, "toAndroid() class:" + Integer.toHexString(mUsbClass)
+ " subclass:" + Integer.toHexString(mUsbSubclass)
+ " " + mEndpointDescriptors.size() + " endpoints.");
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbVCEndpoint.java b/services/usb/java/com/android/server/usb/descriptors/UsbVCEndpoint.java
new file mode 100644
index 000000000000..f9acecee6bcf
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbVCEndpoint.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.usb.descriptors;
+
+import android.util.Log;
+
+/**
+ * @hide
+ * A video class-specific Endpoint
+ * see USB_Video_Class_1.1.pdf - 3.10 VideoStreaming Endpoint Descriptors
+ */
+abstract class UsbVCEndpoint extends UsbDescriptor {
+ private static final String TAG = "UsbVCEndpoint";
+
+
+ public static final byte VCEP_UNDEFINED = 0x00;
+ public static final byte VCEP_GENERAL = 0x01;
+ public static final byte VCEP_ENDPOINT = 0x02;
+ public static final byte VCEP_INTERRUPT = 0x03;
+
+ UsbVCEndpoint(int length, byte type) {
+ super(length, type);
+ }
+
+ public static UsbDescriptor allocDescriptor(UsbDescriptorParser parser,
+ int length, byte type, byte subtype) {
+ UsbInterfaceDescriptor interfaceDesc = parser.getCurInterface();
+
+ // TODO - create classes for each specific subtype
+ // (don't need it to answer if this device supports video
+ switch (subtype) {
+ case VCEP_UNDEFINED:
+ if (UsbDescriptorParser.DEBUG) {
+ Log.d(TAG, "---> VCEP_UNDEFINED");
+ }
+ return null;
+
+ case VCEP_GENERAL:
+ if (UsbDescriptorParser.DEBUG) {
+ Log.d(TAG, "---> VCEP_GENERAL");
+ }
+ return null;
+
+ case VCEP_ENDPOINT:
+ if (UsbDescriptorParser.DEBUG) {
+ Log.d(TAG, "---> VCEP_ENDPOINT");
+ }
+ return null;
+
+ case VCEP_INTERRUPT:
+ if (UsbDescriptorParser.DEBUG) {
+ Log.d(TAG, "---> VCEP_INTERRUPT");
+ }
+ return null;
+
+ default:
+ Log.w(TAG, "Unknown Video Class Endpoint id:0x" + Integer.toHexString(subtype));
+ return null;
+ }
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbVCHeader.java b/services/usb/java/com/android/server/usb/descriptors/UsbVCHeader.java
new file mode 100644
index 000000000000..3fc42241b550
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbVCHeader.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.usb.descriptors;
+
+import android.util.Log;
+
+import com.android.server.usb.descriptors.report.ReportCanvas;
+
+/**
+ * @hide
+ * A video class-specific Interface Header.
+ * see USB_Video_Class_1.1.pdf section 3.9.2 - Class-Specific VS Interface Descriptors
+ */
+public final class UsbVCHeader extends UsbVCHeaderInterface {
+ private static final String TAG = "UsbVCHeader";
+
+ // TODO Add data members for this descriptor's data
+
+ public UsbVCHeader(int length, byte type, byte subtype, int spec) {
+ super(length, type, subtype, spec);
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ if (UsbDescriptorParser.DEBUG) {
+ Log.d(TAG, " ---> parseRawDescriptors()");
+ }
+ // TODO parse data members for this descriptor's data
+ return super.parseRawDescriptors(stream);
+ }
+
+ @Override
+ public void report(ReportCanvas canvas) {
+ super.report(canvas);
+ // TODO add reporting specific to this descriptor
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbVCHeaderInterface.java b/services/usb/java/com/android/server/usb/descriptors/UsbVCHeaderInterface.java
new file mode 100644
index 000000000000..372509105ac3
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbVCHeaderInterface.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.usb.descriptors;
+
+import com.android.server.usb.descriptors.report.ReportCanvas;
+
+/**
+ * @hide
+ * A video class-specific Interface Header super class.
+ * see USB_Video_Class_1.1.pdf section 3.9.2 - Class-Specific VS Interface Descriptors
+ */
+public abstract class UsbVCHeaderInterface extends UsbVCInterface {
+ private static final String TAG = "UsbVCHeaderInterface";
+
+ protected int mVDCRelease; // Video Device Class Specification Release (BCD).
+ protected int mTotalLength; // Total number of bytes returned for the class-specific
+ // VideoControl interface descriptor. Includes the combined length
+ // of this descriptor header and all Unit and Terminal descriptors.
+
+ public UsbVCHeaderInterface(
+ int length, byte type, byte subtype, int vdcRelease) {
+ super(length, type, subtype);
+ mVDCRelease = vdcRelease;
+ }
+
+ public int getVDCRelease() {
+ return mVDCRelease;
+ }
+
+ public int getTotalLength() {
+ return mTotalLength;
+ }
+
+ @Override
+ public void report(ReportCanvas canvas) {
+ super.report(canvas);
+
+ canvas.openList();
+ canvas.writeListItem("Release: " + ReportCanvas.getBCDString(getVDCRelease()));
+ canvas.writeListItem("Total Length: " + getTotalLength());
+ canvas.closeList();
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbVCInputTerminal.java b/services/usb/java/com/android/server/usb/descriptors/UsbVCInputTerminal.java
new file mode 100644
index 000000000000..df637950899b
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbVCInputTerminal.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.usb.descriptors;
+
+import android.util.Log;
+
+import com.android.server.usb.descriptors.report.ReportCanvas;
+
+/**
+ * @hide
+ * A video class-specific Input terminal interface.
+ * see USB_Video_Class_1.1.pdf section 3.7.2.1 Input Terminal Descriptor
+ */
+public final class UsbVCInputTerminal extends UsbVCInterface {
+ private static final String TAG = "UsbVCInputTerminal";
+
+ // TODO Define members to hold the data from this descriptor
+ public UsbVCInputTerminal(int length, byte type, byte subtype) {
+ super(length, type, subtype);
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ // TODO Parse the data from this descriptor
+ if (UsbDescriptorParser.DEBUG) {
+ Log.d(TAG, " ---> parseRawDescriptors()");
+ }
+ return super.parseRawDescriptors(stream);
+ }
+
+ @Override
+ public void report(ReportCanvas canvas) {
+ // TODO Add reporting specific to this descriptor
+ super.report(canvas);
+ }
+};
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbVCInterface.java b/services/usb/java/com/android/server/usb/descriptors/UsbVCInterface.java
new file mode 100644
index 000000000000..46263dde5564
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbVCInterface.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.usb.descriptors;
+
+import android.util.Log;
+
+/**
+ * @hide
+ * A video class-specific Interface.
+ * see USB_Video_Class_1.1.pdf, section 3.7.2
+ */
+public abstract class UsbVCInterface extends UsbDescriptor {
+ private static final String TAG = "UsbVCInterface";
+
+ // Class-specific Video Subtypes
+ public static final byte VCI_UNDEFINED = 0x00;
+ public static final byte VCI_VEADER = 0x01;
+ public static final byte VCI_INPUT_TERMINAL = 0x02;
+ public static final byte VCI_OUTPUT_TERMINAL = 0x03;
+ public static final byte VCI_SELECTOR_UNIT = 0x04;
+ public static final byte VCI_PROCESSING_UNIT = 0x05;
+ public static final byte VCI_EXTENSION_UNIT = 0x06;
+
+ // See “Universal Serial Bus Device Class Definition for Video
+ protected final byte mSubtype; // 2:1 HEADER descriptor subtype
+
+ public UsbVCInterface(int length, byte type, byte subtype) {
+ super(length, type);
+ mSubtype = subtype;
+ }
+
+ /**
+ * Allocates an audio class interface subtype based on subtype and subclass.
+ */
+ public static UsbDescriptor allocDescriptor(UsbDescriptorParser parser, ByteStream stream,
+ int length, byte type) {
+ byte subtype = stream.getByte();
+ UsbInterfaceDescriptor interfaceDesc = parser.getCurInterface();
+ if (UsbDescriptorParser.DEBUG) {
+ Log.d(TAG, " Video Class-specific Interface subtype: " + subtype);
+ }
+ switch (subtype) {
+ // TODO - Create descriptor classes and parse these...
+ case VCI_UNDEFINED:
+ if (UsbDescriptorParser.DEBUG) {
+ Log.d(TAG, " ---> VCI_UNDEFINED");
+ }
+ break;
+
+ case VCI_VEADER:
+ {
+ if (UsbDescriptorParser.DEBUG) {
+ Log.d(TAG, " ---> VCI_VEADER");
+ }
+ int vcInterfaceSpec = stream.unpackUsbShort();
+ parser.setVCInterfaceSpec(vcInterfaceSpec);
+ if (UsbDescriptorParser.DEBUG) {
+ Log.d(TAG, " vcInterfaceSpec:0x" + Integer.toHexString(vcInterfaceSpec));
+ }
+ return new UsbVCHeader(length, type, subtype, vcInterfaceSpec);
+ }
+
+ case VCI_INPUT_TERMINAL:
+ if (UsbDescriptorParser.DEBUG) {
+ Log.d(TAG, " ---> VCI_INPUT_TERMINAL");
+ }
+ return new UsbVCInputTerminal(length, type, subtype);
+
+ case VCI_OUTPUT_TERMINAL:
+ if (UsbDescriptorParser.DEBUG) {
+ Log.d(TAG, " ---> VCI_OUTPUT_TERMINAL");
+ }
+ return new UsbVCOutputTerminal(length, type, subtype);
+
+ case VCI_SELECTOR_UNIT:
+ if (UsbDescriptorParser.DEBUG) {
+ Log.d(TAG, " ---> VCI_SELECTOR_UNIT");
+ }
+ return new UsbVCSelectorUnit(length, type, subtype);
+
+ case VCI_PROCESSING_UNIT:
+ if (UsbDescriptorParser.DEBUG) {
+ Log.d(TAG, " ---> VCI_PROCESSING_UNIT");
+ }
+ return new UsbVCProcessingUnit(length, type, subtype);
+
+ case VCI_EXTENSION_UNIT:
+ if (UsbDescriptorParser.DEBUG) {
+ Log.d(TAG, " ---> VCI_EXTENSION_UNIT");
+ }
+ break;
+
+ default:
+ Log.w(TAG, "Unknown Video Class Interface subtype: 0x"
+ + Integer.toHexString(subtype));
+ return null;
+ }
+
+ return null;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbVCOutputTerminal.java b/services/usb/java/com/android/server/usb/descriptors/UsbVCOutputTerminal.java
new file mode 100644
index 000000000000..4aa8ca22cc4e
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbVCOutputTerminal.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.usb.descriptors;
+
+import android.util.Log;
+
+import com.android.server.usb.descriptors.report.ReportCanvas;
+
+/**
+ * @hide
+ * A video class-specific Output Terminal Descriptor.
+ * see USB_Video_Class_1.1.pdf section 3.7.2.2 Output Terminal Descriptor
+ */
+public final class UsbVCOutputTerminal extends UsbVCInterface {
+ private static final String TAG = "UsbVCOutputTerminal";
+
+ // TODO Add members for the data in this descriptor
+ public UsbVCOutputTerminal(int length, byte type, byte subtype) {
+ super(length, type, subtype);
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ // TODO Parse the data in this descriptor
+ if (UsbDescriptorParser.DEBUG) {
+ Log.d(TAG, " ---> parseRawDescriptors()");
+ }
+ return super.parseRawDescriptors(stream);
+ }
+
+ @Override
+ public void report(ReportCanvas canvas) {
+ super.report(canvas);
+ // TODO Add reporting specific to this descriptor
+ }
+};
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbVCProcessingUnit.java b/services/usb/java/com/android/server/usb/descriptors/UsbVCProcessingUnit.java
new file mode 100644
index 000000000000..5ce842e82598
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbVCProcessingUnit.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.usb.descriptors;
+
+import android.util.Log;
+
+import com.android.server.usb.descriptors.report.ReportCanvas;
+
+/**
+ * @hide
+ * An video class-specific Processing Unit Interface.
+ * see USB_Video_Class_1.1.pdf section Table 3-8 Processing Unit Descriptor
+ */
+public final class UsbVCProcessingUnit extends UsbVCInterface {
+ private static final String TAG = "UsbVCProcessingUnit";
+
+ // TODO Add data members for this descriptor
+
+ public UsbVCProcessingUnit(int length, byte type, byte subtype) {
+ super(length, type, subtype);
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ // TODO Parse this descriptor
+ if (UsbDescriptorParser.DEBUG) {
+ Log.d(TAG, " ---> parseRawDescriptors()");
+ }
+ return super.parseRawDescriptors(stream);
+ }
+
+ @Override
+ public void report(ReportCanvas canvas) {
+ super.report(canvas);
+ // TODO Add reporting specific to this descriptor
+ }
+};
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbVCSelectorUnit.java b/services/usb/java/com/android/server/usb/descriptors/UsbVCSelectorUnit.java
new file mode 100644
index 000000000000..8e9b0d886389
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbVCSelectorUnit.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.usb.descriptors;
+
+import android.util.Log;
+
+import com.android.server.usb.descriptors.report.ReportCanvas;
+
+/**
+ * @hide
+ * An video class-specific Selector Unit Descriptor
+ * see USB_Video_Class_1.1.pdf section 3.7.2.4
+ */
+public final class UsbVCSelectorUnit extends UsbVCInterface {
+ private static final String TAG = "UsbVCSelectorUnit";
+
+ // TODO Add data members for this descriptor
+
+ public UsbVCSelectorUnit(int length, byte type, byte subtype) {
+ super(length, type, subtype);
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ // TODO Parse this descriptor
+ if (UsbDescriptorParser.DEBUG) {
+ Log.d(TAG, " ---> parseRawDescriptors()");
+ }
+ return super.parseRawDescriptors(stream);
+ }
+
+ @Override
+ public void report(ReportCanvas canvas) {
+ super.report(canvas);
+ // TODO Add reporting specific to this descriptor
+ }
+};
diff --git a/services/usb/java/com/android/server/usb/descriptors/report/UsbStrings.java b/services/usb/java/com/android/server/usb/descriptors/report/UsbStrings.java
index fb4576a6ee78..918ba2cc9249 100644
--- a/services/usb/java/com/android/server/usb/descriptors/report/UsbStrings.java
+++ b/services/usb/java/com/android/server/usb/descriptors/report/UsbStrings.java
@@ -56,9 +56,10 @@ public final class UsbStrings {
sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_HID, "HID");
sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_REPORT, "Report");
sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_PHYSICAL, "Physical");
- sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_AUDIO_INTERFACE,
- "Audio Class Interface");
- sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_AUDIO_ENDPOINT, "Audio Class Endpoint");
+ sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_CLASSSPECIFIC_INTERFACE,
+ "Class-specific Interface");
+ sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_CLASSSPECIFIC_ENDPOINT,
+ "Class-specific Endpoint");
sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_HUB, "Hub");
sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_SUPERSPEED_HUB, "Superspeed Hub");
sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_ENDPOINT_COMPANION,
diff --git a/services/usb/java/com/android/server/usb/descriptors/tree/UsbDescriptorsTree.java b/services/usb/java/com/android/server/usb/descriptors/tree/UsbDescriptorsTree.java
index 1aa30fa94f42..72fa8977b675 100644
--- a/services/usb/java/com/android/server/usb/descriptors/tree/UsbDescriptorsTree.java
+++ b/services/usb/java/com/android/server/usb/descriptors/tree/UsbDescriptorsTree.java
@@ -126,11 +126,13 @@ public final class UsbDescriptorsTree {
//
// Audio Class Descriptors
//
- case UsbDescriptor.DESCRIPTORTYPE_AUDIO_INTERFACE:
- addACInterface((UsbACInterface) descriptor);
+ case UsbDescriptor.DESCRIPTORTYPE_CLASSSPECIFIC_INTERFACE:
+ //TODO: This needs to be parsed out to Audio/Video...
+ // addACInterface((UsbACInterface) descriptor);
break;
- case UsbDescriptor.DESCRIPTORTYPE_AUDIO_ENDPOINT:
+ case UsbDescriptor.DESCRIPTORTYPE_CLASSSPECIFIC_ENDPOINT:
+ //TODO: This needs to be parsed out to Audio/Video...
break;
}
}