Merge "Adding USB Headset awareness." into oc-dr1-dev
diff --git a/services/core/jni/Android.mk b/services/core/jni/Android.mk
index a3d28bb..e2a82b7 100644
--- a/services/core/jni/Android.mk
+++ b/services/core/jni/Android.mk
@@ -33,6 +33,7 @@
$(LOCAL_REL_DIR)/com_android_server_tv_TvInputHal.cpp \
$(LOCAL_REL_DIR)/com_android_server_vr_VrManagerService.cpp \
$(LOCAL_REL_DIR)/com_android_server_UsbDeviceManager.cpp \
+ $(LOCAL_REL_DIR)/com_android_server_UsbDescriptorParser.cpp \
$(LOCAL_REL_DIR)/com_android_server_UsbMidiDevice.cpp \
$(LOCAL_REL_DIR)/com_android_server_UsbHostManager.cpp \
$(LOCAL_REL_DIR)/com_android_server_VibratorService.cpp \
diff --git a/services/core/jni/com_android_server_UsbDescriptorParser.cpp b/services/core/jni/com_android_server_UsbDescriptorParser.cpp
new file mode 100644
index 0000000..98c5ec1
--- /dev/null
+++ b/services/core/jni/com_android_server_UsbDescriptorParser.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#define LOG_TAG "UsbHostManagerJNI"
+#include "utils/Log.h"
+
+#include "jni.h"
+#include "JNIHelp.h"
+
+#include <usbhost/usbhost.h>
+
+#define MAX_DESCRIPTORS_LENGTH 16384
+
+// com.android.server.usb.descriptors
+extern "C" {
+jbyteArray JNICALL Java_com_android_server_usb_descriptors_UsbDescriptorParser_getRawDescriptors(
+ JNIEnv* env, jobject thiz, jstring deviceAddr) {
+ const char *deviceAddrStr = env->GetStringUTFChars(deviceAddr, NULL);
+ struct usb_device* device = usb_device_open(deviceAddrStr);
+ env->ReleaseStringUTFChars(deviceAddr, deviceAddrStr);
+
+ if (!device) {
+ ALOGE("usb_device_open failed");
+ return NULL;
+ }
+
+ int fd = usb_device_get_fd(device);
+ if (fd < 0) {
+ return NULL;
+ }
+
+ // from android_hardware_UsbDeviceConnection_get_desc()
+ jbyte buffer[MAX_DESCRIPTORS_LENGTH];
+ lseek(fd, 0, SEEK_SET);
+ int numBytes = read(fd, buffer, sizeof(buffer));
+
+ usb_device_close(device);
+
+ jbyteArray ret = NULL;
+ if (numBytes != 0) {
+ ret = env->NewByteArray(numBytes);
+ env->SetByteArrayRegion(ret, 0, numBytes, buffer);
+ }
+ return ret;
+}
+
+} // extern "C"
+
+
diff --git a/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
index d315b18..68c1d5f 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java
@@ -65,6 +65,9 @@
private final HashMap<UsbDevice,UsbAudioDevice>
mAudioDevices = new HashMap<UsbDevice,UsbAudioDevice>();
+ private boolean mIsInputHeadset; // as reported by UsbDescriptorParser
+ private boolean mIsOutputHeadset; // as reported by UsbDescriptorParser
+
private final HashMap<UsbDevice,UsbMidiDevice>
mMidiDevices = new HashMap<UsbDevice,UsbMidiDevice>();
@@ -184,9 +187,14 @@
try {
// Playback Device
if (audioDevice.mHasPlayback) {
- int device = (audioDevice == mAccessoryAudioDevice ?
- AudioSystem.DEVICE_OUT_USB_ACCESSORY :
- AudioSystem.DEVICE_OUT_USB_DEVICE);
+ int device;
+ if (mIsOutputHeadset) {
+ device = AudioSystem.DEVICE_OUT_USB_HEADSET;
+ } else {
+ device = (audioDevice == mAccessoryAudioDevice
+ ? AudioSystem.DEVICE_OUT_USB_ACCESSORY
+ : AudioSystem.DEVICE_OUT_USB_DEVICE);
+ }
if (DEBUG) {
Slog.i(TAG, "pre-call device:0x" + Integer.toHexString(device) +
" addr:" + address + " name:" + audioDevice.getDeviceName());
@@ -197,9 +205,14 @@
// Capture Device
if (audioDevice.mHasCapture) {
- int device = (audioDevice == mAccessoryAudioDevice ?
- AudioSystem.DEVICE_IN_USB_ACCESSORY :
- AudioSystem.DEVICE_IN_USB_DEVICE);
+ int device;
+ if (mIsInputHeadset) {
+ device = AudioSystem.DEVICE_IN_USB_HEADSET;
+ } else {
+ device = (audioDevice == mAccessoryAudioDevice
+ ? AudioSystem.DEVICE_IN_USB_ACCESSORY
+ : AudioSystem.DEVICE_IN_USB_DEVICE);
+ }
mAudioService.setWiredDeviceConnectionState(
device, state, address, audioDevice.getDeviceName(), TAG);
}
@@ -343,12 +356,16 @@
return selectAudioCard(mCardsParser.getDefaultCard());
}
- /* package */ void usbDeviceAdded(UsbDevice usbDevice) {
- if (DEBUG) {
- Slog.d(TAG, "deviceAdded(): " + usbDevice.getManufacturerName() +
- " nm:" + usbDevice.getProductName());
+ /* package */ void usbDeviceAdded(UsbDevice usbDevice,
+ boolean isInputHeadset, boolean isOutputHeadset) {
+ if (DEBUG) {
+ Slog.d(TAG, "deviceAdded(): " + usbDevice.getManufacturerName()
+ + " nm:" + usbDevice.getProductName());
}
+ mIsInputHeadset = isInputHeadset;
+ mIsOutputHeadset = isOutputHeadset;
+
// Is there an audio interface in there?
boolean isAudioDevice = false;
diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java
index af78c05..40aadbb 100644
--- a/services/usb/java/com/android/server/usb/UsbHostManager.java
+++ b/services/usb/java/com/android/server/usb/UsbHostManager.java
@@ -16,7 +16,6 @@
package com.android.server.usb;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
@@ -32,6 +31,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.usb.descriptors.UsbDescriptorParser;
import java.util.ArrayList;
import java.util.HashMap;
@@ -259,7 +259,14 @@
getCurrentUserSettings().deviceAttachedForFixedHandler(mNewDevice,
usbDeviceConnectionHandler);
}
- mUsbAlsaManager.usbDeviceAdded(mNewDevice);
+ // deviceName is something like: "/dev/bus/usb/001/001"
+ UsbDescriptorParser parser = new UsbDescriptorParser();
+ if (parser.parseDevice(mNewDevice.getDeviceName())) {
+ Slog.i(TAG, "---- isHeadset[in:" + parser.isInputHeadset()
+ + " , out:" + parser.isOutputHeadset() + "]");
+ mUsbAlsaManager.usbDeviceAdded(mNewDevice,
+ parser.isInputHeadset(), parser.isOutputHeadset());
+ }
} else {
Slog.e(TAG, "mNewDevice is null in endUsbDeviceAdded");
}
diff --git a/services/usb/java/com/android/server/usb/descriptors/ByteStream.java b/services/usb/java/com/android/server/usb/descriptors/ByteStream.java
new file mode 100644
index 0000000..d678931
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/ByteStream.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2017 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.annotation.NonNull;
+
+/**
+ * @hide
+ * A stream interface wrapping a byte array. Very much like a java.io.ByteArrayInputStream
+ * but with the capability to "back up" in situations where the parser discovers that a
+ * UsbDescriptor has overrun its length.
+ */
+public class ByteStream {
+ private static final String TAG = "ByteStream";
+
+ /** The byte array being wrapped */
+ @NonNull
+ private final byte[] mBytes; // this is never null.
+
+ /**
+ * The index into the byte array to be read next.
+ * This value is altered by reading data out of the stream
+ * (using either the getByte() or unpack*() methods), or alternatively
+ * by explicitly offseting the stream position with either
+ * advance() or reverse().
+ */
+ private int mIndex;
+
+ /*
+ * This member used with resetReadCount() & getReadCount() can be used to determine how many
+ * bytes a UsbDescriptor subclass ACTUALLY reads (as opposed to what its length field says).
+ * using this info, the parser can mark a descriptor as valid or invalid and correct the stream
+ * position with advance() & reverse() to keep from "getting lost" in the descriptor stream.
+ */
+ private int mReadCount;
+
+ /**
+ * Create a ByteStream object wrapping the specified byte array.
+ *
+ * @param bytes The byte array containing the raw descriptor information retrieved from
+ * the USB device.
+ * @throws IllegalArgumentException
+ */
+ public ByteStream(@NonNull byte[] bytes) {
+ if (bytes == null) {
+ throw new IllegalArgumentException();
+ }
+ mBytes = bytes;
+ }
+
+ /**
+ * Resets the running count of bytes read so that later we can see how much more has been read.
+ */
+ public void resetReadCount() {
+ mReadCount = 0;
+ }
+
+ /**
+ * Retrieves the running count of bytes read from the stream.
+ */
+ public int getReadCount() {
+ return mReadCount;
+ }
+
+ /**
+ * @return The value of the next byte in the stream without advancing the stream.
+ * Does not affect the running count as the byte hasn't been "consumed".
+ * @throws IndexOutOfBoundsException
+ */
+ public byte peekByte() {
+ if (available() > 0) {
+ return mBytes[mIndex + 1];
+ } else {
+ throw new IndexOutOfBoundsException();
+ }
+ }
+
+ /**
+ * @return the next byte from the stream and advances the stream and the read count. Note
+ * that this is a signed byte (as is the case of byte in Java). The user may need to understand
+ * from context if it should be interpreted as an unsigned value.
+ * @throws IndexOutOfBoundsException
+ */
+ public byte getByte() {
+ if (available() > 0) {
+ mReadCount++;
+ return mBytes[mIndex++];
+ } else {
+ throw new IndexOutOfBoundsException();
+ }
+ }
+
+ /**
+ * Reads 2 bytes in *little endian format* from the stream and composes a 16-bit integer.
+ * As we are storing the 2-byte value in a 4-byte integer, the upper 2 bytes are always
+ * 0, essentially making the returned value *unsigned*.
+ * @return The 16-bit integer (packed into the lower 2 bytes of an int) encoded by the
+ * next 2 bytes in the stream.
+ * @throws IndexOutOfBoundsException
+ */
+ public int unpackUsbWord() {
+ if (available() >= 2) {
+ int b0 = getByte();
+ int b1 = getByte();
+ return ((b1 << 8) & 0x0000FF00) | (b0 & 0x000000FF);
+ } else {
+ throw new IndexOutOfBoundsException();
+ }
+ }
+
+ /**
+ * Reads 3 bytes in *little endian format* from the stream and composes a 24-bit integer.
+ * As we are storing the 3-byte value in a 4-byte integer, the upper byte is always
+ * 0, essentially making the returned value *unsigned*.
+ * @return The 24-bit integer (packed into the lower 3 bytes of an int) encoded by the
+ * next 3 bytes in the stream.
+ * @throws IndexOutOfBoundsException
+ */
+ public int unpackUsbTriple() {
+ if (available() >= 3) {
+ int b0 = getByte();
+ int b1 = getByte();
+ int b2 = getByte();
+ return ((b2 << 16) & 0x00FF0000) | ((b1 << 8) & 0x0000FF00) | (b0 & 0x000000FF);
+ } else {
+ throw new IndexOutOfBoundsException();
+ }
+ }
+
+ /**
+ * Advances the logical position in the stream. Affects the running count also.
+ * @param numBytes The number of bytes to advance.
+ * @throws IndexOutOfBoundsException
+ * @throws IllegalArgumentException
+ */
+ public void advance(int numBytes) {
+ if (numBytes < 0) {
+ // Positive offsets only
+ throw new IllegalArgumentException();
+ }
+ // do arithmetic and comparison in long to ovoid potention integer overflow
+ long longNewIndex = (long) mIndex + (long) numBytes;
+ if (longNewIndex < (long) mBytes.length) {
+ mReadCount += numBytes;
+ mIndex += numBytes;
+ } else {
+ throw new IndexOutOfBoundsException();
+ }
+ }
+
+ /**
+ * Reverse the logical position in the stream. Affects the running count also.
+ * @param numBytes The (positive) number of bytes to reverse.
+ * @throws IndexOutOfBoundsException
+ * @throws IllegalArgumentException
+ */
+ public void reverse(int numBytes) {
+ if (numBytes < 0) {
+ // Positive (reverse) offsets only
+ throw new IllegalArgumentException();
+ }
+ if (mIndex >= numBytes) {
+ mReadCount -= numBytes;
+ mIndex -= numBytes;
+ } else {
+ throw new IndexOutOfBoundsException();
+ }
+ }
+
+ /**
+ * @return The number of bytes available to be read in the stream.
+ */
+ public int available() {
+ return mBytes.length - mIndex;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACAudioControlEndpoint.java b/services/usb/java/com/android/server/usb/descriptors/UsbACAudioControlEndpoint.java
new file mode 100644
index 0000000..96fcc6a
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACAudioControlEndpoint.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2017 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;
+
+/**
+ * @hide
+ * An audio class-specific Audio Control Endpoint.
+ * audio10.pdf section 4.4.2.1
+ */
+public class UsbACAudioControlEndpoint extends UsbACEndpoint {
+ private static final String TAG = "ACAudioControlEndpoint";
+
+ private byte mAddress; // 2:1 The address of the endpoint on the USB device.
+ // D7: Direction. 1 = IN endpoint
+ // D6..4: Reserved, reset to zero
+ // D3..0: The endpoint number.
+ private byte mAttribs; // 3:1 (see ATTRIBSMASK_* below
+ private int mMaxPacketSize; // 4:2 Maximum packet size this endpoint is capable of sending
+ // or receiving when this configuration is selected.
+ private byte mInterval; // 6:1
+
+ static final byte ADDRESSMASK_DIRECTION = (byte) 0x80;
+ static final byte ADDRESSMASK_ENDPOINT = 0x0F;
+
+ static final byte ATTRIBSMASK_SYNC = 0x0C;
+ static final byte ATTRIBMASK_TRANS = 0x03;
+
+ public UsbACAudioControlEndpoint(int length, byte type, byte subclass) {
+ super(length, type, subclass);
+ }
+
+ public byte getAddress() {
+ return mAddress;
+ }
+
+ public byte getAttribs() {
+ return mAttribs;
+ }
+
+ public int getMaxPacketSize() {
+ return mMaxPacketSize;
+ }
+
+ public byte getInterval() {
+ return mInterval;
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ super.parseRawDescriptors(stream);
+
+ mAddress = stream.getByte();
+ mAttribs = stream.getByte();
+ mMaxPacketSize = stream.unpackUsbWord();
+ mInterval = stream.getByte();
+
+ return mLength;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACAudioStreamEndpoint.java b/services/usb/java/com/android/server/usb/descriptors/UsbACAudioStreamEndpoint.java
new file mode 100644
index 0000000..d387883
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACAudioStreamEndpoint.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2017 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;
+
+/**
+ * @hide
+ * An audio class-specific Streaming Endpoint
+ * see audio10.pdf section 3.7.2
+ */
+public class UsbACAudioStreamEndpoint extends UsbACEndpoint {
+ private static final String TAG = "ACAudioStreamEndpoint";
+
+ //TODO data fields...
+ public UsbACAudioStreamEndpoint(int length, byte type, byte subclass) {
+ super(length, type, subclass);
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ super.parseRawDescriptors(stream);
+
+ //TODO Read fields
+ stream.advance(mLength - stream.getReadCount());
+ return mLength;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACEndpoint.java b/services/usb/java/com/android/server/usb/descriptors/UsbACEndpoint.java
new file mode 100644
index 0000000..223496ab
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACEndpoint.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2017 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
+ * An audio class-specific Endpoint
+ * see audio10.pdf section 4.4.1.2
+ */
+abstract class UsbACEndpoint extends UsbDescriptor {
+ private static final String TAG = "ACEndpoint";
+
+ protected final byte mSubclass; // from the mSubclass member of the "enclosing"
+ // Interface Descriptor, not the stream.
+ protected byte mSubtype; // 2:1 HEADER descriptor subtype
+
+ UsbACEndpoint(int length, byte type, byte subclass) {
+ super(length, type);
+ mSubclass = subclass;
+ }
+
+ public byte getSubclass() {
+ return mSubclass;
+ }
+
+ public byte getSubtype() {
+ return mSubtype;
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ mSubtype = stream.getByte();
+
+ return mLength;
+ }
+
+ public static UsbDescriptor allocDescriptor(UsbDescriptorParser parser,
+ int length, byte type) {
+ UsbInterfaceDescriptor interfaceDesc = parser.getCurInterface();
+ byte subClass = interfaceDesc.getUsbSubclass();
+ switch (subClass) {
+ case AUDIO_AUDIOCONTROL:
+ return new UsbACAudioControlEndpoint(length, type, subClass);
+
+ case AUDIO_AUDIOSTREAMING:
+ return new UsbACAudioStreamEndpoint(length, type, subClass);
+
+ case AUDIO_MIDISTREAMING:
+ return new UsbACMidiEndpoint(length, type, subClass);
+
+ default:
+ Log.w(TAG, "Unknown Audio Class Endpoint id:0x" + Integer.toHexString(subClass));
+ return null;
+ }
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACFeatureUnit.java b/services/usb/java/com/android/server/usb/descriptors/UsbACFeatureUnit.java
new file mode 100644
index 0000000..739fe55
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACFeatureUnit.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2017 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;
+
+/**
+ * @hide
+ * An audio class-specific Feature Unit Interface
+ * see audio10.pdf section 3.5.5
+ */
+public class UsbACFeatureUnit extends UsbACInterface {
+ private static final String TAG = "ACFeatureUnit";
+
+ // audio10.pdf section 4.3.2.5
+ public static final int CONTROL_MASK_MUTE = 0x0001;
+ public static final int CONTROL_MASK_VOL = 0x0002;
+ public static final int CONTROL_MASK_BASS = 0x0004;
+ public static final int CONTROL_MASK_MID = 0x0008;
+ public static final int CONTROL_MASK_TREB = 0x0010;
+ public static final int CONTROL_MASK_EQ = 0x0020;
+ public static final int CONTROL_MASK_AGC = 0x0040;
+ public static final int CONTROL_MASK_DELAY = 0x0080;
+ public static final int CONTROL_MASK_BOOST = 0x0100; // BASS boost
+ public static final int CONTROL_MASK_LOUD = 0x0200; // LOUDNESS
+
+ private int mNumChannels;
+
+ private byte mUnitID; // 3:1 Constant uniquely identifying the Unit within the audio function.
+ // This value is used in all requests to address this Unit
+ private byte mSourceID; // 4:1 ID of the Unit or Terminal to which this Feature Unit
+ // is connected.
+ private byte mControlSize; // 5:1 Size in bytes of an element of the mControls array: n
+ private int[] mControls; // 6:? bitmask (see above) of supported controls in a given
+ // logical channel
+ private byte mUnitName; // ?:1 Index of a string descriptor, describing this Feature Unit.
+
+ public UsbACFeatureUnit(int length, byte type, byte subtype, byte subClass) {
+ super(length, type, subtype, subClass);
+ }
+
+ public int getNumChannels() {
+ return mNumChannels;
+ }
+
+ public byte getUnitID() {
+ return mUnitID;
+ }
+
+ public byte getSourceID() {
+ return mSourceID;
+ }
+
+ public byte getControlSize() {
+ return mControlSize;
+ }
+
+ public int[] getControls() {
+ return mControls;
+ }
+
+ public byte getUnitName() {
+ return mUnitName;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACHeader.java b/services/usb/java/com/android/server/usb/descriptors/UsbACHeader.java
new file mode 100644
index 0000000..e31438c
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACHeader.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2017 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;
+
+/**
+ * @hide
+ * An audio class-specific Interface Header.
+ * see audio10.pdf section 4.3.2
+ */
+public class UsbACHeader extends UsbACInterface {
+ private static final String TAG = "ACHeader";
+
+ private int mADCRelease; // 3:2 Audio Device Class Specification Release (BCD).
+ private int mTotalLength; // 5:2 Total number of bytes returned for the class-specific
+ // AudioControl interface descriptor. Includes the combined length
+ // of this descriptor header and all Unit and Terminal descriptors.
+ private byte mNumInterfaces = 0; // 7:1 The number of AudioStreaming and MIDIStreaming
+ // interfaces in the Audio Interface Collection to which this
+ // AudioControl interface belongs: n
+ private byte[] mInterfaceNums = null; // 8:n List of Audio/MIDI streaming interface
+ // numbers associate with this endpoint
+ private byte mControls; // Vers 2.0 thing
+
+ public UsbACHeader(int length, byte type, byte subtype, byte subclass) {
+ super(length, type, subtype, subclass);
+ }
+
+ public int getADCRelease() {
+ return mADCRelease;
+ }
+
+ public int getTotalLength() {
+ return mTotalLength;
+ }
+
+ public byte getNumInterfaces() {
+ return mNumInterfaces;
+ }
+
+ public byte[] getInterfaceNums() {
+ return mInterfaceNums;
+ }
+
+ public byte getControls() {
+ return mControls;
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ mADCRelease = stream.unpackUsbWord();
+
+ mTotalLength = stream.unpackUsbWord();
+ if (mADCRelease >= 0x200) {
+ mControls = stream.getByte();
+ } else {
+ mNumInterfaces = stream.getByte();
+ mInterfaceNums = new byte[mNumInterfaces];
+ for (int index = 0; index < mNumInterfaces; index++) {
+ mInterfaceNums[index] = stream.getByte();
+ }
+ }
+
+ return mLength;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACInputTerminal.java b/services/usb/java/com/android/server/usb/descriptors/UsbACInputTerminal.java
new file mode 100644
index 0000000..653a7de
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACInputTerminal.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 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;
+
+/**
+ * @hide
+ * An audio class-specific Input Terminal interface.
+ * see audio10.pdf section 4.3.2.1
+ */
+public class UsbACInputTerminal extends UsbACTerminal {
+ private static final String TAG = "ACInputTerminal";
+
+ private byte mNrChannels; // 7:1 1 Channel (0x01)
+ // Number of logical output channels in the
+ // Terminal’s output audio channel cluster
+ private int mChannelConfig; // 8:2 Mono (0x0000)
+ private byte mChannelNames; // 10:1 Unused (0x00)
+ private byte mTerminal; // 11:1 Unused (0x00)
+
+ public UsbACInputTerminal(int length, byte type, byte subtype, byte subclass) {
+ super(length, type, subtype, subclass);
+ }
+
+ public byte getNrChannels() {
+ return mNrChannels;
+ }
+
+ public int getChannelConfig() {
+ return mChannelConfig;
+ }
+
+ public byte getChannelNames() {
+ return mChannelNames;
+ }
+
+ public byte getTerminal() {
+ return mTerminal;
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ super.parseRawDescriptors(stream);
+
+ mNrChannels = stream.getByte();
+ mChannelConfig = stream.unpackUsbWord();
+ mChannelNames = stream.getByte();
+ mTerminal = stream.getByte();
+
+ return mLength;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACInterface.java b/services/usb/java/com/android/server/usb/descriptors/UsbACInterface.java
new file mode 100644
index 0000000..0ab7fcc
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACInterface.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2017 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
+ * An audio class-specific Interface.
+ * see audio10.pdf section 4.3.2
+ */
+public abstract class UsbACInterface extends UsbDescriptor {
+ private static final String TAG = "ACInterface";
+
+ // Audio Control Subtypes
+ public static final byte ACI_UNDEFINED = 0;
+ public static final byte ACI_HEADER = 1;
+ public static final byte ACI_INPUT_TERMINAL = 2;
+ public static final byte ACI_OUTPUT_TERMINAL = 3;
+ public static final byte ACI_MIXER_UNIT = 4;
+ public static final byte ACI_SELECTOR_UNIT = 5;
+ public static final byte ACI_FEATURE_UNIT = 6;
+ public static final byte ACI_PROCESSING_UNIT = 7;
+ public static final byte ACI_EXTENSION_UNIT = 8;
+
+ // Audio Streaming Subtypes
+ public static final byte ASI_UNDEFINED = 0;
+ public static final byte ASI_GENERAL = 1;
+ public static final byte ASI_FORMAT_TYPE = 2;
+ public static final byte ASI_FORMAT_SPECIFIC = 3;
+
+ // MIDI Streaming Subtypes
+ public static final byte MSI_UNDEFINED = 0;
+ public static final byte MSI_HEADER = 1;
+ public static final byte MSI_IN_JACK = 2;
+ public static final byte MSI_OUT_JACK = 3;
+ public static final byte MSI_ELEMENT = 4;
+
+ // Sample format IDs (encodings)
+ // FORMAT_I
+ public static final int FORMAT_I_UNDEFINED = 0x0000;
+ public static final int FORMAT_I_PCM = 0x0001;
+ public static final int FORMAT_I_PCM8 = 0x0002;
+ public static final int FORMAT_I_IEEE_FLOAT = 0x0003;
+ public static final int FORMAT_I_ALAW = 0x0004;
+ public static final int FORMAT_I_MULAW = 0x0005;
+ // FORMAT_II
+ public static final int FORMAT_II_UNDEFINED = 0x1000;
+ public static final int FORMAT_II_MPEG = 0x1001;
+ public static final int FORMAT_II_AC3 = 0x1002;
+ // FORMAT_III
+ public static final int FORMAT_III_UNDEFINED = 0x2000;
+ public static final int FORMAT_III_IEC1937AC3 = 0x2001;
+ public static final int FORMAT_III_IEC1937_MPEG1_Layer1 = 0x2002;
+ public static final int FORMAT_III_IEC1937_MPEG1_Layer2 = 0x2003;
+ public static final int FORMAT_III_IEC1937_MPEG2_EXT = 0x2004;
+ public static final int FORMAT_III_IEC1937_MPEG2_Layer1LS = 0x2005;
+
+ protected final byte mSubtype; // 2:1 HEADER descriptor subtype
+ protected final byte mSubclass; // from the mSubclass member of the
+ // "enclosing" Interface Descriptor
+
+ public UsbACInterface(int length, byte type, byte subtype, byte subclass) {
+ super(length, type);
+ mSubtype = subtype;
+ mSubclass = subclass;
+ }
+
+ public byte getSubtype() {
+ return mSubtype;
+ }
+
+ public byte getSubclass() {
+ return mSubclass;
+ }
+
+ private static UsbDescriptor allocAudioControlDescriptor(ByteStream stream,
+ int length, byte type, byte subtype, byte subClass) {
+ switch (subtype) {
+ case ACI_HEADER:
+ return new UsbACHeader(length, type, subtype, subClass);
+
+ case ACI_INPUT_TERMINAL:
+ return new UsbACInputTerminal(length, type, subtype, subClass);
+
+ case ACI_OUTPUT_TERMINAL:
+ return new UsbACOutputTerminal(length, type, subtype, subClass);
+
+ case ACI_SELECTOR_UNIT:
+ return new UsbACSelectorUnit(length, type, subtype, subClass);
+
+ case ACI_FEATURE_UNIT:
+ return new UsbACFeatureUnit(length, type, subtype, subClass);
+
+ case ACI_MIXER_UNIT:
+ return new UsbACMixerUnit(length, type, subtype, subClass);
+
+ case ACI_PROCESSING_UNIT:
+ case ACI_EXTENSION_UNIT:
+ case ACI_UNDEFINED:
+ // break; Fall through until we implement this descriptor
+ default:
+ Log.w(TAG, "Unknown Audio Class Interface subtype:0x"
+ + Integer.toHexString(subtype));
+ return null;
+ }
+ }
+
+ private static UsbDescriptor allocAudioStreamingDescriptor(ByteStream stream,
+ int length, byte type, byte subtype, byte subClass) {
+ switch (subtype) {
+ case ASI_GENERAL:
+ return new UsbASGeneral(length, type, subtype, subClass);
+
+ case ASI_FORMAT_TYPE:
+ return UsbASFormat.allocDescriptor(stream, length, type, subtype, subClass);
+
+ case ASI_FORMAT_SPECIFIC:
+ case ASI_UNDEFINED:
+ // break; Fall through until we implement this descriptor
+ default:
+ Log.w(TAG, "Unknown Audio Streaming Interface subtype:0x"
+ + Integer.toHexString(subtype));
+ return null;
+ }
+ }
+
+ private static UsbDescriptor allocMidiStreamingDescriptor(int length, byte type,
+ byte subtype, byte subClass) {
+ switch (subtype) {
+ case MSI_HEADER:
+ return new UsbMSMidiHeader(length, type, subtype, subClass);
+
+ case MSI_IN_JACK:
+ return new UsbMSMidiInputJack(length, type, subtype, subClass);
+
+ case MSI_OUT_JACK:
+ return new UsbMSMidiOutputJack(length, type, subtype, subClass);
+
+ case MSI_ELEMENT:
+ // break;
+ // Fall through until we implement that descriptor
+
+ case MSI_UNDEFINED:
+ // break; Fall through until we implement this descriptor
+ default:
+ Log.w(TAG, "Unknown MIDI Streaming Interface subtype:0x"
+ + Integer.toHexString(subtype));
+ return null;
+ }
+ }
+
+ /**
+ * 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();
+ byte subClass = interfaceDesc.getUsbSubclass();
+ switch (subClass) {
+ case AUDIO_AUDIOCONTROL:
+ return allocAudioControlDescriptor(stream, length, type, subtype, subClass);
+
+ case AUDIO_AUDIOSTREAMING:
+ return allocAudioStreamingDescriptor(stream, length, type, subtype, subClass);
+
+ case AUDIO_MIDISTREAMING:
+ return allocMidiStreamingDescriptor(length, type, subtype, subClass);
+
+ default:
+ Log.w(TAG, "Unknown Audio Class Interface Subclass: 0x"
+ + Integer.toHexString(subClass));
+ return null;
+ }
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACMidiEndpoint.java b/services/usb/java/com/android/server/usb/descriptors/UsbACMidiEndpoint.java
new file mode 100644
index 0000000..9c07242
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACMidiEndpoint.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2017 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;
+
+/**
+ * @hide
+ * An audio class-specific Midi Endpoint.
+ * see midi10.pdf section 6.2.2
+ */
+public class UsbACMidiEndpoint extends UsbACEndpoint {
+ private static final String TAG = "ACMidiEndpoint";
+
+ private byte mNumJacks;
+ private byte[] mJackIds;
+
+ public UsbACMidiEndpoint(int length, byte type, byte subclass) {
+ super(length, type, subclass);
+ }
+
+ public byte getNumJacks() {
+ return mNumJacks;
+ }
+
+ public byte[] getJackIds() {
+ return mJackIds;
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ super.parseRawDescriptors(stream);
+
+ mNumJacks = stream.getByte();
+ mJackIds = new byte[mNumJacks];
+ for (int jack = 0; jack < mNumJacks; jack++) {
+ mJackIds[jack] = stream.getByte();
+ }
+ return mLength;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACMixerUnit.java b/services/usb/java/com/android/server/usb/descriptors/UsbACMixerUnit.java
new file mode 100644
index 0000000..552b5ae
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACMixerUnit.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2017 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;
+
+/**
+ * @hide
+ * An audio class-specific Mixer Interface.
+ * see audio10.pdf section 4.3.2.3
+ */
+public class UsbACMixerUnit extends UsbACInterface {
+ private static final String TAG = "ACMixerUnit";
+
+ private byte mUnitID; // 3:1
+ private byte mNumInputs; // 4:1 Number of Input Pins of this Unit.
+ private byte[] mInputIDs; // 5...:1 ID of the Unit or Terminal to which the Input Pins
+ // are connected.
+ private byte mNumOutputs; // The number of output channels
+ private int mChannelConfig; // Spacial location of output channels
+ private byte mChanNameID; // First channel name string descriptor ID
+ private byte[] mControls; // bitmasks of which controls are present for each channel
+ private byte mNameID; // string descriptor ID of mixer name
+
+ public UsbACMixerUnit(int length, byte type, byte subtype, byte subClass) {
+ super(length, type, subtype, subClass);
+ }
+
+ public byte getUnitID() {
+ return mUnitID;
+ }
+
+ public byte getNumInputs() {
+ return mNumInputs;
+ }
+
+ public byte[] getInputIDs() {
+ return mInputIDs;
+ }
+
+ public byte getNumOutputs() {
+ return mNumOutputs;
+ }
+
+ public int getChannelConfig() {
+ return mChannelConfig;
+ }
+
+ public byte getChanNameID() {
+ return mChanNameID;
+ }
+
+ public byte[] getControls() {
+ return mControls;
+ }
+
+ public byte getNameID() {
+ return mNameID;
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ mUnitID = stream.getByte();
+ mNumInputs = stream.getByte();
+ mInputIDs = new byte[mNumInputs];
+ for (int input = 0; input < mNumInputs; input++) {
+ mInputIDs[input] = stream.getByte();
+ }
+ mNumOutputs = stream.getByte();
+ mChannelConfig = stream.unpackUsbWord();
+ mChanNameID = stream.getByte();
+
+ int controlArraySize;
+ int totalChannels = mNumInputs * mNumOutputs;
+ if (totalChannels % 8 == 0) {
+ controlArraySize = totalChannels / 8;
+ } else {
+ controlArraySize = totalChannels / 8 + 1;
+ }
+ mControls = new byte[controlArraySize];
+ for (int index = 0; index < controlArraySize; index++) {
+ mControls[index] = stream.getByte();
+ }
+
+ mNameID = stream.getByte();
+
+ return mLength;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACOutputTerminal.java b/services/usb/java/com/android/server/usb/descriptors/UsbACOutputTerminal.java
new file mode 100644
index 0000000..f957e3d
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACOutputTerminal.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2017 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;
+
+/**
+ * @hide
+ * An audio class-specific Output Terminal Interface.
+ * see audio10.pdf section 4.3.2.2
+ */
+public class UsbACOutputTerminal extends UsbACTerminal {
+ private static final String TAG = "ACOutputTerminal";
+
+ private byte mSourceID; // 7:1 From Input Terminal. (0x01)
+ private byte mTerminal; // 8:1 Unused.
+
+ public UsbACOutputTerminal(int length, byte type, byte subtype, byte subClass) {
+ super(length, type, subtype, subClass);
+ }
+
+ public byte getSourceID() {
+ return mSourceID;
+ }
+
+ public byte getTerminal() {
+ return mTerminal;
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ super.parseRawDescriptors(stream);
+
+ mSourceID = stream.getByte();
+ mTerminal = stream.getByte();
+ return mLength;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACSelectorUnit.java b/services/usb/java/com/android/server/usb/descriptors/UsbACSelectorUnit.java
new file mode 100644
index 0000000..b1f60bd
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACSelectorUnit.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2017 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;
+
+/**
+ * @hide
+ * An audio class-specific Selector Unit Interface.
+ * see audio10.pdf section 4.3.2.4
+ */
+public class UsbACSelectorUnit extends UsbACInterface {
+ private static final String TAG = "ACSelectorUnit";
+
+ private byte mUnitID; // 3:1 Constant uniquely identifying the Unit within the audio function.
+ // This value is used in all requests to address this Unit.
+ private byte mNumPins; // 4:1 Number of input pins in this unit
+ private byte[] mSourceIDs; // 5+mNumPins:1 ID of the Unit or Terminal to which the first
+ // Input Pin of this Selector Unit is connected.
+ private byte mNameIndex; // Index of a string descriptor, describing the Selector Unit.
+
+ public UsbACSelectorUnit(int length, byte type, byte subtype, byte subClass) {
+ super(length, type, subtype, subClass);
+ }
+
+ public byte getUnitID() {
+ return mUnitID;
+ }
+
+ public byte getNumPins() {
+ return mNumPins;
+ }
+
+ public byte[] getSourceIDs() {
+ return mSourceIDs;
+ }
+
+ public byte getNameIndex() {
+ return mNameIndex;
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ mUnitID = stream.getByte();
+ mNumPins = stream.getByte();
+ mSourceIDs = new byte[mNumPins];
+ for (int index = 0; index < mNumPins; index++) {
+ mSourceIDs[index] = stream.getByte();
+ }
+ mNameIndex = stream.getByte();
+
+ return mLength;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACTerminal.java b/services/usb/java/com/android/server/usb/descriptors/UsbACTerminal.java
new file mode 100644
index 0000000..ea80208
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbACTerminal.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 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;
+
+/**
+ * @hide
+ */
+public abstract class UsbACTerminal extends UsbACInterface {
+ // Note that these fields are the same for both the
+ // audio class-specific Output Terminal Interface.(audio10.pdf section 4.3.2.2)
+ // and audio class-specific Input Terminal interface.(audio10.pdf section 4.3.2.1)
+ // so we may as well unify the parsing here.
+ protected byte mTerminalID; // 3:1 ID of this Output Terminal. (0x02)
+ protected int mTerminalType; // 4:2 USB Streaming. (0x0101)
+ protected byte mAssocTerminal; // 6:1 Unused (0x00)
+
+ public UsbACTerminal(int length, byte type, byte subtype, byte subclass) {
+ super(length, type, subtype, subclass);
+ }
+
+ public byte getTerminalID() {
+ return mTerminalID;
+ }
+
+ public int getTerminalType() {
+ return mTerminalType;
+ }
+
+ public byte getAssocTerminal() {
+ return mAssocTerminal;
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ mTerminalID = stream.getByte();
+ mTerminalType = stream.unpackUsbWord();
+ mAssocTerminal = stream.getByte();
+
+ return mLength;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbASFormat.java b/services/usb/java/com/android/server/usb/descriptors/UsbASFormat.java
new file mode 100644
index 0000000..d7c84c6
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbASFormat.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 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;
+
+/**
+ * @hide
+ * An audio class-specific Format Interface.
+ * Subclasses: UsbACFormatI and UsbACFormatII.
+ * see audio10.pdf section 4.5.3 & & Frmts10.pdf
+ */
+public abstract class UsbASFormat extends UsbACInterface {
+ private static final String TAG = "ASFormat";
+
+ private final byte mFormatType; // 3:1 FORMAT_TYPE_*
+
+ public static final byte FORMAT_TYPE_I = 1;
+ public static final byte FORMAT_TYPE_II = 2;
+
+ public UsbASFormat(int length, byte type, byte subtype, byte formatType, byte mSubclass) {
+ super(length, type, subtype, mSubclass);
+ mFormatType = formatType;
+ }
+
+ public byte getFormatType() {
+ return mFormatType;
+ }
+
+ /**
+ * Allocates the audio-class format subtype associated with the format type read from the
+ * stream.
+ */
+ public static UsbDescriptor allocDescriptor(ByteStream stream, int length, byte type,
+ byte subtype, byte subclass) {
+
+ byte formatType = stream.getByte();
+ //TODO
+ // There is an issue parsing format descriptors on (some) USB 2.0 pro-audio interfaces
+ // Since we don't need this info for headset detection, just skip these descriptors
+ // for now to avoid the (low) possibility of an IndexOutOfBounds exception.
+ switch (formatType) {
+// case FORMAT_TYPE_I:
+// return new UsbASFormatI(length, type, subtype, formatType, subclass);
+//
+// case FORMAT_TYPE_II:
+// return new UsbASFormatII(length, type, subtype, formatType, subclass);
+
+ default:
+ return null;
+ }
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbASFormatI.java b/services/usb/java/com/android/server/usb/descriptors/UsbASFormatI.java
new file mode 100644
index 0000000..347a6cf
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbASFormatI.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2017 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;
+
+/**
+ * @hide
+ * An audio class-specific Format I interface.
+ * see Frmts10.pdf section 2.2
+ */
+public class UsbASFormatI extends UsbASFormat {
+ private static final String TAG = "ASFormatI";
+
+ private byte mNumChannels; // 4:1
+ private byte mSubframeSize; // 5:1 frame size in bytes
+ private byte mBitResolution; // 6:1 sample size in bits
+ private byte mSampleFreqType; // 7:1
+ private int[] mSampleRates; // if mSamFreqType == 0, there will be 2 values: the
+ // min & max rates otherwise mSamFreqType rates.
+ // All 3-byte values. All rates in Hz
+
+ public UsbASFormatI(int length, byte type, byte subtype, byte formatType, byte subclass) {
+ super(length, type, subtype, formatType, subclass);
+ }
+
+ public byte getNumChannels() {
+ return mNumChannels;
+ }
+
+ public byte getSubframeSize() {
+ return mSubframeSize;
+ }
+
+ public byte getBitResolution() {
+ return mBitResolution;
+ }
+
+ public byte getSampleFreqType() {
+ return mSampleFreqType;
+ }
+
+ public int[] getSampleRates() {
+ return mSampleRates;
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ mNumChannels = stream.getByte();
+ mSubframeSize = stream.getByte();
+ mBitResolution = stream.getByte();
+ mSampleFreqType = stream.getByte();
+ if (mSampleFreqType == 0) {
+ mSampleRates = new int[2];
+ mSampleRates[0] = stream.unpackUsbTriple();
+ mSampleRates[1] = stream.unpackUsbTriple();
+ } else {
+ mSampleRates = new int[mSampleFreqType];
+ for (int index = 0; index < mSampleFreqType; index++) {
+ mSampleRates[index] = stream.unpackUsbTriple();
+ }
+ }
+
+ return mLength;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbASFormatII.java b/services/usb/java/com/android/server/usb/descriptors/UsbASFormatII.java
new file mode 100644
index 0000000..abdc621
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbASFormatII.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2017 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;
+
+/**
+ * @hide
+ * An audio class-specific Format II interface.
+ * see Frmts10.pdf section 2.3
+ */
+public class UsbASFormatII extends UsbASFormat {
+ private static final String TAG = "ASFormatII";
+
+ private int mMaxBitRate; // 4:2 Indicates the maximum number of bits per second this
+ // interface can handle. Expressed in kbits/s.
+ private int mSamplesPerFrame; // 6:2 Indicates the number of PCM audio samples contained
+ // in one encoded audio frame.
+ private byte mSamFreqType; // Indicates how the sampling frequency can be programmed:
+ // 0: Continuous sampling frequency
+ // 1..255: The number of discrete sampling frequencies supported
+ // by the isochronous data endpoint of the AudioStreaming
+ // interface (ns)
+ private int[] mSampleRates; // if mSamFreqType == 0, there will be 2 values:
+ // the min & max rates. otherwise mSamFreqType rates.
+ // All 3-byte values. All rates in Hz
+
+ public UsbASFormatII(int length, byte type, byte subtype, byte formatType, byte subclass) {
+ super(length, type, subtype, formatType, subclass);
+ }
+
+ public int getMaxBitRate() {
+ return mMaxBitRate;
+ }
+
+ public int getSamplesPerFrame() {
+ return mSamplesPerFrame;
+ }
+
+ public byte getSamFreqType() {
+ return mSamFreqType;
+ }
+
+ public int[] getSampleRates() {
+ return mSampleRates;
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ mMaxBitRate = stream.unpackUsbWord();
+ mSamplesPerFrame = stream.unpackUsbWord();
+ mSamFreqType = stream.getByte();
+ int numFreqs = mSamFreqType == 0 ? 2 : mSamFreqType;
+ mSampleRates = new int[numFreqs];
+ for (int index = 0; index < numFreqs; index++) {
+ mSampleRates[index] = stream.unpackUsbTriple();
+ }
+
+ return mLength;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbASGeneral.java b/services/usb/java/com/android/server/usb/descriptors/UsbASGeneral.java
new file mode 100644
index 0000000..c4f42d3
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbASGeneral.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2017 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;
+
+/**
+ * @hide
+ * An audio class-specific General interface.
+ * see audio10.pdf section 4.5.2
+ */
+public class UsbASGeneral extends UsbACInterface {
+ private static final String TAG = "ACGeneral";
+
+ // audio10.pdf - section 4.5.2
+ private byte mTerminalLink; // 3:1 The Terminal ID of the Terminal to which the endpoint
+ // of this interface is connected.
+ private byte mDelay; // 4:1 Delay introduced by the data path (see Section 3.4,
+ // “Inter Channel Synchronization”). Expressed in number of frames.
+ private int mFormatTag; // 5:2 The Audio Data Format that has to be used to communicate
+ // with this interface.
+
+ public UsbASGeneral(int length, byte type, byte subtype, byte subclass) {
+ super(length, type, subtype, subclass);
+ }
+
+ public byte getTerminalLink() {
+ return mTerminalLink;
+ }
+
+ public byte getDelay() {
+ return mDelay;
+ }
+
+ public int getFormatTag() {
+ return mFormatTag;
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ mTerminalLink = stream.getByte();
+ mDelay = stream.getByte();
+ mFormatTag = stream.unpackUsbWord();
+
+ return mLength;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbBinaryParser.java b/services/usb/java/com/android/server/usb/descriptors/UsbBinaryParser.java
new file mode 100644
index 0000000..185cee2
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbBinaryParser.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2017 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.hardware.usb.UsbDeviceConnection;
+import android.util.Log;
+
+import com.android.server.usb.descriptors.report.UsbStrings;
+
+/**
+ * @hide
+ * A class that just walks the descriptors and does a hex dump of the contained values.
+ * Usefull as a debugging tool.
+ */
+public class UsbBinaryParser {
+ private static final String TAG = "UsbBinaryParser";
+ private static final boolean LOGGING = false;
+
+ private void dumpDescriptor(ByteStream stream, int length, byte type, StringBuilder builder) {
+
+ // Log
+ if (LOGGING) {
+ Log.i(TAG, "l:" + length + " t:" + Integer.toHexString(type) + " "
+ + UsbStrings.getDescriptorName(type));
+ StringBuilder sb = new StringBuilder();
+ for (int index = 2; index < length; index++) {
+ sb.append("0x" + Integer.toHexString(stream.getByte() & 0xFF) + " ");
+ }
+ Log.i(TAG, sb.toString());
+ } else {
+ // Screen Dump
+ builder.append("<p>");
+ builder.append("<b> l:" + length
+ + " t:0x" + Integer.toHexString(type) + " "
+ + UsbStrings.getDescriptorName(type) + "</b><br>");
+ for (int index = 2; index < length; index++) {
+ builder.append("0x" + Integer.toHexString(stream.getByte() & 0xFF) + " ");
+ }
+ builder.append("</p>");
+ }
+ }
+
+ /**
+ * Walk through descriptor stream and generate an HTML text report of the contents.
+ * TODO: This should be done in the model of UsbDescriptorsParser/Reporter model.
+ */
+ public void parseDescriptors(UsbDeviceConnection connection, byte[] descriptors,
+ StringBuilder builder) {
+
+ builder.append("<tt>");
+ ByteStream stream = new ByteStream(descriptors);
+ while (stream.available() > 0) {
+ int length = (int) stream.getByte() & 0x000000FF;
+ byte type = stream.getByte();
+ dumpDescriptor(stream, length, type, builder);
+ }
+ builder.append("</tt>");
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java
new file mode 100644
index 0000000..8ae6d0f
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2017 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;
+
+/**
+ * @hide
+ * An USB Config Descriptor.
+ * see usb11.pdf section 9.6.2
+ */
+public class UsbConfigDescriptor extends UsbDescriptor {
+ private static final String TAG = "Config";
+
+ private int mTotalLength; // 2:2 Total length in bytes of data returned
+ private byte mNumInterfaces; // 4:1 Number of Interfaces
+ private byte mConfigValue; // 5:1 Value to use as an argument to select this configuration
+ private byte mConfigIndex; // 6:1 Index of String Descriptor describing this configuration
+ private byte mAttribs; // 7:1 D7 Reserved, set to 1. (USB 1.0 Bus Powered)
+ // D6 Self Powered
+ // D5 Remote Wakeup
+ // D4..0 Reserved, set to 0.
+ private byte mMaxPower; // 8:1 Maximum Power Consumption in 2mA units
+
+ UsbConfigDescriptor(int length, byte type) {
+ super(length, type);
+ }
+
+ public int getTotalLength() {
+ return mTotalLength;
+ }
+
+ public byte getNumInterfaces() {
+ return mNumInterfaces;
+ }
+
+ public byte getConfigValue() {
+ return mConfigValue;
+ }
+
+ public byte getConfigIndex() {
+ return mConfigIndex;
+ }
+
+ public byte getAttribs() {
+ return mAttribs;
+ }
+
+ public byte getMaxPower() {
+ return mMaxPower;
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ mTotalLength = stream.unpackUsbWord();
+ mNumInterfaces = stream.getByte();
+ mConfigValue = stream.getByte();
+ mConfigIndex = stream.getByte();
+ mAttribs = stream.getByte();
+ mMaxPower = stream.getByte();
+
+ return mLength;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptor.java
new file mode 100644
index 0000000..63b2d7f
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptor.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2017 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.hardware.usb.UsbConstants;
+import android.hardware.usb.UsbDeviceConnection;
+import android.util.Log;
+
+/*
+ * Some notes about UsbDescriptor and its subclasses.
+ *
+ * It is assumed that the user of the UsbDescriptorParser knows what they are doing
+ * so NO PROTECTION is implemented against "improper" use. Such uses are specifically:
+ * allocating a UsbDescriptor (subclass) object outside of the context of parsing/reading
+ * a rawdescriptor stream and perhaps accessing fields which have not been inialized (by
+ * parsing/reading or course).
+ */
+
+/**
+ * @hide
+ * Common superclass for all USB Descriptors.
+ */
+public abstract class UsbDescriptor {
+ private static final String TAG = "Descriptor";
+
+ 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.
+ protected final byte mType; // 1:1 bDescriptorType Constant Device Descriptor (0x01)
+
+ private byte[] mRawData;
+
+ private static final int SIZE_STRINGBUFFER = 256;
+ 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;
+ private int mStatus = STATUS_UNPARSED;
+
+ private static String[] sStatusStrings = {
+ "UNPARSED", "PARSED - OK", "PARSED - UNDERRUN", "PARSED - OVERRUN"};
+
+ // Descriptor Type IDs
+ public static final byte DESCRIPTORTYPE_DEVICE = 0x01; // 1
+ public static final byte DESCRIPTORTYPE_CONFIG = 0x02; // 2
+ public static final byte DESCRIPTORTYPE_STRING = 0x03; // 3
+ public static final byte DESCRIPTORTYPE_INTERFACE = 0x04; // 4
+ public static final byte DESCRIPTORTYPE_ENDPOINT = 0x05; // 5
+ public static final byte DESCRIPTORTYPE_INTERFACEASSOC = 0x0B; // 11
+ public static final byte DESCRIPTORTYPE_BOS = 0x0F; // 15
+ public static final byte DESCRIPTORTYPE_CAPABILITY = 0x10; // 16
+
+ 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_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 byte CLASSID_DEVICE = 0x00;
+ public static final byte CLASSID_AUDIO = 0x01;
+ public static final byte CLASSID_COM = 0x02;
+ public static final byte CLASSID_HID = 0x03;
+ // public static final byte CLASSID_??? = 0x04;
+ public static final byte CLASSID_PHYSICAL = 0x05;
+ public static final byte CLASSID_IMAGE = 0x06;
+ public static final byte CLASSID_PRINTER = 0x07;
+ public static final byte CLASSID_STORAGE = 0x08;
+ public static final byte CLASSID_HUB = 0x09;
+ public static final byte CLASSID_CDC_CONTROL = 0x0A;
+ public static final byte CLASSID_SMART_CARD = 0x0B;
+ //public static final byte CLASSID_??? = 0x0C;
+ public static final byte CLASSID_SECURITY = 0x0D;
+ public static final byte CLASSID_VIDEO = 0x0E;
+ public static final byte CLASSID_HEALTHCARE = 0x0F;
+ public static final byte CLASSID_AUDIOVIDEO = 0x10;
+ public static final byte CLASSID_BILLBOARD = 0x11;
+ public static final byte CLASSID_TYPECBRIDGE = 0x12;
+ public static final byte CLASSID_DIAGNOSTIC = (byte) 0xDC;
+ public static final byte CLASSID_WIRELESS = (byte) 0xE0;
+ public static final byte CLASSID_MISC = (byte) 0xEF;
+ public static final byte CLASSID_APPSPECIFIC = (byte) 0xFE;
+ public static final byte CLASSID_VENDSPECIFIC = (byte) 0xFF;
+
+ // Audio Subclass codes
+ public static final byte AUDIO_SUBCLASS_UNDEFINED = 0x00;
+ public static final byte AUDIO_AUDIOCONTROL = 0x01;
+ public static final byte AUDIO_AUDIOSTREAMING = 0x02;
+ public static final byte 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;
+
+ /**
+ * @throws IllegalArgumentException
+ */
+ UsbDescriptor(int length, byte type) {
+ // a descriptor has at least a length byte and type byte
+ // one could imagine an empty one otherwise
+ if (length < 2) {
+ // huh?
+ throw new IllegalArgumentException();
+ }
+
+ mLength = length;
+ mType = type;
+ }
+
+ public int getLength() {
+ return mLength;
+ }
+
+ public byte getType() {
+ return mType;
+ }
+
+ public int getStatus() {
+ return mStatus;
+ }
+
+ public void setStatus(int status) {
+ mStatus = status;
+ }
+
+ public String getStatusString() {
+ return sStatusStrings[mStatus];
+ }
+
+ public byte[] getRawData() {
+ return mRawData;
+ }
+
+ /**
+ * Called by the parser for any necessary cleanup.
+ */
+ public void postParse(ByteStream stream) {
+ // Status
+ int bytesRead = stream.getReadCount();
+ if (bytesRead < mLength) {
+ // Too cold...
+ stream.advance(mLength - bytesRead);
+ mStatus = STATUS_PARSED_UNDERRUN;
+ Log.w(TAG, "UNDERRUN t:0x" + Integer.toHexString(mType)
+ + " r:" + bytesRead + " < l:" + mLength);
+ } else if (bytesRead > mLength) {
+ // Too hot...
+ stream.reverse(bytesRead - mLength);
+ mStatus = STATUS_PARSED_OVERRUN;
+ Log.w(TAG, "OVERRRUN t:0x" + Integer.toHexString(mType)
+ + " r:" + bytesRead + " > l:" + mLength);
+ } else {
+ // Just right!
+ mStatus = STATUS_PARSED_OK;
+ }
+ }
+
+ /**
+ * Reads data fields from specified raw-data stream.
+ */
+ public int parseRawDescriptors(ByteStream stream) {
+ int numRead = stream.getReadCount();
+ int dataLen = mLength - numRead;
+ if (dataLen > 0) {
+ mRawData = new byte[dataLen];
+ for (int index = 0; index < dataLen; index++) {
+ mRawData[index] = stream.getByte();
+ }
+ }
+ return mLength;
+ }
+
+ /**
+ * Gets a string for the specified index from the USB Device's string descriptors.
+ */
+ public static String getUsbDescriptorString(UsbDeviceConnection connection, byte strIndex) {
+ String usbStr = "";
+ if (strIndex != 0) {
+ try {
+ int rdo = connection.controlTransfer(
+ UsbConstants.USB_DIR_IN | UsbConstants.USB_TYPE_STANDARD,
+ REQUEST_GET_DESCRIPTOR,
+ (DESCRIPTORTYPE_STRING << 8) | strIndex,
+ 0,
+ sStringBuffer,
+ 0xFF,
+ 0);
+ if (rdo >= 0) {
+ usbStr = new String(sStringBuffer, 2, rdo - 2, "UTF-16LE");
+ } else {
+ usbStr = "?";
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Can not communicate with USB device", e);
+ }
+ }
+ return usbStr;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
new file mode 100644
index 0000000..7c074da
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
@@ -0,0 +1,376 @@
+/*
+ * Copyright (C) 2017 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 java.util.ArrayList;
+
+/**
+ * @hide
+ * Class for parsing a binary stream of USB Descriptors.
+ */
+public class UsbDescriptorParser {
+ private static final String TAG = "DescriptorParser";
+
+ // Descriptor Objects
+ private ArrayList<UsbDescriptor> mDescriptors = new ArrayList<UsbDescriptor>();
+
+ private UsbDeviceDescriptor mDeviceDescriptor;
+ private UsbInterfaceDescriptor mCurInterfaceDescriptor;
+
+ public UsbDescriptorParser() {}
+
+ /**
+ * The probability (as returned by getHeadsetProbability() at which we conclude
+ * the peripheral is a headset.
+ */
+ private static final float IN_HEADSET_TRIGGER = 0.75f;
+ private static final float OUT_HEADSET_TRIGGER = 0.75f;
+
+ private UsbDescriptor allocDescriptor(ByteStream stream) {
+ stream.resetReadCount();
+
+ int length = (int) stream.getByte() & 0x000000FF;
+ byte type = stream.getByte();
+
+ UsbDescriptor descriptor = null;
+ switch (type) {
+ /*
+ * Standard
+ */
+ case UsbDescriptor.DESCRIPTORTYPE_DEVICE:
+ descriptor = mDeviceDescriptor = new UsbDeviceDescriptor(length, type);
+ break;
+
+ case UsbDescriptor.DESCRIPTORTYPE_CONFIG:
+ descriptor = new UsbConfigDescriptor(length, type);
+ break;
+
+ case UsbDescriptor.DESCRIPTORTYPE_INTERFACE:
+ descriptor = mCurInterfaceDescriptor = new UsbInterfaceDescriptor(length, type);
+ break;
+
+ case UsbDescriptor.DESCRIPTORTYPE_ENDPOINT:
+ descriptor = new UsbEndpointDescriptor(length, type);
+ break;
+
+ /*
+ * HID
+ */
+ case UsbDescriptor.DESCRIPTORTYPE_HID:
+ descriptor = new UsbHIDDescriptor(length, type);
+ break;
+
+ /*
+ * Other
+ */
+ case UsbDescriptor.DESCRIPTORTYPE_INTERFACEASSOC:
+ descriptor = new UsbInterfaceAssoc(length, type);
+ break;
+
+ /*
+ * Audio Class Specific
+ */
+ case UsbDescriptor.DESCRIPTORTYPE_AUDIO_INTERFACE:
+ descriptor = UsbACInterface.allocDescriptor(this, stream, length, type);
+ break;
+
+ case UsbDescriptor.DESCRIPTORTYPE_AUDIO_ENDPOINT:
+ descriptor = UsbACEndpoint.allocDescriptor(this, length, type);
+ break;
+
+ default:
+ break;
+ }
+
+ if (descriptor == null) {
+ // Unknown Descriptor
+ Log.i(TAG, "Unknown Descriptor len:" + length + " type:0x"
+ + Integer.toHexString(type));
+ descriptor = new UsbUnknown(length, type);
+ }
+
+ return descriptor;
+ }
+
+ public UsbDeviceDescriptor getDeviceDescriptor() {
+ return mDeviceDescriptor;
+ }
+
+ public UsbInterfaceDescriptor getCurInterface() {
+ return mCurInterfaceDescriptor;
+ }
+
+ /**
+ * @hide
+ */
+ public boolean parseDescriptors(byte[] descriptors) {
+ try {
+ mDescriptors.clear();
+
+ ByteStream stream = new ByteStream(descriptors);
+ while (stream.available() > 0) {
+ UsbDescriptor descriptor = allocDescriptor(stream);
+ if (descriptor != null) {
+ // Parse
+ descriptor.parseRawDescriptors(stream);
+ mDescriptors.add(descriptor);
+
+ // Clean up
+ descriptor.postParse(stream);
+ }
+ }
+ return true;
+ } catch (Exception ex) {
+ Log.e(TAG, "Exception parsing USB descriptors.", ex);
+ }
+ return false;
+ }
+
+ /**
+ * @hide
+ */
+ public boolean parseDevice(String deviceAddr) {
+ byte[] rawDescriptors = getRawDescriptors(deviceAddr);
+ return rawDescriptors != null && parseDescriptors(rawDescriptors);
+ }
+
+ private native byte[] getRawDescriptors(String deviceAddr);
+
+ public int getParsingSpec() {
+ return mDeviceDescriptor != null ? mDeviceDescriptor.getSpec() : 0;
+ }
+
+ public ArrayList<UsbDescriptor> getDescriptors() {
+ return mDescriptors;
+ }
+
+ /**
+ * @hide
+ */
+ public ArrayList<UsbDescriptor> getDescriptors(byte type) {
+ ArrayList<UsbDescriptor> list = new ArrayList<UsbDescriptor>();
+ for (UsbDescriptor descriptor : mDescriptors) {
+ if (descriptor.getType() == type) {
+ list.add(descriptor);
+ }
+ }
+ return list;
+ }
+
+ /**
+ * @hide
+ */
+ public ArrayList<UsbDescriptor> getInterfaceDescriptorsForClass(byte usbClass) {
+ ArrayList<UsbDescriptor> list = new ArrayList<UsbDescriptor>();
+ for (UsbDescriptor descriptor : mDescriptors) {
+ // ensure that this isn't an unrecognized DESCRIPTORTYPE_INTERFACE
+ if (descriptor.getType() == UsbDescriptor.DESCRIPTORTYPE_INTERFACE) {
+ if (descriptor instanceof UsbInterfaceDescriptor) {
+ UsbInterfaceDescriptor intrDesc = (UsbInterfaceDescriptor) descriptor;
+ if (intrDesc.getUsbClass() == usbClass) {
+ list.add(descriptor);
+ }
+ } else {
+ Log.w(TAG, "Unrecognized Interface l:" + descriptor.getLength()
+ + " t:0x" + Integer.toHexString(descriptor.getType()));
+ }
+ }
+ }
+ return list;
+ }
+
+ /**
+ * @hide
+ */
+ public ArrayList<UsbDescriptor> getACInterfaceDescriptors(byte subtype, byte 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 instanceof UsbACInterface) {
+ UsbACInterface acDescriptor = (UsbACInterface) descriptor;
+ if (acDescriptor.getSubtype() == subtype
+ && acDescriptor.getSubclass() == subclass) {
+ list.add(descriptor);
+ }
+ } else {
+ Log.w(TAG, "Unrecognized Audio Interface l:" + descriptor.getLength()
+ + " t:0x" + Integer.toHexString(descriptor.getType()));
+ }
+ }
+ }
+ return list;
+ }
+
+ /**
+ * @hide
+ */
+ public boolean hasHIDDescriptor() {
+ ArrayList<UsbDescriptor> descriptors =
+ getInterfaceDescriptorsForClass(UsbDescriptor.CLASSID_HID);
+ return !descriptors.isEmpty();
+ }
+
+ /**
+ * @hide
+ */
+ public boolean hasMIDIInterface() {
+ ArrayList<UsbDescriptor> descriptors =
+ getInterfaceDescriptorsForClass(UsbDescriptor.CLASSID_AUDIO);
+ for (UsbDescriptor descriptor : descriptors) {
+ // enusure that this isn't an unrecognized interface descriptor
+ if (descriptor instanceof UsbInterfaceDescriptor) {
+ UsbInterfaceDescriptor interfaceDescr = (UsbInterfaceDescriptor) descriptor;
+ if (interfaceDescr.getUsbSubclass() == UsbDescriptor.AUDIO_MIDISTREAMING) {
+ return true;
+ }
+ } else {
+ Log.w(TAG, "Undefined Audio Class Interface l:" + descriptor.getLength()
+ + " t:0x" + Integer.toHexString(descriptor.getType()));
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @hide
+ */
+ public float getInputHeadsetProbability() {
+ if (hasMIDIInterface()) {
+ return 0.0f;
+ }
+
+ float probability = 0.0f;
+ ArrayList<UsbDescriptor> acDescriptors;
+
+ // Look for a microphone
+ boolean hasMic = false;
+ acDescriptors = getACInterfaceDescriptors(UsbACInterface.ACI_INPUT_TERMINAL,
+ UsbACInterface.AUDIO_AUDIOCONTROL);
+ for (UsbDescriptor descriptor : acDescriptors) {
+ if (descriptor instanceof UsbACInputTerminal) {
+ UsbACInputTerminal inDescr = (UsbACInputTerminal) descriptor;
+ if (inDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_IN_MIC
+ || inDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_BIDIR_HEADSET
+ || inDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_BIDIR_UNDEFINED
+ || inDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_EXTERN_LINE) {
+ hasMic = true;
+ break;
+ }
+ } else {
+ Log.w(TAG, "Undefined Audio Input terminal l:" + descriptor.getLength()
+ + " t:0x" + Integer.toHexString(descriptor.getType()));
+ }
+ }
+
+ // Look for a "speaker"
+ boolean hasSpeaker = false;
+ acDescriptors =
+ getACInterfaceDescriptors(UsbACInterface.ACI_OUTPUT_TERMINAL,
+ UsbACInterface.AUDIO_AUDIOCONTROL);
+ for (UsbDescriptor descriptor : acDescriptors) {
+ if (descriptor instanceof UsbACOutputTerminal) {
+ UsbACOutputTerminal outDescr = (UsbACOutputTerminal) descriptor;
+ if (outDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_OUT_SPEAKER
+ || outDescr.getTerminalType()
+ == UsbTerminalTypes.TERMINAL_OUT_HEADPHONES
+ || outDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_BIDIR_HEADSET) {
+ hasSpeaker = true;
+ break;
+ }
+ } else {
+ Log.w(TAG, "Undefined Audio Output terminal l:" + descriptor.getLength()
+ + " t:0x" + Integer.toHexString(descriptor.getType()));
+ }
+ }
+
+ if (hasMic && hasSpeaker) {
+ probability += 0.75f;
+ }
+
+ if (hasMic && hasHIDDescriptor()) {
+ probability += 0.25f;
+ }
+
+ return probability;
+ }
+
+ /**
+ * getInputHeadsetProbability() reports a probability of a USB Input peripheral being a
+ * headset. The probability range is between 0.0f (definitely NOT a headset) and
+ * 1.0f (definitely IS a headset). A probability of 0.75f seems sufficient
+ * to count on the peripheral being a headset.
+ */
+ public boolean isInputHeadset() {
+ return getInputHeadsetProbability() >= IN_HEADSET_TRIGGER;
+ }
+
+ /**
+ * @hide
+ */
+ public float getOutputHeadsetProbability() {
+ if (hasMIDIInterface()) {
+ return 0.0f;
+ }
+
+ float probability = 0.0f;
+ ArrayList<UsbDescriptor> acDescriptors;
+
+ // Look for a "speaker"
+ boolean hasSpeaker = false;
+ acDescriptors =
+ getACInterfaceDescriptors(UsbACInterface.ACI_OUTPUT_TERMINAL,
+ UsbACInterface.AUDIO_AUDIOCONTROL);
+ for (UsbDescriptor descriptor : acDescriptors) {
+ if (descriptor instanceof UsbACOutputTerminal) {
+ UsbACOutputTerminal outDescr = (UsbACOutputTerminal) descriptor;
+ if (outDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_OUT_SPEAKER
+ || outDescr.getTerminalType()
+ == UsbTerminalTypes.TERMINAL_OUT_HEADPHONES
+ || outDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_BIDIR_HEADSET) {
+ hasSpeaker = true;
+ break;
+ }
+ } else {
+ Log.w(TAG, "Undefined Audio Output terminal l:" + descriptor.getLength()
+ + " t:0x" + Integer.toHexString(descriptor.getType()));
+ }
+ }
+
+ if (hasSpeaker) {
+ probability += 0.75f;
+ }
+
+ if (hasSpeaker && hasHIDDescriptor()) {
+ probability += 0.25f;
+ }
+
+ return probability;
+ }
+
+ /**
+ * getOutputHeadsetProbability() reports a probability of a USB Output peripheral being a
+ * headset. The probability range is between 0.0f (definitely NOT a headset) and
+ * 1.0f (definitely IS a headset). A probability of 0.75f seems sufficient
+ * to count on the peripheral being a headset.
+ */
+ public boolean isOutputHeadset() {
+ return getOutputHeadsetProbability() >= OUT_HEADSET_TRIGGER;
+ }
+
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java
new file mode 100644
index 0000000..90848ca
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2017 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;
+
+/**
+ * @hide
+ * A USB Device Descriptor.
+ * see usb11.pdf section 9.6.1
+ */
+/* public */ public class UsbDeviceDescriptor extends UsbDescriptor {
+ private static final String TAG = "Device";
+
+ private int mSpec; // 2:2 bcdUSB 2 BCD USB Specification Number - BCD
+ private byte mDevClass; // 4:1 class code
+ private byte mDevSubClass; // 5:1 subclass code
+ private byte mProtocol; // 6:1 protocol
+ private byte mPacketSize; // 7:1 Maximum Packet Size for Zero Endpoint.
+ // Valid Sizes are 8, 16, 32, 64
+ private int mVendorID; // 8:2 vendor ID
+ private int mProductID; // 10:2 product ID
+ private int mDeviceRelease; // 12:2 Device Release number - BCD
+ private byte mMfgIndex; // 14:1 Index of Manufacturer String Descriptor
+ private byte mProductIndex; // 15:1 Index of Product String Descriptor
+ private byte mSerialNum; // 16:1 Index of Serial Number String Descriptor
+ private byte mNumConfigs; // 17:1 Number of Possible Configurations
+
+ UsbDeviceDescriptor(int length, byte type) {
+ super(length, type);
+ }
+
+ public int getSpec() {
+ return mSpec;
+ }
+
+ public byte getDevClass() {
+ return mDevClass;
+ }
+
+ public byte getDevSubClass() {
+ return mDevSubClass;
+ }
+
+ public byte getProtocol() {
+ return mProtocol;
+ }
+
+ public byte getPacketSize() {
+ return mPacketSize;
+ }
+
+ public int getVendorID() {
+ return mVendorID;
+ }
+
+ public int getProductID() {
+ return mProductID;
+ }
+
+ public int getDeviceRelease() {
+ return mDeviceRelease;
+ }
+
+ public byte getMfgIndex() {
+ return mMfgIndex;
+ }
+
+ public byte getProductIndex() {
+ return mProductIndex;
+ }
+
+ public byte getSerialNum() {
+ return mSerialNum;
+ }
+
+ public byte getNumConfigs() {
+ return mNumConfigs;
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ mSpec = stream.unpackUsbWord();
+ mDevClass = stream.getByte();
+ mDevSubClass = stream.getByte();
+ mProtocol = stream.getByte();
+ mPacketSize = stream.getByte();
+ mVendorID = stream.unpackUsbWord();
+ mProductID = stream.unpackUsbWord();
+ mDeviceRelease = stream.unpackUsbWord();
+ mMfgIndex = stream.getByte();
+ mProductIndex = stream.getByte();
+ mSerialNum = stream.getByte();
+ mNumConfigs = stream.getByte();
+
+ return mLength;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java
new file mode 100644
index 0000000..def6700
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2017 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;
+
+/**
+ * @hide
+ * A Usb Endpoint Descriptor.
+ * see usb11.pdf section 9.6.4
+ */
+public class UsbEndpointDescriptor extends UsbDescriptor {
+ private static final String TAG = "EndPoint";
+
+ public static final byte MASK_ENDPOINT_ADDRESS = 0b0001111;
+ public static final byte MASK_ENDPOINT_DIRECTION = (byte) 0b10000000;
+ public static final byte DIRECTION_OUTPUT = 0x00;
+ public static final byte DIRECTION_INPUT = (byte) 0x80;
+
+ public static final byte MASK_ATTRIBS_TRANSTYPE = 0b00000011;
+ public static final byte TRANSTYPE_CONTROL = 0x00;
+ public static final byte TRANSTYPE_ISO = 0x01;
+ public static final byte TRANSTYPE_BULK = 0x02;
+ public static final byte TRANSTYPE_INTERRUPT = 0x03;
+
+ public static final byte MASK_ATTRIBS_SYNCTYPE = 0b00001100;
+ public static final byte SYNCTYPE_NONE = 0b00000000;
+ public static final byte SYNCTYPE_ASYNC = 0b00000100;
+ public static final byte SYNCTYPE_ADAPTSYNC = 0b00001000;
+ public static final byte SYNCTYPE_RESERVED = 0b00001100;
+
+ public static final byte MASK_ATTRIBS_USEAGE = 0b00110000;
+ public static final byte USEAGE_DATA = 0b00000000;
+ public static final byte USEAGE_FEEDBACK = 0b00010000;
+ public static final byte USEAGE_EXPLICIT = 0b00100000;
+ public static final byte USEAGE_RESERVED = 0b00110000;
+
+ private byte mEndpointAddress; // 2:1 Endpoint Address
+ // Bits 0..3b Endpoint Number.
+ // Bits 4..6b Reserved. Set to Zero
+ // Bits 7 Direction 0 = Out, 1 = In
+ // (Ignored for Control Endpoints)
+ private byte mAttributes; // 3:1 Various flags
+ // Bits 0..1 Transfer Type:
+ // 00 = Control, 01 = Isochronous, 10 = Bulk, 11 = Interrupt
+ // Bits 2..7 are reserved. If Isochronous endpoint,
+ // Bits 3..2 = Synchronisation Type (Iso Mode)
+ // 00 = No Synchonisation
+ // 01 = Asynchronous
+ // 10 = Adaptive
+ // 11 = Synchronous
+ // Bits 5..4 = Usage Type (Iso Mode)
+ // 00: Data Endpoint
+ // 01:Feedback Endpoint 10
+ // Explicit Feedback Data Endpoint
+ // 11: Reserved
+ private int mPacketSize; // 4:2 Maximum Packet Size this endpoint is capable of
+ // sending or receiving
+ private byte mInterval; // 6:1 Interval for polling endpoint data transfers. Value in
+ // frame counts.
+ // Ignored for Bulk & Control Endpoints. Isochronous must equal
+ // 1 and field may range from 1 to 255 for interrupt endpoints.
+ private byte mRefresh;
+ private byte mSyncAddress;
+
+ public UsbEndpointDescriptor(int length, byte type) {
+ super(length, type);
+ }
+
+ public byte getEndpointAddress() {
+ return mEndpointAddress;
+ }
+
+ public byte getAttributes() {
+ return mAttributes;
+ }
+
+ public int getPacketSize() {
+ return mPacketSize;
+ }
+
+ public byte getInterval() {
+ return mInterval;
+ }
+
+ public byte getRefresh() {
+ return mRefresh;
+ }
+
+ public byte getSyncAddress() {
+ return mSyncAddress;
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ mEndpointAddress = stream.getByte();
+ mAttributes = stream.getByte();
+ mPacketSize = stream.unpackUsbWord();
+ mInterval = stream.getByte();
+ if (mLength == 9) {
+ mRefresh = stream.getByte();
+ mSyncAddress = stream.getByte();
+ }
+ return mLength;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbHIDDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbHIDDescriptor.java
new file mode 100644
index 0000000..56c07ec
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbHIDDescriptor.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2017 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;
+
+/**
+ * @hide
+ * A USB HID (Human Interface Descriptor).
+ * see HID1_11.pdf - 6.2.1
+ */
+public class UsbHIDDescriptor extends UsbDescriptor {
+ private static final String TAG = "HID";
+
+ private int mRelease; // 2:2 the HID Class Specification release.
+ private byte mCountryCode; // 4:1 country code of the localized hardware.
+ private byte mNumDescriptors; // number of descriptors (always at least one
+ // i.e. Report descriptor.)
+ private byte mDescriptorType; // 6:1 type of class descriptor.
+ // See Section 7.1.2: Set_Descriptor
+ // Request for a table of class descriptor constants.
+ private int mDescriptorLen; // 7:2 Numeric expression that is the total size of
+ // the Report descriptor.
+
+ public UsbHIDDescriptor(int length, byte type) {
+ super(length, type);
+ }
+
+ public int getRelease() {
+ return mRelease;
+ }
+
+ public byte getCountryCode() {
+ return mCountryCode;
+ }
+
+ public byte getNumDescriptors() {
+ return mNumDescriptors;
+ }
+
+ public byte getDescriptorType() {
+ return mDescriptorType;
+ }
+
+ public int getDescriptorLen() {
+ return mDescriptorLen;
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ mRelease = stream.unpackUsbWord();
+ mCountryCode = stream.getByte();
+ mNumDescriptors = stream.getByte();
+ mDescriptorType = stream.getByte();
+ mDescriptorLen = stream.unpackUsbWord();
+
+ return mLength;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceAssoc.java b/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceAssoc.java
new file mode 100644
index 0000000..4b18a01
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceAssoc.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2017 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;
+
+/**
+ * @hide
+ * A USB Interface Association Descriptor.
+ * found this one here: http://www.usb.org/developers/docs/whitepapers/iadclasscode_r10.pdf
+ * also: https://msdn.microsoft.com/en-us/library/windows/hardware/ff540054(v=vs.85).aspx
+ */
+public class UsbInterfaceAssoc extends UsbDescriptor {
+ private static final String TAG = "InterfaceAssoc";
+
+ private byte mFirstInterface;
+ private byte mInterfaceCount;
+ private byte mFunctionClass;
+ private byte mFunctionSubClass;
+ private byte mFunctionProtocol;
+ private byte mFunction;
+
+ public UsbInterfaceAssoc(int length, byte type) {
+ super(length, type);
+ }
+
+ public byte getFirstInterface() {
+ return mFirstInterface;
+ }
+
+ public byte getInterfaceCount() {
+ return mInterfaceCount;
+ }
+
+ public byte getFunctionClass() {
+ return mFunctionClass;
+ }
+
+ public byte getFunctionSubClass() {
+ return mFunctionSubClass;
+ }
+
+ public byte getFunctionProtocol() {
+ return mFunctionProtocol;
+ }
+
+ public byte getFunction() {
+ return mFunction;
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ mFirstInterface = stream.getByte();
+ mInterfaceCount = stream.getByte();
+ mFunctionClass = stream.getByte();
+ mFunctionSubClass = stream.getByte();
+ mFunctionProtocol = stream.getByte();
+ mFunction = stream.getByte();
+
+ return mLength;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
new file mode 100644
index 0000000..21b5e0c
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2017 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;
+
+/**
+ * @hide
+ * A common super-class for all USB Interface Descritor subtypes.
+ * see usb11.pdf section 9.6.3
+ */
+public class UsbInterfaceDescriptor extends UsbDescriptor {
+ private static final String TAG = "Interface";
+
+ protected byte 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
+ protected byte mUsbClass; // 5:1 Class Code
+ protected byte mUsbSubclass; // 6:1 Subclass Code
+ protected byte mProtocol; // 7:1 Protocol Code
+ protected byte mDescrIndex; // 8:1 Index of String Descriptor Describing this interface
+
+ UsbInterfaceDescriptor(int length, byte type) {
+ super(length, type);
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ mInterfaceNumber = stream.getByte();
+ mAlternateSetting = stream.getByte();
+ mNumEndpoints = stream.getByte();
+ mUsbClass = stream.getByte();
+ mUsbSubclass = stream.getByte();
+ mProtocol = stream.getByte();
+ mDescrIndex = stream.getByte();
+
+ return mLength;
+ }
+
+ public byte getInterfaceNumber() {
+ return mInterfaceNumber;
+ }
+
+ public byte getAlternateSetting() {
+ return mAlternateSetting;
+ }
+
+ public byte getNumEndpoints() {
+ return mNumEndpoints;
+ }
+
+ public byte getUsbClass() {
+ return mUsbClass;
+ }
+
+ public byte getUsbSubclass() {
+ return mUsbSubclass;
+ }
+
+ public byte getProtocol() {
+ return mProtocol;
+ }
+
+ public byte getDescrIndex() {
+ return mDescrIndex;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbMSMidiHeader.java b/services/usb/java/com/android/server/usb/descriptors/UsbMSMidiHeader.java
new file mode 100644
index 0000000..4452b23
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbMSMidiHeader.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2017 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;
+
+/**
+ * @hide
+ * An audio class-specific Midi Streaming Interface.
+ * see midi10.pdf section 6.1.2.1
+ */
+public class UsbMSMidiHeader extends UsbACInterface {
+ private static final String TAG = "MSMidiHeader";
+
+ public UsbMSMidiHeader(int length, byte type, byte subtype, byte subclass) {
+ super(length, type, subtype, subclass);
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ // TODO - read data memebers
+ stream.advance(mLength - stream.getReadCount());
+ return mLength;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbMSMidiInputJack.java b/services/usb/java/com/android/server/usb/descriptors/UsbMSMidiInputJack.java
new file mode 100644
index 0000000..2d33ba7
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbMSMidiInputJack.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2017 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;
+
+/**
+ * @hide
+ * An audio class-specific Midi Input Jack Interface.
+ * see midi10.pdf section B.4.3
+ */
+public class UsbMSMidiInputJack extends UsbACInterface {
+ private static final String TAG = "MSMidiInputJack";
+
+ UsbMSMidiInputJack(int length, byte type, byte subtype, byte subclass) {
+ super(length, type, subtype, subclass);
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ // TODO - read data memebers
+ stream.advance(mLength - stream.getReadCount());
+ return mLength;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbMSMidiOutputJack.java b/services/usb/java/com/android/server/usb/descriptors/UsbMSMidiOutputJack.java
new file mode 100644
index 0000000..bd2dc11
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbMSMidiOutputJack.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2017 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;
+
+/**
+ * @hide
+ * An audio class-specific Midi Output Jack Interface.
+ * see midi10.pdf section B.4.4
+ */
+public class UsbMSMidiOutputJack extends UsbACInterface {
+ private static final String TAG = "MSMidiOutputJack";
+
+ public UsbMSMidiOutputJack(int length, byte type, byte subtype, byte subclass) {
+ super(length, type, subtype, subclass);
+ }
+
+ @Override
+ public int parseRawDescriptors(ByteStream stream) {
+ // TODO - read data memebers
+ stream.advance(mLength - stream.getReadCount());
+ return mLength;
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbTerminalTypes.java b/services/usb/java/com/android/server/usb/descriptors/UsbTerminalTypes.java
new file mode 100644
index 0000000..b521462
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbTerminalTypes.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2017 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;
+
+/**
+ * @hide
+ * A class for decoding information in Terminal Descriptors.
+ * see termt10.pdf
+ */
+public class UsbTerminalTypes {
+ private static final String TAG = "TerminalTypes";
+
+ // USB
+ public static final int TERMINAL_USB_STREAMING = 0x0101;
+
+ // Inputs
+ public static final int TERMINAL_IN_UNDEFINED = 0x0200;
+ public static final int TERMINAL_IN_MIC = 0x0201;
+ public static final int TERMINAL_IN_DESKTOP_MIC = 0x0202;
+ public static final int TERMINAL_IN_PERSONAL_MIC = 0x0203;
+ public static final int TERMINAL_IN_OMNI_MIC = 0x0204;
+ public static final int TERMINAL_IN_MIC_ARRAY = 0x0205;
+ public static final int TERMINAL_IN_PROC_MIC_ARRAY = 0x0206;
+
+ // Outputs
+ public static final int TERMINAL_OUT_UNDEFINED = 0x0300;
+ public static final int TERMINAL_OUT_SPEAKER = 0x0301;
+ public static final int TERMINAL_OUT_HEADPHONES = 0x0302;
+ public static final int TERMINAL_OUT_HEADMOUNTED = 0x0303;
+ public static final int TERMINAL_OUT_DESKTOPSPEAKER = 0x0304;
+ public static final int TERMINAL_OUT_ROOMSPEAKER = 0x0305;
+ public static final int TERMINAL_OUT_COMSPEAKER = 0x0306;
+ public static final int TERMINAL_OUT_LFSPEAKER = 0x0307;
+
+ // Bi-directional
+ public static final int TERMINAL_BIDIR_UNDEFINED = 0x0400;
+ public static final int TERMINAL_BIDIR_HANDSET = 0x0401;
+ public static final int TERMINAL_BIDIR_HEADSET = 0x0402;
+ public static final int TERMINAL_BIDIR_SKRPHONE = 0x0403;
+ public static final int TERMINAL_BIDIR_SKRPHONE_SUPRESS = 0x0404;
+ public static final int TERMINAL_BIDIR_SKRPHONE_CANCEL = 0x0405;
+
+ // Telephony
+ public static final int TERMINAL_TELE_UNDEFINED = 0x0500;
+ public static final int TERMINAL_TELE_PHONELINE = 0x0501;
+ public static final int TERMINAL_TELE_PHONE = 0x0502;
+ public static final int TERMINAL_TELE_DOWNLINEPHONE = 0x0503;
+
+ // External
+ public static final int TERMINAL_EXTERN_UNDEFINED = 0x0600;
+ public static final int TERMINAL_EXTERN_ANALOG = 0x0601;
+ public static final int TERMINAL_EXTERN_DIGITAL = 0x0602;
+ public static final int TERMINAL_EXTERN_LINE = 0x0603;
+ public static final int TERMINAL_EXTERN_LEGACY = 0x0604;
+ public static final int TERMINAL_EXTERN_SPIDF = 0x0605;
+ public static final int TERMINAL_EXTERN_1394DA = 0x0606;
+ public static final int TERMINAL_EXTERN_1394DV = 0x0607;
+
+ public static final int TERMINAL_EMBED_UNDEFINED = 0x0700;
+ public static final int TERMINAL_EMBED_CALNOISE = 0x0701;
+ public static final int TERMINAL_EMBED_EQNOISE = 0x0702;
+ public static final int TERMINAL_EMBED_CDPLAYER = 0x0703;
+ public static final int TERMINAL_EMBED_DAT = 0x0704;
+ public static final int TERMINAL_EMBED_DCC = 0x0705;
+ public static final int TERMINAL_EMBED_MINIDISK = 0x0706;
+ public static final int TERMINAL_EMBED_ANALOGTAPE = 0x0707;
+ public static final int TERMINAL_EMBED_PHONOGRAPH = 0x0708;
+ public static final int TERMINAL_EMBED_VCRAUDIO = 0x0709;
+ public static final int TERMINAL_EMBED_VIDDISKAUDIO = 0x070A;
+ public static final int TERMINAL_EMBED_DVDAUDIO = 0x070B;
+ public static final int TERMINAL_EMBED_TVAUDIO = 0x070C;
+ public static final int TERMINAL_EMBED_SATELLITEAUDIO = 0x070D;
+ public static final int TERMINAL_EMBED_CABLEAUDIO = 0x070E;
+ public static final int TERMINAL_EMBED_DSSAUDIO = 0x070F;
+ public static final int TERMINAL_EMBED_RADIOAUDIO = 0x0710;
+ public static final int TERMINAL_EMBED_RADIOTRANSMITTER = 0x0711;
+ public static final int TERMINAL_EMBED_MULTITRACK = 0x0712;
+ public static final int TERMINAL_EMBED_SYNTHESIZER = 0x0713;
+
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbUnknown.java b/services/usb/java/com/android/server/usb/descriptors/UsbUnknown.java
new file mode 100644
index 0000000..a6fe8bb
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbUnknown.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2017 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;
+
+/**
+ * @hide
+ * A holder for any unrecognized descriptor encountered in the descriptor stream.
+ */
+public class UsbUnknown extends UsbDescriptor {
+ static final String TAG = "Unknown";
+
+ public UsbUnknown(int length, byte type) {
+ super(length, type);
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/report/HTMLReporter.java b/services/usb/java/com/android/server/usb/descriptors/report/HTMLReporter.java
new file mode 100644
index 0000000..c98789d
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/report/HTMLReporter.java
@@ -0,0 +1,572 @@
+/*
+ * Copyright (C) 2017 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.report;
+
+import android.hardware.usb.UsbDeviceConnection;
+
+import com.android.server.usb.descriptors.UsbACAudioControlEndpoint;
+import com.android.server.usb.descriptors.UsbACAudioStreamEndpoint;
+import com.android.server.usb.descriptors.UsbACFeatureUnit;
+import com.android.server.usb.descriptors.UsbACHeader;
+import com.android.server.usb.descriptors.UsbACInputTerminal;
+import com.android.server.usb.descriptors.UsbACInterface;
+import com.android.server.usb.descriptors.UsbACMidiEndpoint;
+import com.android.server.usb.descriptors.UsbACMixerUnit;
+import com.android.server.usb.descriptors.UsbACOutputTerminal;
+import com.android.server.usb.descriptors.UsbACSelectorUnit;
+import com.android.server.usb.descriptors.UsbACTerminal;
+import com.android.server.usb.descriptors.UsbASFormat;
+import com.android.server.usb.descriptors.UsbASFormatI;
+import com.android.server.usb.descriptors.UsbASFormatII;
+import com.android.server.usb.descriptors.UsbASGeneral;
+import com.android.server.usb.descriptors.UsbConfigDescriptor;
+import com.android.server.usb.descriptors.UsbDescriptor;
+import com.android.server.usb.descriptors.UsbDeviceDescriptor;
+import com.android.server.usb.descriptors.UsbEndpointDescriptor;
+import com.android.server.usb.descriptors.UsbHIDDescriptor;
+import com.android.server.usb.descriptors.UsbInterfaceAssoc;
+import com.android.server.usb.descriptors.UsbInterfaceDescriptor;
+import com.android.server.usb.descriptors.UsbMSMidiHeader;
+import com.android.server.usb.descriptors.UsbMSMidiInputJack;
+import com.android.server.usb.descriptors.UsbMSMidiOutputJack;
+import com.android.server.usb.descriptors.UsbUnknown;
+
+/**
+ * Implements the Reporter inteface to provide HTML reporting for UsbDescriptor subclasses.
+ */
+public class HTMLReporter implements Reporter {
+ private final StringBuilder mStringBuilder;
+ private final UsbDeviceConnection mConnection;
+
+ public HTMLReporter(StringBuilder stringBuilder, UsbDeviceConnection connection) {
+ mStringBuilder = stringBuilder;
+ mConnection = connection;
+ }
+
+ /*
+ * HTML Helpers
+ */
+ private void writeHeader(int level, String text) {
+ mStringBuilder
+ .append("<h").append(level).append('>')
+ .append(text)
+ .append("</h").append(level).append('>');
+ }
+
+ private void openParagraph() {
+ mStringBuilder.append("<p>");
+ }
+
+ private void closeParagraph() {
+ mStringBuilder.append("</p>");
+ }
+
+ private void writeParagraph(String text) {
+ openParagraph();
+ mStringBuilder.append(text);
+ closeParagraph();
+ }
+
+ private void openList() {
+ mStringBuilder.append("<ul>");
+ }
+
+ private void closeList() {
+ mStringBuilder.append("</ul>");
+ }
+
+ private void openListItem() {
+ mStringBuilder.append("<li>");
+ }
+
+ private void closeListItem() {
+ mStringBuilder.append("</li>");
+ }
+
+ private void writeListItem(String text) {
+ openListItem();
+ mStringBuilder.append(text);
+ closeListItem();
+ }
+
+ /*
+ * Data Formating Helpers
+ */
+ private static String getHexString(byte value) {
+ return "0x" + Integer.toHexString(((int) value) & 0xFF).toUpperCase();
+ }
+
+ private static String getBCDString(int value) {
+ int major = value >> 8;
+ int minor = (value >> 4) & 0x0F;
+ int subminor = value & 0x0F;
+
+ return "" + major + "." + minor + subminor;
+ }
+
+ private static String getHexString(int value) {
+ int intValue = value & 0xFFFF;
+ return "0x" + Integer.toHexString(intValue).toUpperCase();
+ }
+
+ private void dumpHexArray(byte[] rawData, StringBuilder builder) {
+ if (rawData != null) {
+ // Assume the type and Length and perhaps sub-type have been displayed
+ openParagraph();
+ for (int index = 0; index < rawData.length; index++) {
+ builder.append(getHexString(rawData[index]) + " ");
+ }
+ closeParagraph();
+ }
+ }
+
+ /**
+ * Decode ACTUAL UsbDescriptor sub classes and call type-specific report methods.
+ */
+ @Override
+ public void report(UsbDescriptor descriptor) {
+ if (descriptor instanceof UsbDeviceDescriptor) {
+ tsReport((UsbDeviceDescriptor) descriptor);
+ } else if (descriptor instanceof UsbConfigDescriptor) {
+ tsReport((UsbConfigDescriptor) descriptor);
+ } else if (descriptor instanceof UsbInterfaceDescriptor) {
+ tsReport((UsbInterfaceDescriptor) descriptor);
+ } else if (descriptor instanceof UsbEndpointDescriptor) {
+ tsReport((UsbEndpointDescriptor) descriptor);
+ } else if (descriptor instanceof UsbHIDDescriptor) {
+ tsReport((UsbHIDDescriptor) descriptor);
+ } else if (descriptor instanceof UsbACAudioControlEndpoint) {
+ tsReport((UsbACAudioControlEndpoint) descriptor);
+ } else if (descriptor instanceof UsbACAudioStreamEndpoint) {
+ tsReport((UsbACAudioStreamEndpoint) descriptor);
+ } else if (descriptor instanceof UsbACHeader) {
+ tsReport((UsbACHeader) descriptor);
+ } else if (descriptor instanceof UsbACFeatureUnit) {
+ tsReport((UsbACFeatureUnit) descriptor);
+ } else if (descriptor instanceof UsbACInputTerminal) {
+ tsReport((UsbACInputTerminal) descriptor);
+ } else if (descriptor instanceof UsbACOutputTerminal) {
+ tsReport((UsbACOutputTerminal) descriptor);
+ } else if (descriptor instanceof UsbACMidiEndpoint) {
+ tsReport((UsbACMidiEndpoint) descriptor);
+ } else if (descriptor instanceof UsbACMixerUnit) {
+ tsReport((UsbACMixerUnit) descriptor);
+ } else if (descriptor instanceof UsbACSelectorUnit) {
+ tsReport((UsbACSelectorUnit) descriptor);
+ } else if (descriptor instanceof UsbASFormatI) {
+ tsReport((UsbASFormatI) descriptor);
+ } else if (descriptor instanceof UsbASFormatII) {
+ tsReport((UsbASFormatII) descriptor);
+ } else if (descriptor instanceof UsbASFormat) {
+ tsReport((UsbASFormat) descriptor);
+ } else if (descriptor instanceof UsbASGeneral) {
+ tsReport((UsbASGeneral) descriptor);
+ } else if (descriptor instanceof UsbInterfaceAssoc) {
+ tsReport((UsbInterfaceAssoc) descriptor);
+ } else if (descriptor instanceof UsbMSMidiHeader) {
+ tsReport((UsbMSMidiHeader) descriptor);
+ } else if (descriptor instanceof UsbMSMidiInputJack) {
+ tsReport((UsbMSMidiInputJack) descriptor);
+ } else if (descriptor instanceof UsbMSMidiOutputJack) {
+ tsReport((UsbMSMidiOutputJack) descriptor);
+ } else if (descriptor instanceof UsbUnknown) {
+ tsReport((UsbUnknown) descriptor);
+ } else if (descriptor instanceof UsbACInterface) {
+ tsReport((UsbACInterface) descriptor);
+ } else if (descriptor instanceof UsbDescriptor) {
+ tsReport((UsbDescriptor) descriptor);
+ }
+ }
+
+ //
+ // Type-specific report() implementations
+ //
+ private void tsReport(UsbDescriptor descriptor) {
+ int length = descriptor.getLength();
+ byte type = descriptor.getType();
+ int status = descriptor.getStatus();
+
+ String descTypeStr = UsbStrings.getDescriptorName(type);
+ writeParagraph(descTypeStr + ":" + type + " l:" + length + " s:" + status);
+ }
+
+ private void tsReport(UsbDeviceDescriptor descriptor) {
+ writeHeader(1, "Device len:" + descriptor.getLength());
+ openList();
+
+ int spec = descriptor.getSpec();
+ writeListItem("spec:" + getBCDString(spec));
+
+ byte devClass = descriptor.getDevClass();
+ String classStr = UsbStrings.getClassName(devClass);
+ byte devSubClass = descriptor.getDevSubClass();
+ String subClasStr = UsbStrings.getClassName(devSubClass);
+ writeListItem("class " + devClass + ":" + classStr + " subclass"
+ + devSubClass + ":" + subClasStr);
+ writeListItem("vendorID:" + descriptor.getVendorID()
+ + " prodID:" + descriptor.getProductID()
+ + " prodRel:" + getBCDString(descriptor.getDeviceRelease()));
+
+ byte mfgIndex = descriptor.getMfgIndex();
+ String manufacturer = UsbDescriptor.getUsbDescriptorString(mConnection, mfgIndex);
+ byte productIndex = descriptor.getProductIndex();
+ String product = UsbDescriptor.getUsbDescriptorString(mConnection, productIndex);
+
+ writeListItem("mfg " + mfgIndex + ":" + manufacturer
+ + " prod " + productIndex + ":" + product);
+ closeList();
+ }
+
+ private void tsReport(UsbConfigDescriptor descriptor) {
+ writeHeader(2, "Config #" + descriptor.getConfigValue()
+ + " len:" + descriptor.getLength());
+
+ openList();
+ writeListItem(descriptor.getNumInterfaces() + " interfaces.");
+ writeListItem("attribs:" + getHexString(descriptor.getAttribs()));
+ closeList();
+ }
+
+ private void tsReport(UsbInterfaceDescriptor descriptor) {
+ byte usbClass = descriptor.getUsbClass();
+ byte usbSubclass = descriptor.getUsbSubclass();
+ String descr = UsbStrings.getDescriptorName(descriptor.getType());
+ String className = UsbStrings.getClassName(usbClass);
+ String subclassName = "";
+ if (usbClass == UsbDescriptor.CLASSID_AUDIO) {
+ subclassName = UsbStrings.getAudioSubclassName(usbSubclass);
+ }
+
+ writeHeader(2, descr + " #" + descriptor.getInterfaceNumber()
+ + " len:" + descriptor.getLength());
+ String descrStr =
+ UsbDescriptor.getUsbDescriptorString(mConnection, descriptor.getDescrIndex());
+ if (descrStr.length() > 0) {
+ mStringBuilder.append("<br>" + descrStr);
+ }
+ openList();
+ writeListItem("class " + getHexString(usbClass) + ":" + className
+ + " subclass " + getHexString(usbSubclass) + ":" + subclassName);
+ writeListItem("" + descriptor.getNumEndpoints() + " endpoints");
+ closeList();
+ }
+
+ private void tsReport(UsbEndpointDescriptor descriptor) {
+ writeHeader(3, "Endpoint " + getHexString(descriptor.getType())
+ + " len:" + descriptor.getLength());
+ openList();
+
+ byte address = descriptor.getEndpointAddress();
+ writeListItem("address:"
+ + getHexString(address & UsbEndpointDescriptor.MASK_ENDPOINT_ADDRESS)
+ + ((address & UsbEndpointDescriptor.MASK_ENDPOINT_DIRECTION)
+ == UsbEndpointDescriptor.DIRECTION_OUTPUT ? " [out]" : " [in]"));
+
+ byte attributes = descriptor.getAttributes();
+ openListItem();
+ mStringBuilder.append("attribs:" + getHexString(attributes) + " ");
+ switch (attributes & UsbEndpointDescriptor.MASK_ATTRIBS_TRANSTYPE) {
+ case UsbEndpointDescriptor.TRANSTYPE_CONTROL:
+ mStringBuilder.append("Control");
+ break;
+ case UsbEndpointDescriptor.TRANSTYPE_ISO:
+ mStringBuilder.append("Iso");
+ break;
+ case UsbEndpointDescriptor.TRANSTYPE_BULK:
+ mStringBuilder.append("Bulk");
+ break;
+ case UsbEndpointDescriptor.TRANSTYPE_INTERRUPT:
+ mStringBuilder.append("Interrupt");
+ break;
+ }
+ closeListItem();
+
+ // These flags are only relevant for ISO transfer type
+ if ((attributes & UsbEndpointDescriptor.MASK_ATTRIBS_TRANSTYPE)
+ == UsbEndpointDescriptor.TRANSTYPE_ISO) {
+ openListItem();
+ mStringBuilder.append("sync:");
+ switch (attributes & UsbEndpointDescriptor.MASK_ATTRIBS_SYNCTYPE) {
+ case UsbEndpointDescriptor.SYNCTYPE_NONE:
+ mStringBuilder.append("NONE");
+ break;
+ case UsbEndpointDescriptor.SYNCTYPE_ASYNC:
+ mStringBuilder.append("ASYNC");
+ break;
+ case UsbEndpointDescriptor.SYNCTYPE_ADAPTSYNC:
+ mStringBuilder.append("ADAPTIVE ASYNC");
+ break;
+ }
+ closeListItem();
+
+ openListItem();
+ mStringBuilder.append("useage:");
+ switch (attributes & UsbEndpointDescriptor.MASK_ATTRIBS_USEAGE) {
+ case UsbEndpointDescriptor.USEAGE_DATA:
+ mStringBuilder.append("DATA");
+ break;
+ case UsbEndpointDescriptor.USEAGE_FEEDBACK:
+ mStringBuilder.append("FEEDBACK");
+ break;
+ case UsbEndpointDescriptor.USEAGE_EXPLICIT:
+ mStringBuilder.append("EXPLICIT FEEDBACK");
+ break;
+ case UsbEndpointDescriptor.USEAGE_RESERVED:
+ mStringBuilder.append("RESERVED");
+ break;
+ }
+ closeListItem();
+ }
+ writeListItem("package size:" + descriptor.getPacketSize());
+ writeListItem("interval:" + descriptor.getInterval());
+ closeList();
+ }
+
+ private void tsReport(UsbHIDDescriptor descriptor) {
+ String descr = UsbStrings.getDescriptorName(descriptor.getType());
+ writeHeader(2, descr + " len:" + descriptor.getLength());
+ openList();
+ writeListItem("spec:" + getBCDString(descriptor.getRelease()));
+ writeListItem("type:" + getBCDString(descriptor.getDescriptorType()));
+ writeListItem("descriptor.getNumDescriptors() descriptors len:"
+ + descriptor.getDescriptorLen());
+ closeList();
+ }
+
+ private void tsReport(UsbACAudioControlEndpoint descriptor) {
+ writeHeader(3, "AC Audio Control Endpoint:" + getHexString(descriptor.getType())
+ + " length:" + descriptor.getLength());
+ }
+
+ private void tsReport(UsbACAudioStreamEndpoint descriptor) {
+ writeHeader(3, "AC Audio Streaming Endpoint:"
+ + getHexString(descriptor.getType())
+ + " length:" + descriptor.getLength());
+ }
+
+ private void tsReport(UsbACHeader descriptor) {
+ tsReport((UsbACInterface) descriptor);
+
+ openList();
+ writeListItem("spec:" + getBCDString(descriptor.getADCRelease()));
+ int numInterfaces = descriptor.getNumInterfaces();
+ writeListItem("" + numInterfaces + " interfaces");
+ if (numInterfaces > 0) {
+ openListItem();
+ mStringBuilder.append("[");
+ byte[] interfaceNums = descriptor.getInterfaceNums();
+ if (numInterfaces != 0 && interfaceNums != null) {
+ for (int index = 0; index < numInterfaces; index++) {
+ mStringBuilder.append("" + interfaceNums[index]);
+ if (index < numInterfaces - 1) {
+ mStringBuilder.append(" ");
+ }
+ }
+ }
+ mStringBuilder.append("]");
+ closeListItem();
+ }
+ writeListItem("controls:" + getHexString(descriptor.getControls()));
+ closeList();
+ }
+
+ private void tsReport(UsbACFeatureUnit descriptor) {
+ tsReport((UsbACInterface) descriptor);
+ }
+
+ private void tsReport(UsbACInterface descriptor) {
+ String subClassName =
+ descriptor.getSubclass() == UsbDescriptor.AUDIO_AUDIOCONTROL
+ ? "AC Control"
+ : "AC Streaming";
+ byte subtype = descriptor.getSubtype();
+ String subTypeStr = UsbStrings.getACControlInterfaceName(subtype);
+ writeHeader(4, subClassName + " - " + getHexString(subtype)
+ + ":" + subTypeStr + " len:" + descriptor.getLength());
+ }
+
+ private void tsReport(UsbACTerminal descriptor) {
+ tsReport((UsbACInterface) descriptor);
+ }
+
+ private void tsReport(UsbACInputTerminal descriptor) {
+ tsReport((UsbACTerminal) descriptor);
+
+ openList();
+ writeListItem("ID:" + getHexString(descriptor.getTerminalID()));
+ int terminalType = descriptor.getTerminalType();
+ writeListItem("Type:<b>" + getHexString(terminalType) + ":"
+ + UsbStrings.getTerminalName(terminalType) + "</b>");
+ writeListItem("AssocTerminal:" + getHexString(descriptor.getAssocTerminal()));
+ writeListItem("" + descriptor.getNrChannels() + " chans. config:"
+ + getHexString(descriptor.getChannelConfig()));
+ closeList();
+ }
+
+ private void tsReport(UsbACOutputTerminal descriptor) {
+ tsReport((UsbACTerminal) descriptor);
+
+ openList();
+ writeListItem("ID:" + getHexString(descriptor.getTerminalID()));
+ int terminalType = descriptor.getTerminalType();
+ writeListItem("Type:<b>" + getHexString(terminalType) + ":"
+ + UsbStrings.getTerminalName(terminalType) + "</b>");
+ writeListItem("AssocTerminal:" + getHexString(descriptor.getAssocTerminal()));
+ writeListItem("Source:" + getHexString(descriptor.getSourceID()));
+ closeList();
+ }
+
+ private void tsReport(UsbACMidiEndpoint descriptor) {
+ writeHeader(3, "AC Midi Endpoint:" + getHexString(descriptor.getType())
+ + " length:" + descriptor.getLength());
+ openList();
+ writeListItem("" + descriptor.getNumJacks() + " jacks.");
+ closeList();
+ }
+
+ private void tsReport(UsbACMixerUnit descriptor) {
+ tsReport((UsbACInterface) descriptor);
+ openList();
+
+ writeListItem("Unit ID:" + getHexString(descriptor.getUnitID()));
+ byte numInputs = descriptor.getNumInputs();
+ byte[] inputIDs = descriptor.getInputIDs();
+ openListItem();
+ mStringBuilder.append("Num Inputs:" + numInputs + " [");
+ for (int input = 0; input < numInputs; input++) {
+ mStringBuilder.append("" + getHexString(inputIDs[input]));
+ if (input < numInputs - 1) {
+ mStringBuilder.append(" ");
+ }
+ }
+ mStringBuilder.append("]");
+ closeListItem();
+
+ writeListItem("Num Outputs:" + descriptor.getNumOutputs());
+ writeListItem("Chan Config:" + getHexString(descriptor.getChannelConfig()));
+
+ byte[] controls = descriptor.getControls();
+ openListItem();
+ mStringBuilder.append("controls:" + controls.length + " [");
+ for (int ctrl = 0; ctrl < controls.length; ctrl++) {
+ mStringBuilder.append("" + controls[ctrl]);
+ if (ctrl < controls.length - 1) {
+ mStringBuilder.append(" ");
+ }
+ }
+ mStringBuilder.append("]");
+ closeListItem();
+ closeList();
+ // byte mChanNameID; // First channel name string descriptor ID
+ // byte mNameID; // string descriptor ID of mixer name
+ }
+
+ private void tsReport(UsbACSelectorUnit descriptor) {
+ tsReport((UsbACInterface) descriptor);
+ }
+
+ private void tsReport(UsbASFormat descriptor) {
+ writeHeader(4, "AC Streaming Format "
+ + (descriptor.getFormatType() == UsbASFormat.FORMAT_TYPE_I ? "I" : "II")
+ + " - " + getHexString(descriptor.getSubtype()) + ":"
+ + " len:" + descriptor.getLength());
+ }
+
+ private void tsReport(UsbASFormatI descriptor) {
+ tsReport((UsbASFormat) descriptor);
+ openList();
+ writeListItem("chans:" + descriptor.getNumChannels());
+ writeListItem("subframe size:" + descriptor.getSubframeSize());
+ writeListItem("bit resolution:" + descriptor.getBitResolution());
+ byte sampleFreqType = descriptor.getSampleFreqType();
+ int[] sampleRates = descriptor.getSampleRates();
+ writeListItem("sample freq type:" + sampleFreqType);
+ if (sampleFreqType == 0) {
+ openList();
+ writeListItem("min:" + sampleRates[0]);
+ writeListItem("max:" + sampleRates[1]);
+ closeList();
+ } else {
+ openList();
+ for (int index = 0; index < sampleFreqType; index++) {
+ writeListItem("" + sampleRates[index]);
+ }
+ closeList();
+ }
+ closeList();
+ }
+
+ private void tsReport(UsbASFormatII descriptor) {
+ tsReport((UsbASFormat) descriptor);
+ openList();
+ writeListItem("max bit rate:" + descriptor.getMaxBitRate());
+ writeListItem("samples per frame:" + descriptor.getMaxBitRate());
+ byte sampleFreqType = descriptor.getSamFreqType();
+ int[] sampleRates = descriptor.getSampleRates();
+ writeListItem("sample freq type:" + sampleFreqType);
+ if (sampleFreqType == 0) {
+ openList();
+ writeListItem("min:" + sampleRates[0]);
+ writeListItem("max:" + sampleRates[1]);
+ closeList();
+ } else {
+ openList();
+ for (int index = 0; index < sampleFreqType; index++) {
+ writeListItem("" + sampleRates[index]);
+ }
+ closeList();
+ }
+
+ closeList();
+ }
+
+ private void tsReport(UsbASGeneral descriptor) {
+ tsReport((UsbACInterface) descriptor);
+ openList();
+ int formatTag = descriptor.getFormatTag();
+ writeListItem("fmt:" + UsbStrings.getAudioFormatName(formatTag) + " - "
+ + getHexString(formatTag));
+ closeList();
+ }
+
+ private void tsReport(UsbInterfaceAssoc descriptor) {
+ tsReport((UsbDescriptor) descriptor);
+ }
+
+ private void tsReport(UsbMSMidiHeader descriptor) {
+ writeHeader(3, "MS Midi Header:" + getHexString(descriptor.getType())
+ + " subType:" + getHexString(descriptor.getSubclass())
+ + " length:" + descriptor.getSubclass());
+ }
+
+ private void tsReport(UsbMSMidiInputJack descriptor) {
+ writeHeader(3, "MS Midi Input Jack:" + getHexString(descriptor.getType())
+ + " subType:" + getHexString(descriptor.getSubclass())
+ + " length:" + descriptor.getSubclass());
+ }
+
+ private void tsReport(UsbMSMidiOutputJack descriptor) {
+ writeHeader(3, "MS Midi Output Jack:" + getHexString(descriptor.getType())
+ + " subType:" + getHexString(descriptor.getSubclass())
+ + " length:" + descriptor.getSubclass());
+ }
+
+ private void tsReport(UsbUnknown descriptor) {
+ writeParagraph("<i><b>Unknown Descriptor " + getHexString(descriptor.getType())
+ + " len:" + descriptor.getLength() + "</b></i>");
+ dumpHexArray(descriptor.getRawData(), mStringBuilder);
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/report/Reporter.java b/services/usb/java/com/android/server/usb/descriptors/report/Reporter.java
new file mode 100644
index 0000000..2944c10
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/report/Reporter.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017 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.report;
+
+import com.android.server.usb.descriptors.UsbDescriptor;
+
+/**
+ * Declares the Reporter interface to provide HTML reporting for UsbDescriptor (sub)classes.
+ *
+ * NOTE: It is the responsibility of the implementor of this interface to correctly
+ * interpret/decode the SPECIFIC UsbDescriptor subclass (perhaps with 'instanceof') that is
+ * passed and handle that in the appropriate manner. This appears to be a
+ * not very object-oriented approach, and that is true. This approach DOES however move the
+ * complexity and 'plumbing' of reporting into the Reporter implementation and avoids needing
+ * a (trivial) type-specific call to 'report()' in each UsbDescriptor (sub)class, instead
+ * having just one in the top-level UsbDescriptor class. It also removes the need to add new
+ * type-specific 'report()' methods to be added to Reporter interface whenever a
+ * new UsbDescriptor subclass is defined. This seems like a pretty good trade-off.
+ *
+ * See HTMLReporter.java in this package for an example of type decoding.
+ */
+public interface Reporter {
+ /**
+ * Generate report for this UsbDescriptor descriptor
+ */
+ void report(UsbDescriptor descriptor);
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/report/Reporting.java b/services/usb/java/com/android/server/usb/descriptors/report/Reporting.java
new file mode 100644
index 0000000..c13111b
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/report/Reporting.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2017 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.report;
+
+/**
+ * Declares the interface for classes that provide reporting functionality.
+ * (This is the double-indirection aspect of the "Visitor" pattern.
+ */
+public interface Reporting {
+ /**
+ * Declares the report method that UsbDescriptor subclasses call.
+ */
+ void report(Reporter reporter);
+}
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
new file mode 100644
index 0000000..0461150
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/descriptors/report/UsbStrings.java
@@ -0,0 +1,312 @@
+/*
+ * Copyright (C) 2017 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.report;
+
+import com.android.server.usb.descriptors.UsbACInterface;
+import com.android.server.usb.descriptors.UsbDescriptor;
+import com.android.server.usb.descriptors.UsbTerminalTypes;
+
+import java.util.HashMap;
+
+/**
+ * @hide
+ * A class to provide human-readable strings for various USB constants.
+ */
+public class UsbStrings {
+ private static final String TAG = "UsbStrings";
+
+ private static HashMap<Byte, String> sDescriptorNames;
+ private static HashMap<Byte, String> sACControlInterfaceNames;
+ private static HashMap<Byte, String> sACStreamingInterfaceNames;
+ private static HashMap<Byte, String> sClassNames;
+ private static HashMap<Byte, String> sAudioSubclassNames;
+ private static HashMap<Integer, String> sAudioEncodingNames;
+ private static HashMap<Integer, String> sTerminalNames;
+
+ private static void initDescriptorNames() {
+ sDescriptorNames = new HashMap<Byte, String>();
+ sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_DEVICE, "Device");
+ sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_CONFIG, "Config");
+ sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_STRING, "String");
+ sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_INTERFACE, "Interface");
+ sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_ENDPOINT, "Endpoint");
+ sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_BOS, "BOS (whatever that means)");
+ sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_INTERFACEASSOC,
+ "Interface Association");
+ sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_CAPABILITY, "Capability");
+ 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_HUB, "Hub");
+ sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_SUPERSPEED_HUB, "Superspeed Hub");
+ sDescriptorNames.put(UsbDescriptor.DESCRIPTORTYPE_ENDPOINT_COMPANION,
+ "Endpoint Companion");
+ }
+
+ private static void initACControlInterfaceNames() {
+ sACControlInterfaceNames = new HashMap<Byte, String>();
+ sACControlInterfaceNames.put(UsbACInterface.ACI_UNDEFINED, "Undefined");
+ sACControlInterfaceNames.put(UsbACInterface.ACI_HEADER, "Header");
+ sACControlInterfaceNames.put(UsbACInterface.ACI_INPUT_TERMINAL, "Input Terminal");
+ sACControlInterfaceNames.put(UsbACInterface.ACI_OUTPUT_TERMINAL, "Output Terminal");
+ sACControlInterfaceNames.put(UsbACInterface.ACI_MIXER_UNIT, "Mixer Unit");
+ sACControlInterfaceNames.put(UsbACInterface.ACI_SELECTOR_UNIT, "Selector Unit");
+ sACControlInterfaceNames.put(UsbACInterface.ACI_FEATURE_UNIT, "Feature Unit");
+ sACControlInterfaceNames.put(UsbACInterface.ACI_PROCESSING_UNIT, "Processing Unit");
+ sACControlInterfaceNames.put(UsbACInterface.ACI_EXTENSION_UNIT, "Extension Unit");
+ }
+
+ private static void initACStreamingInterfaceNames() {
+ sACStreamingInterfaceNames = new HashMap<Byte, String>();
+ sACStreamingInterfaceNames.put(UsbACInterface.ASI_UNDEFINED, "Undefined");
+ sACStreamingInterfaceNames.put(UsbACInterface.ASI_GENERAL, "General");
+ sACStreamingInterfaceNames.put(UsbACInterface.ASI_FORMAT_TYPE, "Format Type");
+ sACStreamingInterfaceNames.put(UsbACInterface.ASI_FORMAT_SPECIFIC, "Format Specific");
+ }
+
+ private static void initClassNames() {
+ sClassNames = new HashMap<Byte, String>();
+ sClassNames.put(UsbDescriptor.CLASSID_DEVICE, "Device");
+ sClassNames.put(UsbDescriptor.CLASSID_AUDIO, "Audio");
+ sClassNames.put(UsbDescriptor.CLASSID_COM, "Communications");
+ sClassNames.put(UsbDescriptor.CLASSID_HID, "HID");
+ sClassNames.put(UsbDescriptor.CLASSID_PHYSICAL, "Physical");
+ sClassNames.put(UsbDescriptor.CLASSID_IMAGE, "Image");
+ sClassNames.put(UsbDescriptor.CLASSID_PRINTER, "Printer");
+ sClassNames.put(UsbDescriptor.CLASSID_STORAGE, "Storage");
+ sClassNames.put(UsbDescriptor.CLASSID_HUB, "Hub");
+ sClassNames.put(UsbDescriptor.CLASSID_CDC_CONTROL, "CDC Control");
+ sClassNames.put(UsbDescriptor.CLASSID_SMART_CARD, "Smart Card");
+ sClassNames.put(UsbDescriptor.CLASSID_SECURITY, "Security");
+ sClassNames.put(UsbDescriptor.CLASSID_VIDEO, "Video");
+ sClassNames.put(UsbDescriptor.CLASSID_HEALTHCARE, "Healthcare");
+ sClassNames.put(UsbDescriptor.CLASSID_AUDIOVIDEO, "Audio/Video");
+ sClassNames.put(UsbDescriptor.CLASSID_BILLBOARD, "Billboard");
+ sClassNames.put(UsbDescriptor.CLASSID_TYPECBRIDGE, "Type C Bridge");
+ sClassNames.put(UsbDescriptor.CLASSID_DIAGNOSTIC, "Diagnostic");
+ sClassNames.put(UsbDescriptor.CLASSID_WIRELESS, "Wireless");
+ sClassNames.put(UsbDescriptor.CLASSID_MISC, "Misc");
+ sClassNames.put(UsbDescriptor.CLASSID_APPSPECIFIC, "Application Specific");
+ sClassNames.put(UsbDescriptor.CLASSID_VENDSPECIFIC, "Vendor Specific");
+ }
+
+ private static void initAudioSubclassNames() {
+ sAudioSubclassNames = new HashMap<Byte, String>();
+ sAudioSubclassNames.put(UsbDescriptor.AUDIO_SUBCLASS_UNDEFINED, "Undefinded");
+ sAudioSubclassNames.put(UsbDescriptor.AUDIO_AUDIOCONTROL, "Audio Control");
+ sAudioSubclassNames.put(UsbDescriptor.AUDIO_AUDIOSTREAMING, "Audio Streaming");
+ sAudioSubclassNames.put(UsbDescriptor.AUDIO_MIDISTREAMING, "MIDI Streaming");
+ }
+
+ private static void initAudioEncodingNames() {
+ sAudioEncodingNames = new HashMap<Integer, String>();
+ sAudioEncodingNames.put(UsbACInterface.FORMAT_I_UNDEFINED, "Format I Undefined");
+ sAudioEncodingNames.put(UsbACInterface.FORMAT_I_PCM, "Format I PCM");
+ sAudioEncodingNames.put(UsbACInterface.FORMAT_I_PCM8, "Format I PCM8");
+ sAudioEncodingNames.put(UsbACInterface.FORMAT_I_IEEE_FLOAT, "Format I FLOAT");
+ sAudioEncodingNames.put(UsbACInterface.FORMAT_I_ALAW, "Format I ALAW");
+ sAudioEncodingNames.put(UsbACInterface.FORMAT_I_MULAW, "Format I MuLAW");
+ sAudioEncodingNames.put(UsbACInterface.FORMAT_II_UNDEFINED, "FORMAT_II Undefined");
+ sAudioEncodingNames.put(UsbACInterface.FORMAT_II_MPEG, "FORMAT_II MPEG");
+ sAudioEncodingNames.put(UsbACInterface.FORMAT_II_AC3, "FORMAT_II AC3");
+ sAudioEncodingNames.put(UsbACInterface.FORMAT_III_UNDEFINED, "FORMAT_III Undefined");
+ sAudioEncodingNames.put(UsbACInterface.FORMAT_III_IEC1937AC3, "FORMAT_III IEC1937 AC3");
+ sAudioEncodingNames.put(UsbACInterface.FORMAT_III_IEC1937_MPEG1_Layer1,
+ "FORMAT_III MPEG1 Layer 1");
+ sAudioEncodingNames.put(UsbACInterface.FORMAT_III_IEC1937_MPEG1_Layer2,
+ "FORMAT_III MPEG1 Layer 2");
+ sAudioEncodingNames.put(UsbACInterface.FORMAT_III_IEC1937_MPEG2_EXT,
+ "FORMAT_III MPEG2 EXT");
+ sAudioEncodingNames.put(UsbACInterface.FORMAT_III_IEC1937_MPEG2_Layer1LS,
+ "FORMAT_III MPEG2 Layer1LS");
+ }
+
+ private static void initTerminalNames() {
+ sTerminalNames = new HashMap<Integer, String>();
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_USB_STREAMING, "USB Streaming");
+
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_IN_UNDEFINED, "Undefined");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_IN_MIC, "Microphone");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_IN_DESKTOP_MIC, "Desktop Microphone");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_IN_PERSONAL_MIC,
+ "Personal (headset) Microphone");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_IN_OMNI_MIC, "Omni Microphone");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_IN_MIC_ARRAY, "Microphone Array");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_IN_PROC_MIC_ARRAY,
+ "Proecessing Microphone Array");
+
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_OUT_UNDEFINED, "Undefined");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_OUT_SPEAKER, "Speaker");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_OUT_HEADPHONES, "Headphones");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_OUT_HEADMOUNTED, "Head Mounted Speaker");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_OUT_DESKTOPSPEAKER, "Desktop Speaker");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_OUT_ROOMSPEAKER, "Room Speaker");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_OUT_COMSPEAKER, "Communications Speaker");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_OUT_LFSPEAKER, "Low Frequency Speaker");
+
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_BIDIR_UNDEFINED, "Undefined");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_BIDIR_HANDSET, "Handset");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_BIDIR_HEADSET, "Headset");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_BIDIR_SKRPHONE, "Speaker Phone");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_BIDIR_SKRPHONE_SUPRESS,
+ "Speaker Phone (echo supressing)");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_BIDIR_SKRPHONE_CANCEL,
+ "Speaker Phone (echo canceling)");
+
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_TELE_UNDEFINED, "Undefined");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_TELE_PHONELINE, "Phone Line");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_TELE_PHONE, "Telephone");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_TELE_DOWNLINEPHONE, "Down Line Phone");
+
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_EXTERN_UNDEFINED, "Undefined");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_EXTERN_ANALOG, "Analog Connector");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_EXTERN_DIGITAL, "Digital Connector");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_EXTERN_LINE, "Line Connector");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_EXTERN_LEGACY, "Legacy Audio Connector");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_EXTERN_SPIDF, "S/PIDF Interface");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_EXTERN_1394DA, "1394 Audio");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_EXTERN_1394DV, "1394 Audio/Video");
+
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_EMBED_UNDEFINED, "Undefined");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_EMBED_CALNOISE, "Calibration Nose");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_EMBED_EQNOISE, "EQ Noise");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_EMBED_CDPLAYER, "CD Player");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_EMBED_DAT, "DAT");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_EMBED_DCC, "DCC");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_EMBED_MINIDISK, "Mini Disk");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_EMBED_ANALOGTAPE, "Analog Tap");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_EMBED_PHONOGRAPH, "Phonograph");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_EMBED_VCRAUDIO, "VCR Audio");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_EMBED_VIDDISKAUDIO, "Video Disk Audio");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_EMBED_DVDAUDIO, "DVD Audio");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_EMBED_TVAUDIO, "TV Audio");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_EMBED_SATELLITEAUDIO, "Satellite Audio");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_EMBED_CABLEAUDIO, "Cable Tuner Audio");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_EMBED_DSSAUDIO, "DSS Audio");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_EMBED_RADIOTRANSMITTER, "Radio Transmitter");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_EMBED_MULTITRACK, "Multitrack Recorder");
+ sTerminalNames.put(UsbTerminalTypes.TERMINAL_EMBED_SYNTHESIZER, "Synthesizer");
+ }
+
+ /**
+ * Retrieves the terminal name for the specified terminal type ID.
+ */
+ public static String getTerminalName(int terminalType) {
+ String name = sTerminalNames.get(terminalType);
+ return name != null
+ ? name
+ : "Unknown Terminal Type 0x" + Integer.toHexString(terminalType);
+ }
+ /**
+ * Initializes string tables.
+ */
+ public static void allocUsbStrings() {
+ initDescriptorNames();
+ initACControlInterfaceNames();
+ initACStreamingInterfaceNames();
+ initClassNames();
+ initAudioSubclassNames();
+ initAudioEncodingNames();
+ initTerminalNames();
+ }
+
+ /**
+ * Initializes string tables.
+ */
+ public static void releaseUsbStrings() {
+ sDescriptorNames = null;
+ sACControlInterfaceNames = null;
+ sACStreamingInterfaceNames = null;
+ sClassNames = null;
+ sAudioSubclassNames = null;
+ sAudioEncodingNames = null;
+ sTerminalNames = null;
+ }
+
+ /**
+ * Retrieves the name for the specified descriptor ID.
+ */
+ public static String getDescriptorName(byte descriptorID) {
+ String name = sDescriptorNames.get(descriptorID);
+ int iDescriptorID = descriptorID & 0xFF;
+ return name != null
+ ? name
+ : "Unknown Descriptor [0x" + Integer.toHexString(iDescriptorID)
+ + ":" + iDescriptorID + "]";
+ }
+
+ /**
+ * Retrieves the audio-class control interface name for the specified audio-class subtype.
+ */
+ public static String getACControlInterfaceName(byte subtype) {
+ String name = sACControlInterfaceNames.get(subtype);
+ int iSubType = subtype & 0xFF;
+ return name != null
+ ? name
+ : "Unknown subtype [0x" + Integer.toHexString(iSubType)
+ + ":" + iSubType + "]";
+ }
+
+ /**
+ * Retrieves the audio-class streaming interface name for the specified audio-class subtype.
+ */
+ public static String getACStreamingInterfaceName(byte subtype) {
+ String name = sACStreamingInterfaceNames.get(subtype);
+ int iSubType = subtype & 0xFF;
+ return name != null
+ ? name
+ : "Unknown Subtype [0x" + Integer.toHexString(iSubType) + ":"
+ + iSubType + "]";
+ }
+
+ /**
+ * Retrieves the name for the specified USB class ID.
+ */
+ public static String getClassName(byte classID) {
+ String name = sClassNames.get(classID);
+ int iClassID = classID & 0xFF;
+ return name != null
+ ? name
+ : "Unknown Class ID [0x" + Integer.toHexString(iClassID) + ":"
+ + iClassID + "]";
+ }
+
+ /**
+ * Retrieves the name for the specified USB audio subclass ID.
+ */
+ public static String getAudioSubclassName(byte subClassID) {
+ String name = sAudioSubclassNames.get(subClassID);
+ int iSubclassID = subClassID & 0xFF;
+ return name != null
+ ? name
+ : "Unknown Audio Subclass [0x" + Integer.toHexString(iSubclassID) + ":"
+ + iSubclassID + "]";
+ }
+
+ /**
+ * Retrieves the name for the specified USB audio format ID.
+ */
+ public static String getAudioFormatName(int formatID) {
+ String name = sAudioEncodingNames.get(formatID);
+ return name != null
+ ? name
+ : "Unknown Format (encoding) ID [0x" + Integer.toHexString(formatID) + ":"
+ + formatID + "]";
+ }
+}