Merge "Adding USB Headset awareness." into oc-dr1-dev

This commit is contained in:
TreeHugger Robot
2017-06-29 19:25:09 +00:00
committed by Android (Google) Code Review
39 changed files with 3842 additions and 12 deletions

View File

@@ -33,6 +33,7 @@ LOCAL_SRC_FILES += \
$(LOCAL_REL_DIR)/com_android_server_tv_TvInputHal.cpp \ $(LOCAL_REL_DIR)/com_android_server_tv_TvInputHal.cpp \
$(LOCAL_REL_DIR)/com_android_server_vr_VrManagerService.cpp \ $(LOCAL_REL_DIR)/com_android_server_vr_VrManagerService.cpp \
$(LOCAL_REL_DIR)/com_android_server_UsbDeviceManager.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_UsbMidiDevice.cpp \
$(LOCAL_REL_DIR)/com_android_server_UsbHostManager.cpp \ $(LOCAL_REL_DIR)/com_android_server_UsbHostManager.cpp \
$(LOCAL_REL_DIR)/com_android_server_VibratorService.cpp \ $(LOCAL_REL_DIR)/com_android_server_VibratorService.cpp \

View File

@@ -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"

View File

@@ -65,6 +65,9 @@ public final class UsbAlsaManager {
private final HashMap<UsbDevice,UsbAudioDevice> private final HashMap<UsbDevice,UsbAudioDevice>
mAudioDevices = new 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> private final HashMap<UsbDevice,UsbMidiDevice>
mMidiDevices = new HashMap<UsbDevice,UsbMidiDevice>(); mMidiDevices = new HashMap<UsbDevice,UsbMidiDevice>();
@@ -184,9 +187,14 @@ public final class UsbAlsaManager {
try { try {
// Playback Device // Playback Device
if (audioDevice.mHasPlayback) { if (audioDevice.mHasPlayback) {
int device = (audioDevice == mAccessoryAudioDevice ? int device;
AudioSystem.DEVICE_OUT_USB_ACCESSORY : if (mIsOutputHeadset) {
AudioSystem.DEVICE_OUT_USB_DEVICE); device = AudioSystem.DEVICE_OUT_USB_HEADSET;
} else {
device = (audioDevice == mAccessoryAudioDevice
? AudioSystem.DEVICE_OUT_USB_ACCESSORY
: AudioSystem.DEVICE_OUT_USB_DEVICE);
}
if (DEBUG) { if (DEBUG) {
Slog.i(TAG, "pre-call device:0x" + Integer.toHexString(device) + Slog.i(TAG, "pre-call device:0x" + Integer.toHexString(device) +
" addr:" + address + " name:" + audioDevice.getDeviceName()); " addr:" + address + " name:" + audioDevice.getDeviceName());
@@ -197,9 +205,14 @@ public final class UsbAlsaManager {
// Capture Device // Capture Device
if (audioDevice.mHasCapture) { if (audioDevice.mHasCapture) {
int device = (audioDevice == mAccessoryAudioDevice ? int device;
AudioSystem.DEVICE_IN_USB_ACCESSORY : if (mIsInputHeadset) {
AudioSystem.DEVICE_IN_USB_DEVICE); device = AudioSystem.DEVICE_IN_USB_HEADSET;
} else {
device = (audioDevice == mAccessoryAudioDevice
? AudioSystem.DEVICE_IN_USB_ACCESSORY
: AudioSystem.DEVICE_IN_USB_DEVICE);
}
mAudioService.setWiredDeviceConnectionState( mAudioService.setWiredDeviceConnectionState(
device, state, address, audioDevice.getDeviceName(), TAG); device, state, address, audioDevice.getDeviceName(), TAG);
} }
@@ -343,12 +356,16 @@ public final class UsbAlsaManager {
return selectAudioCard(mCardsParser.getDefaultCard()); return selectAudioCard(mCardsParser.getDefaultCard());
} }
/* package */ void usbDeviceAdded(UsbDevice usbDevice) { /* package */ void usbDeviceAdded(UsbDevice usbDevice,
if (DEBUG) { boolean isInputHeadset, boolean isOutputHeadset) {
Slog.d(TAG, "deviceAdded(): " + usbDevice.getManufacturerName() + if (DEBUG) {
" nm:" + usbDevice.getProductName()); Slog.d(TAG, "deviceAdded(): " + usbDevice.getManufacturerName()
+ " nm:" + usbDevice.getProductName());
} }
mIsInputHeadset = isInputHeadset;
mIsOutputHeadset = isOutputHeadset;
// Is there an audio interface in there? // Is there an audio interface in there?
boolean isAudioDevice = false; boolean isAudioDevice = false;

View File

@@ -16,7 +16,6 @@
package com.android.server.usb; package com.android.server.usb;
import android.annotation.NonNull;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Context; import android.content.Context;
@@ -32,6 +31,7 @@ import android.util.Slog;
import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.IndentingPrintWriter;
import com.android.server.usb.descriptors.UsbDescriptorParser;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
@@ -259,7 +259,14 @@ public class UsbHostManager {
getCurrentUserSettings().deviceAttachedForFixedHandler(mNewDevice, getCurrentUserSettings().deviceAttachedForFixedHandler(mNewDevice,
usbDeviceConnectionHandler); 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 { } else {
Slog.e(TAG, "mNewDevice is null in endUsbDeviceAdded"); Slog.e(TAG, "mNewDevice is null in endUsbDeviceAdded");
} }

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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
// Terminals 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;
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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>");
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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 + "]";
}
}