Merge "Adding USB Headset awareness." into oc-dr1-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
f56b57781e
@@ -33,6 +33,7 @@ LOCAL_SRC_FILES += \
|
||||
$(LOCAL_REL_DIR)/com_android_server_tv_TvInputHal.cpp \
|
||||
$(LOCAL_REL_DIR)/com_android_server_vr_VrManagerService.cpp \
|
||||
$(LOCAL_REL_DIR)/com_android_server_UsbDeviceManager.cpp \
|
||||
$(LOCAL_REL_DIR)/com_android_server_UsbDescriptorParser.cpp \
|
||||
$(LOCAL_REL_DIR)/com_android_server_UsbMidiDevice.cpp \
|
||||
$(LOCAL_REL_DIR)/com_android_server_UsbHostManager.cpp \
|
||||
$(LOCAL_REL_DIR)/com_android_server_VibratorService.cpp \
|
||||
|
||||
62
services/core/jni/com_android_server_UsbDescriptorParser.cpp
Normal file
62
services/core/jni/com_android_server_UsbDescriptorParser.cpp
Normal 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"
|
||||
|
||||
|
||||
@@ -65,6 +65,9 @@ public final class UsbAlsaManager {
|
||||
private final HashMap<UsbDevice,UsbAudioDevice>
|
||||
mAudioDevices = new HashMap<UsbDevice,UsbAudioDevice>();
|
||||
|
||||
private boolean mIsInputHeadset; // as reported by UsbDescriptorParser
|
||||
private boolean mIsOutputHeadset; // as reported by UsbDescriptorParser
|
||||
|
||||
private final HashMap<UsbDevice,UsbMidiDevice>
|
||||
mMidiDevices = new HashMap<UsbDevice,UsbMidiDevice>();
|
||||
|
||||
@@ -184,9 +187,14 @@ public final class UsbAlsaManager {
|
||||
try {
|
||||
// Playback Device
|
||||
if (audioDevice.mHasPlayback) {
|
||||
int device = (audioDevice == mAccessoryAudioDevice ?
|
||||
AudioSystem.DEVICE_OUT_USB_ACCESSORY :
|
||||
AudioSystem.DEVICE_OUT_USB_DEVICE);
|
||||
int device;
|
||||
if (mIsOutputHeadset) {
|
||||
device = AudioSystem.DEVICE_OUT_USB_HEADSET;
|
||||
} else {
|
||||
device = (audioDevice == mAccessoryAudioDevice
|
||||
? AudioSystem.DEVICE_OUT_USB_ACCESSORY
|
||||
: AudioSystem.DEVICE_OUT_USB_DEVICE);
|
||||
}
|
||||
if (DEBUG) {
|
||||
Slog.i(TAG, "pre-call device:0x" + Integer.toHexString(device) +
|
||||
" addr:" + address + " name:" + audioDevice.getDeviceName());
|
||||
@@ -197,9 +205,14 @@ public final class UsbAlsaManager {
|
||||
|
||||
// Capture Device
|
||||
if (audioDevice.mHasCapture) {
|
||||
int device = (audioDevice == mAccessoryAudioDevice ?
|
||||
AudioSystem.DEVICE_IN_USB_ACCESSORY :
|
||||
AudioSystem.DEVICE_IN_USB_DEVICE);
|
||||
int device;
|
||||
if (mIsInputHeadset) {
|
||||
device = AudioSystem.DEVICE_IN_USB_HEADSET;
|
||||
} else {
|
||||
device = (audioDevice == mAccessoryAudioDevice
|
||||
? AudioSystem.DEVICE_IN_USB_ACCESSORY
|
||||
: AudioSystem.DEVICE_IN_USB_DEVICE);
|
||||
}
|
||||
mAudioService.setWiredDeviceConnectionState(
|
||||
device, state, address, audioDevice.getDeviceName(), TAG);
|
||||
}
|
||||
@@ -343,12 +356,16 @@ public final class UsbAlsaManager {
|
||||
return selectAudioCard(mCardsParser.getDefaultCard());
|
||||
}
|
||||
|
||||
/* package */ void usbDeviceAdded(UsbDevice usbDevice) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "deviceAdded(): " + usbDevice.getManufacturerName() +
|
||||
" nm:" + usbDevice.getProductName());
|
||||
/* package */ void usbDeviceAdded(UsbDevice usbDevice,
|
||||
boolean isInputHeadset, boolean isOutputHeadset) {
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, "deviceAdded(): " + usbDevice.getManufacturerName()
|
||||
+ " nm:" + usbDevice.getProductName());
|
||||
}
|
||||
|
||||
mIsInputHeadset = isInputHeadset;
|
||||
mIsOutputHeadset = isOutputHeadset;
|
||||
|
||||
// Is there an audio interface in there?
|
||||
boolean isAudioDevice = false;
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package com.android.server.usb;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
@@ -32,6 +31,7 @@ import android.util.Slog;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.util.IndentingPrintWriter;
|
||||
import com.android.server.usb.descriptors.UsbDescriptorParser;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
@@ -259,7 +259,14 @@ public class UsbHostManager {
|
||||
getCurrentUserSettings().deviceAttachedForFixedHandler(mNewDevice,
|
||||
usbDeviceConnectionHandler);
|
||||
}
|
||||
mUsbAlsaManager.usbDeviceAdded(mNewDevice);
|
||||
// deviceName is something like: "/dev/bus/usb/001/001"
|
||||
UsbDescriptorParser parser = new UsbDescriptorParser();
|
||||
if (parser.parseDevice(mNewDevice.getDeviceName())) {
|
||||
Slog.i(TAG, "---- isHeadset[in:" + parser.isInputHeadset()
|
||||
+ " , out:" + parser.isOutputHeadset() + "]");
|
||||
mUsbAlsaManager.usbDeviceAdded(mNewDevice,
|
||||
parser.isInputHeadset(), parser.isOutputHeadset());
|
||||
}
|
||||
} else {
|
||||
Slog.e(TAG, "mNewDevice is null in endUsbDeviceAdded");
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.server.usb.descriptors;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
* An audio class-specific Input Terminal interface.
|
||||
* see audio10.pdf section 4.3.2.1
|
||||
*/
|
||||
public class UsbACInputTerminal extends UsbACTerminal {
|
||||
private static final String TAG = "ACInputTerminal";
|
||||
|
||||
private byte mNrChannels; // 7:1 1 Channel (0x01)
|
||||
// Number of logical output channels in the
|
||||
// Terminal’s output audio channel cluster
|
||||
private int mChannelConfig; // 8:2 Mono (0x0000)
|
||||
private byte mChannelNames; // 10:1 Unused (0x00)
|
||||
private byte mTerminal; // 11:1 Unused (0x00)
|
||||
|
||||
public UsbACInputTerminal(int length, byte type, byte subtype, byte subclass) {
|
||||
super(length, type, subtype, subclass);
|
||||
}
|
||||
|
||||
public byte getNrChannels() {
|
||||
return mNrChannels;
|
||||
}
|
||||
|
||||
public int getChannelConfig() {
|
||||
return mChannelConfig;
|
||||
}
|
||||
|
||||
public byte getChannelNames() {
|
||||
return mChannelNames;
|
||||
}
|
||||
|
||||
public byte getTerminal() {
|
||||
return mTerminal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int parseRawDescriptors(ByteStream stream) {
|
||||
super.parseRawDescriptors(stream);
|
||||
|
||||
mNrChannels = stream.getByte();
|
||||
mChannelConfig = stream.unpackUsbWord();
|
||||
mChannelNames = stream.getByte();
|
||||
mTerminal = stream.getByte();
|
||||
|
||||
return mLength;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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>");
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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 + "]";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user