Merge "Adding USB audio-class 2.0 spec descriptors." into oc-mr1-dev

This commit is contained in:
Paul Mclean
2017-08-30 22:13:36 +00:00
committed by Android (Google) Code Review
57 changed files with 2532 additions and 826 deletions

View File

@@ -15,7 +15,11 @@
*/
package com.android.server.usb.descriptors;
// Framework builds and Android Studio builds use different imports for NonNull.
// This one for Framework builds
import android.annotation.NonNull;
// this one in the AndroidStudio project
// import android.support.annotation.NonNull;
/**
* @hide
@@ -23,7 +27,7 @@ import android.annotation.NonNull;
* but with the capability to "back up" in situations where the parser discovers that a
* UsbDescriptor has overrun its length.
*/
public class ByteStream {
public final class ByteStream {
private static final String TAG = "ByteStream";
/** The byte array being wrapped */
@@ -103,6 +107,20 @@ public class ByteStream {
}
}
/**
* @return the next byte from the stream and advances the stream and the read count. Note
* that this is an unsigned byte encoded in a Java int.
* @throws IndexOutOfBoundsException
*/
public int getUnsignedByte() {
if (available() > 0) {
mReadCount++;
return mBytes[mIndex++] & 0x000000FF;
} 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
@@ -111,11 +129,11 @@ public class ByteStream {
* next 2 bytes in the stream.
* @throws IndexOutOfBoundsException
*/
public int unpackUsbWord() {
public int unpackUsbShort() {
if (available() >= 2) {
int b0 = getByte();
int b1 = getByte();
return ((b1 << 8) & 0x0000FF00) | (b0 & 0x000000FF);
int b0 = getUnsignedByte();
int b1 = getUnsignedByte();
return (b1 << 8) | b0;
} else {
throw new IndexOutOfBoundsException();
}
@@ -131,15 +149,31 @@ public class ByteStream {
*/
public int unpackUsbTriple() {
if (available() >= 3) {
int b0 = getByte();
int b1 = getByte();
int b2 = getByte();
return ((b2 << 16) & 0x00FF0000) | ((b1 << 8) & 0x0000FF00) | (b0 & 0x000000FF);
int b0 = getUnsignedByte();
int b1 = getUnsignedByte();
int b2 = getUnsignedByte();
return (b2 << 16) | (b1 << 8) | b0;
} else {
throw new IndexOutOfBoundsException();
}
}
/**
* Reads 4 bytes in *little endian format* from the stream and composes a 32-bit integer.
* @return The 32-bit integer encoded by the next 4 bytes in the stream.
* @throws IndexOutOfBoundsException
*/
public int unpackUsbInt() {
if (available() >= 4) {
int b0 = getUnsignedByte();
int b1 = getUnsignedByte();
int b2 = getUnsignedByte();
int b3 = getUnsignedByte();
return (b3 << 24) | (b2 << 16) | (b1 << 8) | b0;
} else {
throw new IndexOutOfBoundsException();
}
}
/**
* Advances the logical position in the stream. Affects the running count also.
* @param numBytes The number of bytes to advance.

View File

@@ -15,18 +15,16 @@
*/
package com.android.server.usb.descriptors;
import com.android.server.usb.descriptors.report.ReportCanvas;
/**
* @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";
public final class Usb10ACHeader extends UsbACHeaderInterface {
private static final String TAG = "Usb10ACHeader";
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
@@ -34,16 +32,8 @@ public class UsbACHeader extends UsbACInterface {
// 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 Usb10ACHeader(int length, byte type, byte subtype, byte subclass, int spec) {
super(length, type, subtype, subclass, spec);
}
public byte getNumInterfaces() {
@@ -60,9 +50,8 @@ public class UsbACHeader extends UsbACInterface {
@Override
public int parseRawDescriptors(ByteStream stream) {
mADCRelease = stream.unpackUsbWord();
mTotalLength = stream.unpackUsbWord();
mTotalLength = stream.unpackUsbShort();
if (mADCRelease >= 0x200) {
mControls = stream.getByte();
} else {
@@ -75,4 +64,30 @@ public class UsbACHeader extends UsbACInterface {
return mLength;
}
@Override
public void report(ReportCanvas canvas) {
super.report(canvas);
canvas.openList();
int numInterfaces = getNumInterfaces();
StringBuilder sb = new StringBuilder();
sb.append("" + numInterfaces + " Interfaces");
if (numInterfaces > 0) {
sb.append(" [");
byte[] interfaceNums = getInterfaceNums();
if (interfaceNums != null) {
for (int index = 0; index < numInterfaces; index++) {
sb.append("" + interfaceNums[index]);
if (index < numInterfaces - 1) {
sb.append(" ");
}
}
}
sb.append("]");
}
canvas.writeListItem(sb.toString());
canvas.writeListItem("Controls: " + ReportCanvas.getHexString(getControls()));
canvas.closeList();
}
}

View File

@@ -15,13 +15,15 @@
*/
package com.android.server.usb.descriptors;
import com.android.server.usb.descriptors.report.ReportCanvas;
/**
* @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";
public final class Usb10ACInputTerminal extends UsbACTerminal {
private static final String TAG = "Usb10ACInputTerminal";
private byte mNrChannels; // 7:1 1 Channel (0x01)
// Number of logical output channels in the
@@ -30,7 +32,7 @@ public class UsbACInputTerminal extends UsbACTerminal {
private byte mChannelNames; // 10:1 Unused (0x00)
private byte mTerminal; // 11:1 Unused (0x00)
public UsbACInputTerminal(int length, byte type, byte subtype, byte subclass) {
public Usb10ACInputTerminal(int length, byte type, byte subtype, byte subclass) {
super(length, type, subtype, subclass);
}
@@ -55,10 +57,22 @@ public class UsbACInputTerminal extends UsbACTerminal {
super.parseRawDescriptors(stream);
mNrChannels = stream.getByte();
mChannelConfig = stream.unpackUsbWord();
mChannelConfig = stream.unpackUsbShort();
mChannelNames = stream.getByte();
mTerminal = stream.getByte();
return mLength;
}
@Override
public void report(ReportCanvas canvas) {
super.report(canvas);
canvas.openList();
canvas.writeListItem("Associated Terminal: "
+ ReportCanvas.getHexString(getAssocTerminal()));
canvas.writeListItem("" + getNrChannels() + " Chans. Config: "
+ ReportCanvas.getHexString(getChannelConfig()));
canvas.closeList();
}
}

View File

@@ -0,0 +1,108 @@
/*
* 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 com.android.server.usb.descriptors.report.ReportCanvas;
/**
* @hide
* An audio class-specific Mixer Interface.
* see audio10.pdf section 4.3.2.3
*/
public final class Usb10ACMixerUnit extends UsbACMixerUnit {
private static final String TAG = "Usb10ACMixerUnit";
private int mChannelConfig; // Spatial 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 Usb10ACMixerUnit(int length, byte type, byte subtype, byte subClass) {
super(length, type, subtype, subClass);
}
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) {
super.parseRawDescriptors(stream);
mChannelConfig = stream.unpackUsbShort();
mChanNameID = stream.getByte();
int controlArraySize = calcControlArraySize(mNumInputs, mNumOutputs);
mControls = new byte[controlArraySize];
for (int index = 0; index < controlArraySize; index++) {
mControls[index] = stream.getByte();
}
mNameID = stream.getByte();
return mLength;
}
@Override
public void report(ReportCanvas canvas) {
super.report(canvas);
canvas.writeParagraph("Mixer Unit", false);
canvas.openList();
canvas.writeListItem("Unit ID: " + ReportCanvas.getHexString(getUnitID()));
byte numInputs = getNumInputs();
byte[] inputIDs = getInputIDs();
canvas.openListItem();
canvas.write("Num Inputs: " + numInputs + " [");
for (int input = 0; input < numInputs; input++) {
canvas.write("" + ReportCanvas.getHexString(inputIDs[input]));
if (input < numInputs - 1) {
canvas.write(" ");
}
}
canvas.write("]");
canvas.closeListItem();
canvas.writeListItem("Num Outputs: " + getNumOutputs());
canvas.writeListItem("Channel Config: " + ReportCanvas.getHexString(getChannelConfig()));
byte[] controls = getControls();
canvas.openListItem();
canvas.write("Controls: " + controls.length + " [");
for (int ctrl = 0; ctrl < controls.length; ctrl++) {
canvas.write("" + controls[ctrl]);
if (ctrl < controls.length - 1) {
canvas.write(" ");
}
}
canvas.write("]");
canvas.closeListItem();
canvas.closeList();
}
}

View File

@@ -15,18 +15,20 @@
*/
package com.android.server.usb.descriptors;
import com.android.server.usb.descriptors.report.ReportCanvas;
/**
* @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";
public final class Usb10ACOutputTerminal extends UsbACTerminal {
private static final String TAG = "Usb10ACOutputTerminal";
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) {
public Usb10ACOutputTerminal(int length, byte type, byte subtype, byte subClass) {
super(length, type, subtype, subClass);
}
@@ -46,4 +48,13 @@ public class UsbACOutputTerminal extends UsbACTerminal {
mTerminal = stream.getByte();
return mLength;
}
@Override
public void report(ReportCanvas canvas) {
super.report(canvas);
canvas.openList();
canvas.writeListItem("Source ID: " + ReportCanvas.getHexString(getSourceID()));
canvas.closeList();
}
}

View File

@@ -15,13 +15,15 @@
*/
package com.android.server.usb.descriptors;
import com.android.server.usb.descriptors.report.ReportCanvas;
/**
* @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";
public final class Usb10ASFormatI extends UsbASFormat {
private static final String TAG = "Usb10ASFormatI";
private byte mNumChannels; // 4:1
private byte mSubframeSize; // 5:1 frame size in bytes
@@ -31,7 +33,7 @@ public class UsbASFormatI extends UsbASFormat {
// 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) {
public Usb10ASFormatI(int length, byte type, byte subtype, byte formatType, byte subclass) {
super(length, type, subtype, formatType, subclass);
}
@@ -51,10 +53,23 @@ public class UsbASFormatI extends UsbASFormat {
return mSampleFreqType;
}
@Override
public int[] getSampleRates() {
return mSampleRates;
}
@Override
public int[] getBitDepths() {
int[] depths = {mBitResolution};
return depths;
}
@Override
public int[] getChannelCounts() {
int[] counts = {mNumChannels};
return counts;
}
@Override
public int parseRawDescriptors(ByteStream stream) {
mNumChannels = stream.getByte();
@@ -74,4 +89,28 @@ public class UsbASFormatI extends UsbASFormat {
return mLength;
}
@Override
public void report(ReportCanvas canvas) {
super.report(canvas);
canvas.openList();
canvas.writeListItem("" + getNumChannels() + " Channels.");
canvas.writeListItem("Subframe Size: " + getSubframeSize());
canvas.writeListItem("Bit Resolution: " + getBitResolution());
byte sampleFreqType = getSampleFreqType();
int[] sampleRates = getSampleRates();
canvas.writeListItem("Sample Freq Type: " + sampleFreqType);
canvas.openList();
if (sampleFreqType == 0) {
canvas.writeListItem("min: " + sampleRates[0]);
canvas.writeListItem("max: " + sampleRates[1]);
} else {
for (int index = 0; index < sampleFreqType; index++) {
canvas.writeListItem("" + sampleRates[index]);
}
}
canvas.closeList();
canvas.closeList();
}
}

View File

@@ -15,13 +15,15 @@
*/
package com.android.server.usb.descriptors;
import com.android.server.usb.descriptors.report.ReportCanvas;
/**
* @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";
public final class Usb10ASFormatII extends UsbASFormat {
private static final String TAG = "Usb10ASFormatII";
private int mMaxBitRate; // 4:2 Indicates the maximum number of bits per second this
// interface can handle. Expressed in kbits/s.
@@ -36,7 +38,7 @@ public class UsbASFormatII extends UsbASFormat {
// 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) {
public Usb10ASFormatII(int length, byte type, byte subtype, byte formatType, byte subclass) {
super(length, type, subtype, formatType, subclass);
}
@@ -58,8 +60,8 @@ public class UsbASFormatII extends UsbASFormat {
@Override
public int parseRawDescriptors(ByteStream stream) {
mMaxBitRate = stream.unpackUsbWord();
mSamplesPerFrame = stream.unpackUsbWord();
mMaxBitRate = stream.unpackUsbShort();
mSamplesPerFrame = stream.unpackUsbShort();
mSamFreqType = stream.getByte();
int numFreqs = mSamFreqType == 0 ? 2 : mSamFreqType;
mSampleRates = new int[numFreqs];
@@ -69,4 +71,29 @@ public class UsbASFormatII extends UsbASFormat {
return mLength;
}
@Override
public void report(ReportCanvas canvas) {
super.report(canvas);
canvas.openList();
canvas.writeListItem("Max Bit Rate: " + getMaxBitRate());
canvas.writeListItem("Samples Per Frame: " + getMaxBitRate());
byte sampleFreqType = getSamFreqType();
int[] sampleRates = getSampleRates();
canvas.writeListItem("Sample Freq Type: " + sampleFreqType);
canvas.openList();
if (sampleFreqType == 0) {
canvas.writeListItem("min: " + sampleRates[0]);
canvas.writeListItem("max: " + sampleRates[1]);
} else {
for (int index = 0; index < sampleFreqType; index++) {
canvas.writeListItem("" + sampleRates[index]);
}
}
canvas.closeList();
canvas.closeList();
}
}

View File

@@ -15,13 +15,16 @@
*/
package com.android.server.usb.descriptors;
import com.android.server.usb.descriptors.report.ReportCanvas;
import com.android.server.usb.descriptors.report.UsbStrings;
/**
* @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";
public final class Usb10ASGeneral extends UsbACInterface {
private static final String TAG = "Usb10ASGeneral";
// audio10.pdf - section 4.5.2
private byte mTerminalLink; // 3:1 The Terminal ID of the Terminal to which the endpoint
@@ -31,7 +34,7 @@ public class UsbASGeneral extends UsbACInterface {
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) {
public Usb10ASGeneral(int length, byte type, byte subtype, byte subclass) {
super(length, type, subtype, subclass);
}
@@ -51,8 +54,20 @@ public class UsbASGeneral extends UsbACInterface {
public int parseRawDescriptors(ByteStream stream) {
mTerminalLink = stream.getByte();
mDelay = stream.getByte();
mFormatTag = stream.unpackUsbWord();
mFormatTag = stream.unpackUsbShort();
return mLength;
}
@Override
public void report(ReportCanvas canvas) {
super.report(canvas);
canvas.openList();
canvas.writeListItem("Delay: " + mDelay);
canvas.writeListItem("Terminal Link: " + mTerminalLink);
canvas.writeListItem("Format: " + UsbStrings.getAudioFormatName(mFormatTag) + " - "
+ ReportCanvas.getHexString(mFormatTag));
canvas.closeList();
}
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.usb.descriptors;
import com.android.server.usb.descriptors.report.ReportCanvas;
/**
* @hide
* An audio class-specific Header descriptor.
* see Audio20.pdf section 4.7.2 Class-Specific AC Interface Descriptor
*/
public final class Usb20ACHeader extends UsbACHeaderInterface {
private static final String TAG = "Usb20ACHeader";
private byte mCategory; // 5:1 Constant, indicating the primary use of this audio function.
// See audio20.pdf Appendix A.7, “Audio Function Category Codes.”
private byte mControls; // 8:1 See audio20.pdf Table 4-5.
public Usb20ACHeader(int length, byte type, byte subtype, byte subclass, int spec) {
super(length, type, subtype, subclass, spec);
}
public byte getCategory() {
return mCategory;
}
public byte getControls() {
return mControls;
}
@Override
public int parseRawDescriptors(ByteStream stream) {
mCategory = stream.getByte();
mTotalLength = stream.unpackUsbShort();
mControls = stream.getByte();
return mLength;
}
@Override
public void report(ReportCanvas canvas) {
super.report(canvas);
canvas.openList();
canvas.writeListItem("Category: " + ReportCanvas.getHexString(getCategory()));
canvas.writeListItem("Controls: " + ReportCanvas.getHexString(getControls()));
canvas.closeList();
}
}

View File

@@ -0,0 +1,86 @@
/*
* 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 com.android.server.usb.descriptors.report.ReportCanvas;
/**
* @hide
* An audio class-specific Input Terminal interface.
* see Audio20.pdf section 3.13.2 Input Terminal
*/
public final class Usb20ACInputTerminal extends UsbACTerminal {
private static final String TAG = "Usb20ACInputTerminal";
// See Audio20.pdf - Table 4-9
// Always 17 bytes
private byte mClkSourceID; // 7:1 - ID of the Clock Entity to which this Input
// Terminal is connected.
private byte mNumChannels; // 8:1 - Number of logical output channels in the
// Terminals output audio channel cluster.
private int mChanConfig; // 9:4 - Describes the spatial location of the
// logical channels.
private byte mChanNames; // 13:1 - Index of a string descriptor, describing the
// name of the first logical channel.
private int mControls; // 14:2 - Bitmask (see Audio20.pdf Table 4-9)
private byte mTerminalName; // 16:1 - Index of a string descriptor, describing the
// Input Terminal.
public Usb20ACInputTerminal(int length, byte type, byte subtype, byte subclass) {
super(length, type, subtype, subclass);
}
public byte getClkSourceID() {
return mClkSourceID;
}
public byte getNumChannels() {
return mNumChannels;
}
public int getChanConfig() {
return mChanConfig;
}
public int getControls() {
return mControls;
}
@Override
public int parseRawDescriptors(ByteStream stream) {
super.parseRawDescriptors(stream);
mClkSourceID = stream.getByte();
mNumChannels = stream.getByte();
mChanConfig = stream.unpackUsbInt();
mChanNames = stream.getByte();
mControls = stream.unpackUsbShort();
mTerminalName = stream.getByte();
return mLength;
}
@Override
public void report(ReportCanvas canvas) {
super.report(canvas);
canvas.openList();
canvas.writeListItem("Clock Source: " + getClkSourceID());
canvas.writeListItem("" + getNumChannels() + " Channels. Config: "
+ ReportCanvas.getHexString(getChanConfig()));
canvas.closeList();
}
}

View File

@@ -0,0 +1,56 @@
/*
* 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 Unit interface.
* see Audio20.pdf section 4.7.2.6 Mixer Unit Descriptor
*/
public final class Usb20ACMixerUnit extends UsbACMixerUnit {
private static final String TAG = "Usb20ACMixerUnit";
private int mChanConfig; // 6+p:4 Describes the spatial location of the
// logical channels.
private byte mChanNames; // 10+p:1 Index of a string descriptor, describing the
// name of the first logical channel.
private byte[] mControls; // 11+p:N bitmasks of which controls are present for each channel
// for N, see UsbACMixerUnit.calcControlArraySize()
private byte mControlsMask; // 11+p+N:1 bitmasks of which controls are present for each channel
private byte mNameID; // 12+p+N:1 Index of a string descriptor, describing the
// Mixer Unit.
public Usb20ACMixerUnit(int length, byte type, byte subtype, byte subClass) {
super(length, type, subtype, subClass);
}
@Override
public int parseRawDescriptors(ByteStream stream) {
super.parseRawDescriptors(stream);
mChanConfig = stream.unpackUsbInt();
mChanNames = stream.getByte();
int controlArraySize = calcControlArraySize(mNumInputs, mNumOutputs);
mControls = new byte[controlArraySize];
for (int index = 0; index < controlArraySize; index++) {
mControls[index] = stream.getByte();
}
mControlsMask = stream.getByte();
mNameID = stream.getByte();
return mLength;
}
}

View File

@@ -0,0 +1,79 @@
/*
* 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 com.android.server.usb.descriptors.report.ReportCanvas;
/**
* @hide
* An audio class-specific Output Terminal interface.
* see Audio20.pdf section 3.13.3 Output Terminal
*/
public final class Usb20ACOutputTerminal extends UsbACTerminal {
private static final String TAG = "Usb20ACOutputTerminal";
// Audio20.pdf - section 4.7.2.5, Table 4-10
// Always 12 bytes
private byte mSourceID; // 7:1 - ID of the Unit or Terminal to which this
// Terminal is connected.
private byte mClkSoureID; // 8:1 - ID of the Clock Entity to which this Output
// Terminal is connected.
private int mControls; // 9:2 - see Audio20.pdf Table 4-10
private byte mTerminalID; // 11:1 - Index of a string descriptor, describing the
public Usb20ACOutputTerminal(int length, byte type, byte subtype, byte subClass) {
super(length, type, subtype, subClass);
}
public byte getSourceID() {
return mSourceID;
}
public byte getClkSourceID() {
return mClkSoureID;
}
public int getControls() {
return mControls;
}
public byte getTerminalID() {
return mTerminalID;
}
@Override
public int parseRawDescriptors(ByteStream stream) {
super.parseRawDescriptors(stream);
mSourceID = stream.getByte();
mClkSoureID = stream.getByte();
mControls = stream.unpackUsbShort();
mTerminalID = stream.getByte();
return mLength;
}
@Override
public void report(ReportCanvas canvas) {
super.report(canvas);
canvas.openList();
canvas.writeListItem("Clock Source ID: " + getClkSourceID());
canvas.writeListItem("Controls: " + ReportCanvas.getHexString(getControls()));
canvas.writeListItem("Terminal Name ID: " + getTerminalID());
canvas.closeList();
}
}

View File

@@ -0,0 +1,69 @@
/*
* 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 com.android.server.usb.descriptors.report.ReportCanvas;
/**
* @hide
* An audio class-specific Format I interface.
* see Frmts20.pdf section 2.3.1.6 Type I Format Type Descriptor
*/
public final class Usb20ASFormatI extends UsbASFormat {
private static final String TAG = "Usb20ASFormatI";
// Frmts20.pdf Table 2-2: Type I Format Type Descriptor
private byte mSubSlotSize; // 4:1 The number of bytes occupied by one
// audio subslot. Can be 1, 2, 3 or 4.
private byte mBitResolution; // 5:1 The number of effectively used bits from
// the available bits in an audio subslot.
public Usb20ASFormatI(int length, byte type, byte subtype, byte formatType, byte subclass) {
super(length, type, subtype, formatType, subclass);
}
/**
* TBD
*/
public byte getSubSlotSize() {
return mSubSlotSize;
}
/**
* TBD
*/
public byte getBitResolution() {
return mBitResolution;
}
@Override
public int parseRawDescriptors(ByteStream stream) {
mSubSlotSize = stream.getByte();
mBitResolution = stream.getByte();
return mLength;
}
@Override
public void report(ReportCanvas canvas) {
super.report(canvas);
canvas.openList();
canvas.writeListItem("Subslot Size: " + getSubSlotSize());
canvas.writeListItem("Bit Resolution: " + getBitResolution());
canvas.closeList();
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.usb.descriptors;
import com.android.server.usb.descriptors.report.ReportCanvas;
/**
* @hide
* An audio class-specific Format II interface.
* see Frmts20.pdf section 2.3.2.6 Type II Format Type Descriptor
*/
public final class Usb20ASFormatII extends UsbASFormat {
private static final String TAG = "Usb20ASFormatII";
// Frmts20.pdf Table 2-3: Type II Format Type Descriptor
private int mMaxBitRate; // 4:2 Indicates the maximum number of bits per
// second this interface can handle in kbits/s.
private int mSlotsPerFrame; // 6:2 Indicates the number of PCM audio slots
// contained in one encoded audio frame.
/**
* TBD
*/
public Usb20ASFormatII(int length, byte type, byte subtype, byte formatType, byte subclass) {
super(length, type, subtype, formatType, subclass);
}
/**
* TBD
*/
public int getmaxBitRate() {
return mMaxBitRate;
}
/**
* TBD
*/
public int getSlotsPerFrame() {
return mSlotsPerFrame;
}
@Override
public int parseRawDescriptors(ByteStream stream) {
mMaxBitRate = stream.unpackUsbShort();
mSlotsPerFrame = stream.unpackUsbShort();
return mLength;
}
@Override
public void report(ReportCanvas canvas) {
super.report(canvas);
canvas.openList();
canvas.writeListItem("Max Bit Rate: " + getmaxBitRate());
canvas.writeListItem("slots Per Frame: " + getSlotsPerFrame());
canvas.closeList();
}
}

View File

@@ -0,0 +1,78 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.usb.descriptors;
import com.android.server.usb.descriptors.report.ReportCanvas;
/**
* @hide
* An audio class-specific Format II interface.
* see Frmts20.pdf section 2.4.2.1 Extended Type II Format Type Descriptor
*/
public final class Usb20ASFormatIIEx extends UsbASFormat {
private static final String TAG = "Usb20ASFormatIIEx";
// Frmts20.pdf Table 2-7: Extended Type II Format Type Descriptor
private int mMaxBitRate; // 4:2 Indicates the maximum number of bits per
// second this interface can handle in kbits/s
private int mSamplesPerFrame; // 6:2 Indicates the number of PCM audio
// samples contained in one encoded audio frame.
private byte mHeaderLength; // 8:1 Size of the Packet Header, in bytes.
private byte mSidebandProtocol; // 9:1 Constant, identifying the Side Band
// Protocol used for the Packet Header content.
public Usb20ASFormatIIEx(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 getHeaderLength() {
return mHeaderLength;
}
public byte getSidebandProtocol() {
return mSidebandProtocol;
}
@Override
public int parseRawDescriptors(ByteStream stream) {
mMaxBitRate = stream.unpackUsbShort();
mSamplesPerFrame = stream.unpackUsbShort();
mHeaderLength = stream.getByte();
mSidebandProtocol = stream.getByte();
return mLength;
}
@Override
public void report(ReportCanvas canvas) {
super.report(canvas);
canvas.openList();
canvas.writeListItem("Max Bit Rate: " + getMaxBitRate());
canvas.writeListItem("Samples Per Frame: " + getSamplesPerFrame());
canvas.writeListItem("Header Length: " + getHeaderLength());
canvas.writeListItem("Sideband Protocol: " + getSidebandProtocol());
canvas.closeList();
}
}

View File

@@ -0,0 +1,63 @@
/*
* 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 com.android.server.usb.descriptors.report.ReportCanvas;
/**
* @hide
* An audio class-specific Format III interface.
* see Frmts20.pdf section 2.3.1.6 2.3.3.1 Type III Format Type Descriptor
*/
public final class Usb20ASFormatIII extends UsbASFormat {
private static final String TAG = "Usb20ASFormatIII";
// frmts20.pdf Table 2-4: Type III Format Type Descriptor
private byte mSubslotSize; // 4:1 The number of bytes occupied by one
// audio subslot. Must be set to two.
private byte mBitResolution; // 5:1 The number of effectively used bits from
// the available bits in an audio subframe.
public Usb20ASFormatIII(int length, byte type, byte subtype, byte formatType, byte subclass) {
super(length, type, subtype, formatType, subclass);
}
public byte getSubslotSize() {
return mSubslotSize;
}
public byte getBitResolution() {
return mBitResolution;
}
@Override
public int parseRawDescriptors(ByteStream stream) {
mSubslotSize = stream.getByte();
mBitResolution = stream.getByte();
return mLength;
}
@Override
public void report(ReportCanvas canvas) {
super.report(canvas);
canvas.openList();
canvas.writeListItem("Subslot Size: " + getSubslotSize());
canvas.writeListItem("Bit Resolution: " + getBitResolution());
canvas.closeList();
}
}

View File

@@ -0,0 +1,104 @@
/*
* 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 com.android.server.usb.descriptors.report.ReportCanvas;
/**
* Audio20.pdf - 4.9.2 Class-Specific AS Interface Descriptor
* 16 bytes
*/
public final class Usb20ASGeneral extends UsbACInterface {
private static final String TAG = "Usb20ASGeneral";
// Audio20.pdf - Table 4-27
private byte mTerminalLink; // 3:1 The Terminal ID of the Terminal to which
// this interface is connected.
private byte mControls; // 4:1 see audio20.pdf Table 4-27
private byte mFormatType; // 5:1 Constant identifying the Format Type the
// AudioStreaming interface is using.
private int mFormats; // 6:4 The Audio Data Format(s) that can be
// used to communicate with this interface.
// See the USB Audio Data Formats
// document for further details.
private byte mNumChannels; // 10:1 Number of physical channels in the AS
// Interface audio channel cluster.
private int mChannelConfig; // 11:4 Describes the spatial location of the
// physical channels.
private byte mChannelNames; // 15:1 Index of a string descriptor, describing the
// name of the first physical channel.
public Usb20ASGeneral(int length, byte type, byte subtype, byte subclass) {
super(length, type, subtype, subclass);
}
public byte getTerminalLink() {
return mTerminalLink;
}
public byte getControls() {
return mControls;
}
public byte getFormatType() {
return mFormatType;
}
public int getFormats() {
return mFormats;
}
public byte getNumChannels() {
return mNumChannels;
}
public int getChannelConfig() {
return mChannelConfig;
}
public byte getChannelNames() {
return mChannelNames;
}
@Override
public int parseRawDescriptors(ByteStream stream) {
mTerminalLink = stream.getByte();
mControls = stream.getByte();
mFormatType = stream.getByte();
mFormats = stream.unpackUsbInt();
mNumChannels = stream.getByte();
mChannelConfig = stream.unpackUsbInt();
mChannelNames = stream.getByte();
return mLength;
}
@Override
public void report(ReportCanvas canvas) {
super.report(canvas);
canvas.openList();
canvas.writeListItem("Terminal Link: " + getTerminalLink());
canvas.writeListItem("Controls: " + ReportCanvas.getHexString(getControls()));
canvas.writeListItem("Format Type: " + ReportCanvas.getHexString(getFormatType()));
canvas.writeListItem("Formats: " + ReportCanvas.getHexString(getFormats()));
canvas.writeListItem("Num Channels: " + getNumChannels());
canvas.writeListItem("Channel Config: " + ReportCanvas.getHexString(getChannelConfig()));
canvas.writeListItem("Channel Names String ID: " + getChannelNames());
canvas.closeList();
}
}

View File

@@ -21,7 +21,7 @@ package com.android.server.usb.descriptors;
* audio10.pdf section 4.4.2.1
*/
public class UsbACAudioControlEndpoint extends UsbACEndpoint {
private static final String TAG = "ACAudioControlEndpoint";
private static final String TAG = "UsbACAudioControlEndpoint";
private byte mAddress; // 2:1 The address of the endpoint on the USB device.
// D7: Direction. 1 = IN endpoint
@@ -64,7 +64,7 @@ public class UsbACAudioControlEndpoint extends UsbACEndpoint {
mAddress = stream.getByte();
mAttribs = stream.getByte();
mMaxPacketSize = stream.unpackUsbWord();
mMaxPacketSize = stream.unpackUsbShort();
mInterval = stream.getByte();
return mLength;

View File

@@ -21,7 +21,7 @@ package com.android.server.usb.descriptors;
* see audio10.pdf section 3.7.2
*/
public class UsbACAudioStreamEndpoint extends UsbACEndpoint {
private static final String TAG = "ACAudioStreamEndpoint";
private static final String TAG = "UsbACAudioStreamEndpoint";
//TODO data fields...
public UsbACAudioStreamEndpoint(int length, byte type, byte subclass) {

View File

@@ -23,7 +23,7 @@ import android.util.Log;
* see audio10.pdf section 4.4.1.2
*/
abstract class UsbACEndpoint extends UsbDescriptor {
private static final String TAG = "ACEndpoint";
private static final String TAG = "UsbACEndpoint";
protected final byte mSubclass; // from the mSubclass member of the "enclosing"
// Interface Descriptor, not the stream.
@@ -50,7 +50,7 @@ abstract class UsbACEndpoint extends UsbDescriptor {
}
public static UsbDescriptor allocDescriptor(UsbDescriptorParser parser,
int length, byte type) {
int length, byte type) {
UsbInterfaceDescriptor interfaceDesc = parser.getCurInterface();
byte subClass = interfaceDesc.getUsbSubclass();
switch (subClass) {

View File

@@ -20,8 +20,8 @@ package com.android.server.usb.descriptors;
* 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";
public final class UsbACFeatureUnit extends UsbACInterface {
private static final String TAG = "UsbACFeatureUnit";
// audio10.pdf section 4.3.2.5
public static final int CONTROL_MASK_MUTE = 0x0001;

View File

@@ -0,0 +1,56 @@
/*
* 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 com.android.server.usb.descriptors.report.ReportCanvas;
/**
* @hide
* An audio class-specific Interface Header super class.
* see audio10.pdf section 4.3.2 & Audio20.pdf section 4.7.2
*/
public abstract class UsbACHeaderInterface extends UsbACInterface {
private static final String TAG = "UsbACHeaderInterface";
protected int mADCRelease; // Audio Device Class Specification Release (BCD).
protected int mTotalLength; // 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.
public UsbACHeaderInterface(
int length, byte type, byte subtype, byte subclass, int adcRelease) {
super(length, type, subtype, subclass);
mADCRelease = adcRelease;
}
public int getADCRelease() {
return mADCRelease;
}
public int getTotalLength() {
return mTotalLength;
}
@Override
public void report(ReportCanvas canvas) {
super.report(canvas);
canvas.openList();
canvas.writeListItem("Release: " + ReportCanvas.getBCDString(getADCRelease()));
canvas.writeListItem("Total Length: " + getTotalLength());
canvas.closeList();
}
}

View File

@@ -17,13 +17,16 @@ package com.android.server.usb.descriptors;
import android.util.Log;
import com.android.server.usb.descriptors.report.ReportCanvas;
import com.android.server.usb.descriptors.report.UsbStrings;
/**
* @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";
private static final String TAG = "UsbACInterface";
// Audio Control Subtypes
public static final byte ACI_UNDEFINED = 0;
@@ -35,6 +38,11 @@ public abstract class UsbACInterface extends UsbDescriptor {
public static final byte ACI_FEATURE_UNIT = 6;
public static final byte ACI_PROCESSING_UNIT = 7;
public static final byte ACI_EXTENSION_UNIT = 8;
// Not handled yet
public static final byte ACI_CLOCK_SOURCE = 0x0A;
public static final byte ACI_CLOCK_SELECTOR = 0x0B;
public static final byte ACI_CLOCK_MULTIPLIER = 0x0C;
public static final byte ACI_SAMPLE_RATE_CONVERTER = 0x0D;
// Audio Streaming Subtypes
public static final byte ASI_UNDEFINED = 0;
@@ -87,17 +95,39 @@ public abstract class UsbACInterface extends UsbDescriptor {
return mSubclass;
}
private static UsbDescriptor allocAudioControlDescriptor(ByteStream stream,
int length, byte type, byte subtype, byte subClass) {
private static UsbDescriptor allocAudioControlDescriptor(UsbDescriptorParser parser,
ByteStream stream, int length, byte type, byte subtype, byte subClass) {
switch (subtype) {
case ACI_HEADER:
return new UsbACHeader(length, type, subtype, subClass);
{
int acInterfaceSpec = stream.unpackUsbShort();
parser.setACInterfaceSpec(acInterfaceSpec);
if (acInterfaceSpec == UsbDeviceDescriptor.USBSPEC_2_0) {
return new Usb20ACHeader(length, type, subtype, subClass, acInterfaceSpec);
} else {
return new Usb10ACHeader(length, type, subtype, subClass, acInterfaceSpec);
}
}
case ACI_INPUT_TERMINAL:
return new UsbACInputTerminal(length, type, subtype, subClass);
{
int acInterfaceSpec = parser.getACInterfaceSpec();
if (acInterfaceSpec == UsbDeviceDescriptor.USBSPEC_2_0) {
return new Usb20ACInputTerminal(length, type, subtype, subClass);
} else {
return new Usb10ACInputTerminal(length, type, subtype, subClass);
}
}
case ACI_OUTPUT_TERMINAL:
return new UsbACOutputTerminal(length, type, subtype, subClass);
{
int acInterfaceSpec = parser.getACInterfaceSpec();
if (acInterfaceSpec == UsbDeviceDescriptor.USBSPEC_2_0) {
return new Usb20ACOutputTerminal(length, type, subtype, subClass);
} else {
return new Usb10ACOutputTerminal(length, type, subtype, subClass);
}
}
case ACI_SELECTOR_UNIT:
return new UsbACSelectorUnit(length, type, subtype, subClass);
@@ -106,7 +136,14 @@ public abstract class UsbACInterface extends UsbDescriptor {
return new UsbACFeatureUnit(length, type, subtype, subClass);
case ACI_MIXER_UNIT:
return new UsbACMixerUnit(length, type, subtype, subClass);
{
int acInterfaceSpec = parser.getACInterfaceSpec();
if (acInterfaceSpec == UsbDeviceDescriptor.USBSPEC_2_0) {
return new Usb20ACMixerUnit(length, type, subtype, subClass);
} else {
return new Usb10ACMixerUnit(length, type, subtype, subClass);
}
}
case ACI_PROCESSING_UNIT:
case ACI_EXTENSION_UNIT:
@@ -115,18 +152,24 @@ public abstract class UsbACInterface extends UsbDescriptor {
default:
Log.w(TAG, "Unknown Audio Class Interface subtype:0x"
+ Integer.toHexString(subtype));
return null;
return new UsbACInterfaceUnparsed(length, type, subtype, subClass);
}
}
private static UsbDescriptor allocAudioStreamingDescriptor(ByteStream stream,
int length, byte type, byte subtype, byte subClass) {
private static UsbDescriptor allocAudioStreamingDescriptor(UsbDescriptorParser parser,
ByteStream stream, int length, byte type, byte subtype, byte subClass) {
//int spec = parser.getUsbSpec();
int acInterfaceSpec = parser.getACInterfaceSpec();
switch (subtype) {
case ASI_GENERAL:
return new UsbASGeneral(length, type, subtype, subClass);
if (acInterfaceSpec == UsbDeviceDescriptor.USBSPEC_2_0) {
return new Usb20ASGeneral(length, type, subtype, subClass);
} else {
return new Usb10ASGeneral(length, type, subtype, subClass);
}
case ASI_FORMAT_TYPE:
return UsbASFormat.allocDescriptor(stream, length, type, subtype, subClass);
return UsbASFormat.allocDescriptor(parser, stream, length, type, subtype, subClass);
case ASI_FORMAT_SPECIFIC:
case ASI_UNDEFINED:
@@ -155,7 +198,6 @@ public abstract class UsbACInterface extends UsbDescriptor {
// 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));
@@ -173,10 +215,12 @@ public abstract class UsbACInterface extends UsbDescriptor {
byte subClass = interfaceDesc.getUsbSubclass();
switch (subClass) {
case AUDIO_AUDIOCONTROL:
return allocAudioControlDescriptor(stream, length, type, subtype, subClass);
return allocAudioControlDescriptor(
parser, stream, length, type, subtype, subClass);
case AUDIO_AUDIOSTREAMING:
return allocAudioStreamingDescriptor(stream, length, type, subtype, subClass);
return allocAudioStreamingDescriptor(
parser, stream, length, type, subtype, subClass);
case AUDIO_MIDISTREAMING:
return allocMidiStreamingDescriptor(length, type, subtype, subClass);
@@ -187,4 +231,21 @@ public abstract class UsbACInterface extends UsbDescriptor {
return null;
}
}
@Override
public void report(ReportCanvas canvas) {
super.report(canvas);
byte subClass = getSubclass();
String subClassName = UsbStrings.getACInterfaceSubclassName(subClass);
byte subtype = getSubtype();
String subTypeName = UsbStrings.getACControlInterfaceName(subtype);
canvas.openList();
canvas.writeListItem("Subclass: " + ReportCanvas.getHexString(subClass)
+ " " + subClassName);
canvas.writeListItem("Subtype: " + ReportCanvas.getHexString(subtype) + " " + subTypeName);
canvas.closeList();
}
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.usb.descriptors;
/**
* @hide
* A holder class for as yet unparsed audio-class interfaces.
*/
public final class UsbACInterfaceUnparsed extends UsbACInterface {
private static final String TAG = "UsbACInterfaceUnparsed";
public UsbACInterfaceUnparsed(int length, byte type, byte subtype, byte subClass) {
super(length, type, subtype, subClass);
}
}

View File

@@ -15,13 +15,15 @@
*/
package com.android.server.usb.descriptors;
import com.android.server.usb.descriptors.report.ReportCanvas;
/**
* @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";
public final class UsbACMidiEndpoint extends UsbACEndpoint {
private static final String TAG = "UsbACMidiEndpoint";
private byte mNumJacks;
private byte[] mJackIds;
@@ -49,4 +51,15 @@ public class UsbACMidiEndpoint extends UsbACEndpoint {
}
return mLength;
}
}
@Override
public void report(ReportCanvas canvas) {
super.report(canvas);
canvas.writeHeader(3, "AC Midi Endpoint: " + ReportCanvas.getHexString(getType())
+ " Length: " + getLength());
canvas.openList();
canvas.writeListItem("" + getNumJacks() + " Jacks.");
canvas.closeList();
}
}

View File

@@ -15,23 +15,14 @@
*/
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 static final String TAG = "UsbACMixerUnit";
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
protected byte mUnitID; // 3:1
protected byte mNumInputs; // 4:1 Number of Input Pins of this Unit.
protected byte[] mInputIDs; // 5...:1 ID of the Unit or Terminal to which the Input Pins
// are connected.
protected byte mNumOutputs; // The number of output channels
public UsbACMixerUnit(int length, byte type, byte subtype, byte subClass) {
super(length, type, subtype, subClass);
@@ -53,20 +44,9 @@ public class UsbACMixerUnit extends UsbACInterface {
return mNumOutputs;
}
public int getChannelConfig() {
return mChannelConfig;
}
public byte getChanNameID() {
return mChanNameID;
}
public byte[] getControls() {
return mControls;
}
public byte getNameID() {
return mNameID;
protected static int calcControlArraySize(int numInputs, int numOutputs) {
int totalChannels = numInputs * numOutputs;
return (totalChannels + 7) / 8;
}
@Override
@@ -78,22 +58,6 @@ public class UsbACMixerUnit extends UsbACInterface {
mInputIDs[input] = stream.getByte();
}
mNumOutputs = stream.getByte();
mChannelConfig = stream.unpackUsbWord();
mChanNameID = stream.getByte();
int controlArraySize;
int totalChannels = mNumInputs * mNumOutputs;
if (totalChannels % 8 == 0) {
controlArraySize = totalChannels / 8;
} else {
controlArraySize = totalChannels / 8 + 1;
}
mControls = new byte[controlArraySize];
for (int index = 0; index < controlArraySize; index++) {
mControls[index] = stream.getByte();
}
mNameID = stream.getByte();
return mLength;
}

View File

@@ -15,13 +15,15 @@
*/
package com.android.server.usb.descriptors;
// import com.android.server.usb.descriptors.report.ReportCanvas;
/**
* @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";
public final class UsbACSelectorUnit extends UsbACInterface {
private static final String TAG = "UsbACSelectorUnit";
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.
@@ -62,4 +64,11 @@ public class UsbACSelectorUnit extends UsbACInterface {
return mLength;
}
// @Override
// public void report(ReportCanvas canvas) {
// super.report(canvas);
//
// //TODO
// }
}

View File

@@ -15,10 +15,15 @@
*/
package com.android.server.usb.descriptors;
import com.android.server.usb.descriptors.report.ReportCanvas;
import com.android.server.usb.descriptors.report.UsbStrings;
/**
* @hide
*/
public abstract class UsbACTerminal extends UsbACInterface {
private static final String TAG = "UsbACTerminal";
// 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)
@@ -46,9 +51,21 @@ public abstract class UsbACTerminal extends UsbACInterface {
@Override
public int parseRawDescriptors(ByteStream stream) {
mTerminalID = stream.getByte();
mTerminalType = stream.unpackUsbWord();
mTerminalType = stream.unpackUsbShort();
mAssocTerminal = stream.getByte();
return mLength;
}
@Override
public void report(ReportCanvas canvas) {
super.report(canvas);
canvas.openList();
int terminalType = getTerminalType();
canvas.writeListItem("Type: " + ReportCanvas.getHexString(terminalType) + ": "
+ UsbStrings.getTerminalName(terminalType));
canvas.writeListItem("ID: " + ReportCanvas.getHexString(getTerminalID()));
canvas.closeList();
}
}

View File

@@ -15,19 +15,30 @@
*/
package com.android.server.usb.descriptors;
import com.android.server.usb.descriptors.report.ReportCanvas;
import com.android.server.usb.descriptors.report.UsbStrings;
/**
* @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";
public class UsbASFormat extends UsbACInterface {
private static final String TAG = "UsbASFormat";
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 static final byte FORMAT_TYPE_I = 1;
public static final byte FORMAT_TYPE_II = 2;
// these showed up in USB 2.0
public static final byte FORMAT_TYPE_III = 3;
public static final byte FORMAT_TYPE_IV = 4;
// "extended" formats
public static final byte EXT_FORMAT_TYPE_I = (byte) 0x81;
public static final byte EXT_FORMAT_TYPE_II = (byte) 0x82;
public static final byte EXT_FORMAT_TYPE_III = (byte) 0x83;
public UsbASFormat(int length, byte type, byte subtype, byte formatType, byte mSubclass) {
super(length, type, subtype, mSubclass);
@@ -38,27 +49,59 @@ public abstract class UsbASFormat extends UsbACInterface {
return mFormatType;
}
public int[] getSampleRates() {
return null;
}
public int[] getBitDepths() {
return null;
}
public int[] getChannelCounts() {
return null;
}
/**
* 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,
public static UsbDescriptor allocDescriptor(UsbDescriptorParser parser,
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);
int acInterfaceSpec = parser.getACInterfaceSpec();
switch (formatType) {
case FORMAT_TYPE_I:
if (acInterfaceSpec == UsbDeviceDescriptor.USBSPEC_2_0) {
return new Usb20ASFormatI(length, type, subtype, formatType, subclass);
} else {
return new Usb10ASFormatI(length, type, subtype, formatType, subclass);
}
case FORMAT_TYPE_II:
if (acInterfaceSpec == UsbDeviceDescriptor.USBSPEC_2_0) {
return new Usb20ASFormatII(length, type, subtype, formatType, subclass);
} else {
return new Usb10ASFormatII(length, type, subtype, formatType, subclass);
}
// USB 2.0 Exclusive Format Types
case FORMAT_TYPE_III:
return new Usb20ASFormatIII(length, type, subtype, formatType, subclass);
case FORMAT_TYPE_IV:
//TODO - implement this type.
default:
return null;
return new UsbASFormat(length, type, subtype, formatType, subclass);
}
}
@Override
public void report(ReportCanvas canvas) {
super.report(canvas);
canvas.write(UsbStrings.getFormatName(getFormatType()));
}
}

View File

@@ -25,7 +25,7 @@ import com.android.server.usb.descriptors.report.UsbStrings;
* A class that just walks the descriptors and does a hex dump of the contained values.
* Usefull as a debugging tool.
*/
public class UsbBinaryParser {
public final class UsbBinaryParser {
private static final String TAG = "UsbBinaryParser";
private static final boolean LOGGING = false;
@@ -33,7 +33,7 @@ public class UsbBinaryParser {
// Log
if (LOGGING) {
Log.i(TAG, "l:" + length + " t:" + Integer.toHexString(type) + " "
Log.i(TAG, "l: " + length + " t: " + Integer.toHexString(type) + " "
+ UsbStrings.getDescriptorName(type));
StringBuilder sb = new StringBuilder();
for (int index = 2; index < length; index++) {
@@ -43,7 +43,7 @@ public class UsbBinaryParser {
} else {
// Screen Dump
builder.append("<p>");
builder.append("<b> l:" + length
builder.append("<b> l: " + length
+ " t:0x" + Integer.toHexString(type) + " "
+ UsbStrings.getDescriptorName(type) + "</b><br>");
for (int index = 2; index < length; index++) {

View File

@@ -15,13 +15,15 @@
*/
package com.android.server.usb.descriptors;
import com.android.server.usb.descriptors.report.ReportCanvas;
/**
* @hide
* An USB Config Descriptor.
* see usb11.pdf section 9.6.2
*/
public class UsbConfigDescriptor extends UsbDescriptor {
private static final String TAG = "Config";
public final class UsbConfigDescriptor extends UsbDescriptor {
private static final String TAG = "UsbConfigDescriptor";
private int mTotalLength; // 2:2 Total length in bytes of data returned
private byte mNumInterfaces; // 4:1 Number of Interfaces
@@ -35,6 +37,7 @@ public class UsbConfigDescriptor extends UsbDescriptor {
UsbConfigDescriptor(int length, byte type) {
super(length, type);
mHierarchyLevel = 2;
}
public int getTotalLength() {
@@ -63,7 +66,7 @@ public class UsbConfigDescriptor extends UsbDescriptor {
@Override
public int parseRawDescriptors(ByteStream stream) {
mTotalLength = stream.unpackUsbWord();
mTotalLength = stream.unpackUsbShort();
mNumInterfaces = stream.getByte();
mConfigValue = stream.getByte();
mConfigIndex = stream.getByte();
@@ -72,4 +75,15 @@ public class UsbConfigDescriptor extends UsbDescriptor {
return mLength;
}
@Override
public void report(ReportCanvas canvas) {
super.report(canvas);
canvas.openList();
canvas.writeListItem("Config # " + getConfigValue());
canvas.writeListItem(getNumInterfaces() + " Interfaces.");
canvas.writeListItem("Attributes: " + ReportCanvas.getHexString(getAttribs()));
canvas.closeList();
}
}

View File

@@ -19,6 +19,10 @@ import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDeviceConnection;
import android.util.Log;
import com.android.server.usb.descriptors.report.ReportCanvas;
import com.android.server.usb.descriptors.report.Reporting;
import com.android.server.usb.descriptors.report.UsbStrings;
/*
* Some notes about UsbDescriptor and its subclasses.
*
@@ -33,8 +37,10 @@ import android.util.Log;
* @hide
* Common superclass for all USB Descriptors.
*/
public abstract class UsbDescriptor {
private static final String TAG = "Descriptor";
public abstract class UsbDescriptor implements Reporting {
private static final String TAG = "UsbDescriptor";
protected int mHierarchyLevel;
protected final int mLength; // 0:1 bLength Number Size of the Descriptor in Bytes (18 bytes)
// we store this as an int because Java bytes are SIGNED.
@@ -50,11 +56,15 @@ public abstract class UsbDescriptor {
public static final int STATUS_PARSED_OK = 1;
public static final int STATUS_PARSED_UNDERRUN = 2;
public static final int STATUS_PARSED_OVERRUN = 3;
public static final int STATUS_PARSE_EXCEPTION = 4;
private int mStatus = STATUS_UNPARSED;
private static String[] sStatusStrings = {
"UNPARSED", "PARSED - OK", "PARSED - UNDERRUN", "PARSED - OVERRUN"};
private int mOverUnderRunCount;
// Descriptor Type IDs
public static final byte DESCRIPTORTYPE_DEVICE = 0x01; // 1
public static final byte DESCRIPTORTYPE_CONFIG = 0x02; // 2
@@ -147,6 +157,10 @@ public abstract class UsbDescriptor {
mStatus = status;
}
public int getOverUnderRunCount() {
return mOverUnderRunCount;
}
public String getStatusString() {
return sStatusStrings[mStatus];
}
@@ -165,14 +179,16 @@ public abstract class UsbDescriptor {
// Too cold...
stream.advance(mLength - bytesRead);
mStatus = STATUS_PARSED_UNDERRUN;
mOverUnderRunCount = mLength - bytesRead;
Log.w(TAG, "UNDERRUN t:0x" + Integer.toHexString(mType)
+ " r:" + bytesRead + " < l:" + mLength);
+ " r: " + bytesRead + " < l: " + mLength);
} else if (bytesRead > mLength) {
// Too hot...
stream.reverse(bytesRead - mLength);
mStatus = STATUS_PARSED_OVERRUN;
mOverUnderRunCount = bytesRead - mLength;
Log.w(TAG, "OVERRRUN t:0x" + Integer.toHexString(mType)
+ " r:" + bytesRead + " > l:" + mLength);
+ " r: " + bytesRead + " > l: " + mLength);
} else {
// Just right!
mStatus = STATUS_PARSED_OK;
@@ -220,4 +236,43 @@ public abstract class UsbDescriptor {
}
return usbStr;
}
private void reportParseStatus(ReportCanvas canvas) {
int status = getStatus();
switch (status) {
case UsbDescriptor.STATUS_PARSED_OK:
break; // no need to report
case UsbDescriptor.STATUS_UNPARSED:
case UsbDescriptor.STATUS_PARSED_UNDERRUN:
case UsbDescriptor.STATUS_PARSED_OVERRUN:
canvas.writeParagraph("status: " + getStatusString()
+ " [" + getOverUnderRunCount() + "]", true);
break;
}
}
@Override
public void report(ReportCanvas canvas) {
String descTypeStr = UsbStrings.getDescriptorName(getType());
String text = descTypeStr + ": " + ReportCanvas.getHexString(getType())
+ " Len: " + getLength();
if (mHierarchyLevel != 0) {
canvas.writeHeader(mHierarchyLevel, text);
} else {
canvas.writeParagraph(text, false);
}
if (getStatus() != STATUS_PARSED_OK) {
reportParseStatus(canvas);
}
}
@Override
public void shortReport(ReportCanvas canvas) {
String descTypeStr = UsbStrings.getDescriptorName(getType());
String text = descTypeStr + ": " + ReportCanvas.getHexString(getType())
+ " Len: " + getLength();
canvas.writeParagraph(text, false);
}
}

View File

@@ -23,8 +23,8 @@ import java.util.ArrayList;
* @hide
* Class for parsing a binary stream of USB Descriptors.
*/
public class UsbDescriptorParser {
private static final String TAG = "DescriptorParser";
public final class UsbDescriptorParser {
private static final String TAG = "UsbDescriptorParser";
// Descriptor Objects
private ArrayList<UsbDescriptor> mDescriptors = new ArrayList<UsbDescriptor>();
@@ -32,8 +32,34 @@ public class UsbDescriptorParser {
private UsbDeviceDescriptor mDeviceDescriptor;
private UsbInterfaceDescriptor mCurInterfaceDescriptor;
// The AudioClass spec implemented by the AudioClass Interfaces
// This may well be different than the overall USB Spec.
// Obtained from the first AudioClass Header descriptor.
private int mACInterfacesSpec = UsbDeviceDescriptor.USBSPEC_1_0;
public UsbDescriptorParser() {}
/**
* @return the USB Spec value associated with the Device descriptor for the
* descriptors stream being parsed.
*
* @throws IllegalArgumentException
*/
public int getUsbSpec() {
if (mDeviceDescriptor != null) {
return mDeviceDescriptor.getSpec();
} else {
throw new IllegalArgumentException();
}
}
public void setACInterfaceSpec(int spec) {
mACInterfacesSpec = spec;
}
public int getACInterfaceSpec() {
return mACInterfacesSpec;
}
/**
* The probability (as returned by getHeadsetProbability() at which we conclude
* the peripheral is a headset.
@@ -44,7 +70,7 @@ public class UsbDescriptorParser {
private UsbDescriptor allocDescriptor(ByteStream stream) {
stream.resetReadCount();
int length = (int) stream.getByte() & 0x000000FF;
int length = stream.getUnsignedByte();
byte type = stream.getByte();
UsbDescriptor descriptor = null;
@@ -99,7 +125,7 @@ public class UsbDescriptorParser {
if (descriptor == null) {
// Unknown Descriptor
Log.i(TAG, "Unknown Descriptor len:" + length + " type:0x"
Log.i(TAG, "Unknown Descriptor len: " + length + " type:0x"
+ Integer.toHexString(type));
descriptor = new UsbUnknown(length, type);
}
@@ -135,14 +161,15 @@ public class UsbDescriptorParser {
try {
descriptor.parseRawDescriptors(stream);
// Its OK to add the invalid descriptor as the postParse()
// routine will mark it as invalid.
mDescriptors.add(descriptor);
// Clean up
descriptor.postParse(stream);
} catch (Exception ex) {
Log.e(TAG, "Exception parsing USB descriptors.", ex);
// Clean up
descriptor.setStatus(UsbDescriptor.STATUS_PARSE_EXCEPTION);
} finally {
mDescriptors.add(descriptor);
}
}
}
@@ -197,7 +224,7 @@ public class UsbDescriptorParser {
list.add(descriptor);
}
} else {
Log.w(TAG, "Unrecognized Interface l:" + descriptor.getLength()
Log.w(TAG, "Unrecognized Interface l: " + descriptor.getLength()
+ " t:0x" + Integer.toHexString(descriptor.getType()));
}
}
@@ -220,7 +247,7 @@ public class UsbDescriptorParser {
list.add(descriptor);
}
} else {
Log.w(TAG, "Unrecognized Audio Interface l:" + descriptor.getLength()
Log.w(TAG, "Unrecognized Audio Interface l: " + descriptor.getLength()
+ " t:0x" + Integer.toHexString(descriptor.getType()));
}
}
@@ -251,7 +278,7 @@ public class UsbDescriptorParser {
return true;
}
} else {
Log.w(TAG, "Undefined Audio Class Interface l:" + descriptor.getLength()
Log.w(TAG, "Undefined Audio Class Interface l: " + descriptor.getLength()
+ " t:0x" + Integer.toHexString(descriptor.getType()));
}
}
@@ -274,8 +301,8 @@ public class UsbDescriptorParser {
acDescriptors = getACInterfaceDescriptors(UsbACInterface.ACI_INPUT_TERMINAL,
UsbACInterface.AUDIO_AUDIOCONTROL);
for (UsbDescriptor descriptor : acDescriptors) {
if (descriptor instanceof UsbACInputTerminal) {
UsbACInputTerminal inDescr = (UsbACInputTerminal) descriptor;
if (descriptor instanceof UsbACTerminal) {
UsbACTerminal inDescr = (UsbACTerminal) descriptor;
if (inDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_IN_MIC
|| inDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_BIDIR_HEADSET
|| inDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_BIDIR_UNDEFINED
@@ -284,7 +311,7 @@ public class UsbDescriptorParser {
break;
}
} else {
Log.w(TAG, "Undefined Audio Input terminal l:" + descriptor.getLength()
Log.w(TAG, "Undefined Audio Input terminal l: " + descriptor.getLength()
+ " t:0x" + Integer.toHexString(descriptor.getType()));
}
}
@@ -295,8 +322,8 @@ public class UsbDescriptorParser {
getACInterfaceDescriptors(UsbACInterface.ACI_OUTPUT_TERMINAL,
UsbACInterface.AUDIO_AUDIOCONTROL);
for (UsbDescriptor descriptor : acDescriptors) {
if (descriptor instanceof UsbACOutputTerminal) {
UsbACOutputTerminal outDescr = (UsbACOutputTerminal) descriptor;
if (descriptor instanceof UsbACTerminal) {
UsbACTerminal outDescr = (UsbACTerminal) descriptor;
if (outDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_OUT_SPEAKER
|| outDescr.getTerminalType()
== UsbTerminalTypes.TERMINAL_OUT_HEADPHONES
@@ -305,7 +332,7 @@ public class UsbDescriptorParser {
break;
}
} else {
Log.w(TAG, "Undefined Audio Output terminal l:" + descriptor.getLength()
Log.w(TAG, "Undefined Audio Output terminal l: " + descriptor.getLength()
+ " t:0x" + Integer.toHexString(descriptor.getType()));
}
}
@@ -328,6 +355,8 @@ public class UsbDescriptorParser {
* to count on the peripheral being a headset.
*/
public boolean isInputHeadset() {
// TEMP
Log.i(TAG, "---- isInputHeadset() prob:" + (getInputHeadsetProbability() * 100f) + "%");
return getInputHeadsetProbability() >= IN_HEADSET_TRIGGER;
}
@@ -348,8 +377,8 @@ public class UsbDescriptorParser {
getACInterfaceDescriptors(UsbACInterface.ACI_OUTPUT_TERMINAL,
UsbACInterface.AUDIO_AUDIOCONTROL);
for (UsbDescriptor descriptor : acDescriptors) {
if (descriptor instanceof UsbACOutputTerminal) {
UsbACOutputTerminal outDescr = (UsbACOutputTerminal) descriptor;
if (descriptor instanceof UsbACTerminal) {
UsbACTerminal outDescr = (UsbACTerminal) descriptor;
if (outDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_OUT_SPEAKER
|| outDescr.getTerminalType()
== UsbTerminalTypes.TERMINAL_OUT_HEADPHONES
@@ -358,7 +387,7 @@ public class UsbDescriptorParser {
break;
}
} else {
Log.w(TAG, "Undefined Audio Output terminal l:" + descriptor.getLength()
Log.w(TAG, "Undefined Audio Output terminal l: " + descriptor.getLength()
+ " t:0x" + Integer.toHexString(descriptor.getType()));
}
}
@@ -381,6 +410,8 @@ public class UsbDescriptorParser {
* to count on the peripheral being a headset.
*/
public boolean isOutputHeadset() {
// TEMP
Log.i(TAG, "---- isOutputHeadset() prob:" + (getOutputHeadsetProbability() * 100f) + "%");
return getOutputHeadsetProbability() >= OUT_HEADSET_TRIGGER;
}

View File

@@ -15,13 +15,20 @@
*/
package com.android.server.usb.descriptors;
import com.android.server.usb.descriptors.report.ReportCanvas;
import com.android.server.usb.descriptors.report.UsbStrings;
/**
* @hide
* A USB Device Descriptor.
* see usb11.pdf section 9.6.1
*/
/* public */ public class UsbDeviceDescriptor extends UsbDescriptor {
private static final String TAG = "Device";
public final class UsbDeviceDescriptor extends UsbDescriptor {
private static final String TAG = "UsbDeviceDescriptor";
public static final int USBSPEC_1_0 = 0x0100;
public static final int USBSPEC_1_1 = 0x0110;
public static final int USBSPEC_2_0 = 0x0200;
private int mSpec; // 2:2 bcdUSB 2 BCD USB Specification Number - BCD
private byte mDevClass; // 4:1 class code
@@ -39,6 +46,7 @@ package com.android.server.usb.descriptors;
UsbDeviceDescriptor(int length, byte type) {
super(length, type);
mHierarchyLevel = 1;
}
public int getSpec() {
@@ -91,14 +99,14 @@ package com.android.server.usb.descriptors;
@Override
public int parseRawDescriptors(ByteStream stream) {
mSpec = stream.unpackUsbWord();
mSpec = stream.unpackUsbShort();
mDevClass = stream.getByte();
mDevSubClass = stream.getByte();
mProtocol = stream.getByte();
mPacketSize = stream.getByte();
mVendorID = stream.unpackUsbWord();
mProductID = stream.unpackUsbWord();
mDeviceRelease = stream.unpackUsbWord();
mVendorID = stream.unpackUsbShort();
mProductID = stream.unpackUsbShort();
mDeviceRelease = stream.unpackUsbShort();
mMfgIndex = stream.getByte();
mProductIndex = stream.getByte();
mSerialNum = stream.getByte();
@@ -106,4 +114,35 @@ package com.android.server.usb.descriptors;
return mLength;
}
@Override
public void report(ReportCanvas canvas) {
super.report(canvas);
canvas.openList();
int spec = getSpec();
canvas.writeListItem("Spec: " + ReportCanvas.getBCDString(spec));
byte devClass = getDevClass();
String classStr = UsbStrings.getClassName(devClass);
byte devSubClass = getDevSubClass();
String subClasStr = UsbStrings.getClassName(devSubClass);
canvas.writeListItem("Class " + devClass + ": " + classStr + " Subclass"
+ devSubClass + ": " + subClasStr);
canvas.writeListItem("Vendor ID: " + getVendorID()
+ " Product ID: " + getProductID()
+ " Product Release: " + ReportCanvas.getBCDString(getDeviceRelease()));
byte mfgIndex = getMfgIndex();
String manufacturer =
UsbDescriptor.getUsbDescriptorString(canvas.getConnection(), mfgIndex);
byte productIndex = getProductIndex();
String product =
UsbDescriptor.getUsbDescriptorString(canvas.getConnection(), productIndex);
canvas.writeListItem("Manufacturer " + mfgIndex + ": " + manufacturer
+ " Product " + productIndex + ": " + product);
canvas.closeList();
}
}

View File

@@ -15,36 +15,38 @@
*/
package com.android.server.usb.descriptors;
import com.android.server.usb.descriptors.report.ReportCanvas;
/**
* @hide
* A Usb Endpoint Descriptor.
* see usb11.pdf section 9.6.4
*/
public class UsbEndpointDescriptor extends UsbDescriptor {
private static final String TAG = "EndPoint";
private static final String TAG = "UsbEndpointDescriptor";
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_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 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_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;
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.
@@ -76,6 +78,7 @@ public class UsbEndpointDescriptor extends UsbDescriptor {
public UsbEndpointDescriptor(int length, byte type) {
super(length, type);
mHierarchyLevel = 4;
}
public byte getEndpointAddress() {
@@ -106,7 +109,7 @@ public class UsbEndpointDescriptor extends UsbDescriptor {
public int parseRawDescriptors(ByteStream stream) {
mEndpointAddress = stream.getByte();
mAttributes = stream.getByte();
mPacketSize = stream.unpackUsbWord();
mPacketSize = stream.unpackUsbShort();
mInterval = stream.getByte();
if (mLength == 9) {
mRefresh = stream.getByte();
@@ -114,4 +117,76 @@ public class UsbEndpointDescriptor extends UsbDescriptor {
}
return mLength;
}
@Override
public void report(ReportCanvas canvas) {
super.report(canvas);
canvas.openList();
byte address = getEndpointAddress();
canvas.writeListItem("Address: "
+ ReportCanvas.getHexString(address & UsbEndpointDescriptor.MASK_ENDPOINT_ADDRESS)
+ ((address & UsbEndpointDescriptor.MASK_ENDPOINT_DIRECTION)
== UsbEndpointDescriptor.DIRECTION_OUTPUT ? " [out]" : " [in]"));
byte attributes = getAttributes();
canvas.openListItem();
canvas.write("Attributes: " + ReportCanvas.getHexString(attributes) + " ");
switch (attributes & UsbEndpointDescriptor.MASK_ATTRIBS_TRANSTYPE) {
case UsbEndpointDescriptor.TRANSTYPE_CONTROL:
canvas.write("Control");
break;
case UsbEndpointDescriptor.TRANSTYPE_ISO:
canvas.write("Iso");
break;
case UsbEndpointDescriptor.TRANSTYPE_BULK:
canvas.write("Bulk");
break;
case UsbEndpointDescriptor.TRANSTYPE_INTERRUPT:
canvas.write("Interrupt");
break;
}
canvas.closeListItem();
// These flags are only relevant for ISO transfer type
if ((attributes & UsbEndpointDescriptor.MASK_ATTRIBS_TRANSTYPE)
== UsbEndpointDescriptor.TRANSTYPE_ISO) {
canvas.openListItem();
canvas.write("Aync: ");
switch (attributes & UsbEndpointDescriptor.MASK_ATTRIBS_SYNCTYPE) {
case UsbEndpointDescriptor.SYNCTYPE_NONE:
canvas.write("NONE");
break;
case UsbEndpointDescriptor.SYNCTYPE_ASYNC:
canvas.write("ASYNC");
break;
case UsbEndpointDescriptor.SYNCTYPE_ADAPTSYNC:
canvas.write("ADAPTIVE ASYNC");
break;
}
canvas.closeListItem();
canvas.openListItem();
canvas.write("Useage: ");
switch (attributes & UsbEndpointDescriptor.MASK_ATTRIBS_USEAGE) {
case UsbEndpointDescriptor.USEAGE_DATA:
canvas.write("DATA");
break;
case UsbEndpointDescriptor.USEAGE_FEEDBACK:
canvas.write("FEEDBACK");
break;
case UsbEndpointDescriptor.USEAGE_EXPLICIT:
canvas.write("EXPLICIT FEEDBACK");
break;
case UsbEndpointDescriptor.USEAGE_RESERVED:
canvas.write("RESERVED");
break;
}
canvas.closeListItem();
}
canvas.writeListItem("Package Size: " + getPacketSize());
canvas.writeListItem("Interval: " + getInterval());
canvas.closeList();
}
}

View File

@@ -15,13 +15,15 @@
*/
package com.android.server.usb.descriptors;
import com.android.server.usb.descriptors.report.ReportCanvas;
/**
* @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";
public final class UsbHIDDescriptor extends UsbDescriptor {
private static final String TAG = "UsbHIDDescriptor";
private int mRelease; // 2:2 the HID Class Specification release.
private byte mCountryCode; // 4:1 country code of the localized hardware.
@@ -35,6 +37,7 @@ public class UsbHIDDescriptor extends UsbDescriptor {
public UsbHIDDescriptor(int length, byte type) {
super(length, type);
mHierarchyLevel = 3;
}
public int getRelease() {
@@ -59,12 +62,24 @@ public class UsbHIDDescriptor extends UsbDescriptor {
@Override
public int parseRawDescriptors(ByteStream stream) {
mRelease = stream.unpackUsbWord();
mRelease = stream.unpackUsbShort();
mCountryCode = stream.getByte();
mNumDescriptors = stream.getByte();
mDescriptorType = stream.getByte();
mDescriptorLen = stream.unpackUsbWord();
mDescriptorLen = stream.unpackUsbShort();
return mLength;
}
@Override
public void report(ReportCanvas canvas) {
super.report(canvas);
canvas.openList();
canvas.writeListItem("Spec: " + ReportCanvas.getBCDString(getRelease()));
canvas.writeListItem("Type: " + ReportCanvas.getBCDString(getDescriptorType()));
canvas.writeListItem("" + getNumDescriptors() + " Descriptors Len: "
+ getDescriptorLen());
canvas.closeList();
}
}

View File

@@ -15,14 +15,16 @@
*/
package com.android.server.usb.descriptors;
// import com.android.server.usb.descriptors.report.ReportCanvas;
/**
* @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";
public final class UsbInterfaceAssoc extends UsbDescriptor {
private static final String TAG = "UsbInterfaceAssoc";
private byte mFirstInterface;
private byte mInterfaceCount;
@@ -70,4 +72,11 @@ public class UsbInterfaceAssoc extends UsbDescriptor {
return mLength;
}
// TODO - Report fields
// @Override
// public void report(ReportCanvas canvas) {
// super.report(canvas);
//
// }
}

View File

@@ -15,13 +15,16 @@
*/
package com.android.server.usb.descriptors;
import com.android.server.usb.descriptors.report.ReportCanvas;
import com.android.server.usb.descriptors.report.UsbStrings;
/**
* @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";
private static final String TAG = "UsbInterfaceDescriptor";
protected byte mInterfaceNumber; // 2:1 Number of Interface
protected byte mAlternateSetting; // 3:1 Value used to select alternative setting
@@ -33,6 +36,7 @@ public class UsbInterfaceDescriptor extends UsbDescriptor {
UsbInterfaceDescriptor(int length, byte type) {
super(length, type);
mHierarchyLevel = 3;
}
@Override
@@ -75,4 +79,27 @@ public class UsbInterfaceDescriptor extends UsbDescriptor {
public byte getDescrIndex() {
return mDescrIndex;
}
@Override
public void report(ReportCanvas canvas) {
super.report(canvas);
byte usbClass = getUsbClass();
byte usbSubclass = getUsbSubclass();
byte protocol = getProtocol();
String className = UsbStrings.getClassName(usbClass);
String subclassName = "";
if (usbClass == UsbDescriptor.CLASSID_AUDIO) {
subclassName = UsbStrings.getAudioSubclassName(usbSubclass);
}
canvas.openList();
canvas.writeListItem("Interface #" + getInterfaceNumber());
canvas.writeListItem("Class: " + ReportCanvas.getHexString(usbClass) + ": " + className);
canvas.writeListItem("Subclass: "
+ ReportCanvas.getHexString(usbSubclass) + ": " + subclassName);
canvas.writeListItem("Protocol: " + protocol + ": " + ReportCanvas.getHexString(protocol));
canvas.writeListItem("Endpoints: " + getNumEndpoints());
canvas.closeList();
}
}

View File

@@ -15,13 +15,15 @@
*/
package com.android.server.usb.descriptors;
import com.android.server.usb.descriptors.report.ReportCanvas;
/**
* @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 final class UsbMSMidiHeader extends UsbACInterface {
private static final String TAG = "UsbMSMidiHeader";
public UsbMSMidiHeader(int length, byte type, byte subtype, byte subclass) {
super(length, type, subtype, subclass);
@@ -33,4 +35,13 @@ public class UsbMSMidiHeader extends UsbACInterface {
stream.advance(mLength - stream.getReadCount());
return mLength;
}
@Override
public void report(ReportCanvas canvas) {
super.report(canvas);
canvas.writeHeader(3, "MS Midi Header: " + ReportCanvas.getHexString(getType())
+ " SubType: " + ReportCanvas.getHexString(getSubclass())
+ " Length: " + getLength());
}
}

View File

@@ -15,13 +15,15 @@
*/
package com.android.server.usb.descriptors;
import com.android.server.usb.descriptors.report.ReportCanvas;
/**
* @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";
public final class UsbMSMidiInputJack extends UsbACInterface {
private static final String TAG = "UsbMSMidiInputJack";
UsbMSMidiInputJack(int length, byte type, byte subtype, byte subclass) {
super(length, type, subtype, subclass);
@@ -33,4 +35,13 @@ public class UsbMSMidiInputJack extends UsbACInterface {
stream.advance(mLength - stream.getReadCount());
return mLength;
}
@Override
public void report(ReportCanvas canvas) {
super.report(canvas);
canvas.writeHeader(3, "MS Midi Input Jack: " + ReportCanvas.getHexString(getType())
+ " SubType: " + ReportCanvas.getHexString(getSubclass())
+ " Length: " + getLength());
}
}

View File

@@ -15,13 +15,15 @@
*/
package com.android.server.usb.descriptors;
import com.android.server.usb.descriptors.report.ReportCanvas;
/**
* @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 final class UsbMSMidiOutputJack extends UsbACInterface {
private static final String TAG = "UsbMSMidiOutputJack";
public UsbMSMidiOutputJack(int length, byte type, byte subtype, byte subclass) {
super(length, type, subtype, subclass);
@@ -33,4 +35,13 @@ public class UsbMSMidiOutputJack extends UsbACInterface {
stream.advance(mLength - stream.getReadCount());
return mLength;
}
@Override
public void report(ReportCanvas canvas) {
super.report(canvas);
canvas.writeHeader(3, "MS Midi Output Jack: " + ReportCanvas.getHexString(getType())
+ " SubType: " + ReportCanvas.getHexString(getSubclass())
+ " Length: " + getLength());
}
}

View File

@@ -20,8 +20,8 @@ package com.android.server.usb.descriptors;
* A class for decoding information in Terminal Descriptors.
* see termt10.pdf
*/
public class UsbTerminalTypes {
private static final String TAG = "TerminalTypes";
public final class UsbTerminalTypes {
private static final String TAG = "UsbTerminalTypes";
// USB
public static final int TERMINAL_USB_STREAMING = 0x0101;

View File

@@ -19,8 +19,8 @@ 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 final class UsbUnknown extends UsbDescriptor {
static final String TAG = "UsbUnknown";
public UsbUnknown(int length, byte type) {
super(length, type);

View File

@@ -0,0 +1,97 @@
/*
* 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;
/**
* @hide
* A concrete implementation of ReportCanvas class which generates HTML.
*/
public final class HTMLReportCanvas extends ReportCanvas {
private static final String TAG = "HTMLReportCanvas";
private final StringBuilder mStringBuilder;
/**
* Constructor. Connects HTML output to the provided StringBuilder.
* @param connection The USB connection object used to retrieve strings
* from the USB device.
* @param stringBuilder Generated output gets written into this object.
*/
public HTMLReportCanvas(UsbDeviceConnection connection, StringBuilder stringBuilder) {
super(connection);
mStringBuilder = stringBuilder;
}
@Override
public void write(String text) {
mStringBuilder.append(text);
}
@Override
public void openHeader(int level) {
mStringBuilder.append("<h").append(level).append('>');
}
@Override
public void closeHeader(int level) {
mStringBuilder.append("</h").append(level).append('>');
}
// we can be cleverer (more clever?) with styles, but this will do for now.
@Override
public void openParagraph(boolean emphasis) {
if (emphasis) {
mStringBuilder.append("<p style=\"color:red\">");
} else {
mStringBuilder.append("<p>");
}
}
@Override
public void closeParagraph() {
mStringBuilder.append("</p>");
}
@Override
public void writeParagraph(String text, boolean inRed) {
openParagraph(inRed);
mStringBuilder.append(text);
closeParagraph();
}
@Override
public void openList() {
mStringBuilder.append("<ul>");
}
@Override
public void closeList() {
mStringBuilder.append("</ul>");
}
@Override
public void openListItem() {
mStringBuilder.append("<li>");
}
@Override
public void closeListItem() {
mStringBuilder.append("</li>");
}
}

View File

@@ -1,572 +0,0 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.usb.descriptors.report;
import android.hardware.usb.UsbDeviceConnection;
import com.android.server.usb.descriptors.UsbACAudioControlEndpoint;
import com.android.server.usb.descriptors.UsbACAudioStreamEndpoint;
import com.android.server.usb.descriptors.UsbACFeatureUnit;
import com.android.server.usb.descriptors.UsbACHeader;
import com.android.server.usb.descriptors.UsbACInputTerminal;
import com.android.server.usb.descriptors.UsbACInterface;
import com.android.server.usb.descriptors.UsbACMidiEndpoint;
import com.android.server.usb.descriptors.UsbACMixerUnit;
import com.android.server.usb.descriptors.UsbACOutputTerminal;
import com.android.server.usb.descriptors.UsbACSelectorUnit;
import com.android.server.usb.descriptors.UsbACTerminal;
import com.android.server.usb.descriptors.UsbASFormat;
import com.android.server.usb.descriptors.UsbASFormatI;
import com.android.server.usb.descriptors.UsbASFormatII;
import com.android.server.usb.descriptors.UsbASGeneral;
import com.android.server.usb.descriptors.UsbConfigDescriptor;
import com.android.server.usb.descriptors.UsbDescriptor;
import com.android.server.usb.descriptors.UsbDeviceDescriptor;
import com.android.server.usb.descriptors.UsbEndpointDescriptor;
import com.android.server.usb.descriptors.UsbHIDDescriptor;
import com.android.server.usb.descriptors.UsbInterfaceAssoc;
import com.android.server.usb.descriptors.UsbInterfaceDescriptor;
import com.android.server.usb.descriptors.UsbMSMidiHeader;
import com.android.server.usb.descriptors.UsbMSMidiInputJack;
import com.android.server.usb.descriptors.UsbMSMidiOutputJack;
import com.android.server.usb.descriptors.UsbUnknown;
/**
* Implements the Reporter inteface to provide HTML reporting for UsbDescriptor subclasses.
*/
public class HTMLReporter implements Reporter {
private final StringBuilder mStringBuilder;
private final UsbDeviceConnection mConnection;
public HTMLReporter(StringBuilder stringBuilder, UsbDeviceConnection connection) {
mStringBuilder = stringBuilder;
mConnection = connection;
}
/*
* HTML Helpers
*/
private void writeHeader(int level, String text) {
mStringBuilder
.append("<h").append(level).append('>')
.append(text)
.append("</h").append(level).append('>');
}
private void openParagraph() {
mStringBuilder.append("<p>");
}
private void closeParagraph() {
mStringBuilder.append("</p>");
}
private void writeParagraph(String text) {
openParagraph();
mStringBuilder.append(text);
closeParagraph();
}
private void openList() {
mStringBuilder.append("<ul>");
}
private void closeList() {
mStringBuilder.append("</ul>");
}
private void openListItem() {
mStringBuilder.append("<li>");
}
private void closeListItem() {
mStringBuilder.append("</li>");
}
private void writeListItem(String text) {
openListItem();
mStringBuilder.append(text);
closeListItem();
}
/*
* Data Formating Helpers
*/
private static String getHexString(byte value) {
return "0x" + Integer.toHexString(((int) value) & 0xFF).toUpperCase();
}
private static String getBCDString(int value) {
int major = value >> 8;
int minor = (value >> 4) & 0x0F;
int subminor = value & 0x0F;
return "" + major + "." + minor + subminor;
}
private static String getHexString(int value) {
int intValue = value & 0xFFFF;
return "0x" + Integer.toHexString(intValue).toUpperCase();
}
private void dumpHexArray(byte[] rawData, StringBuilder builder) {
if (rawData != null) {
// Assume the type and Length and perhaps sub-type have been displayed
openParagraph();
for (int index = 0; index < rawData.length; index++) {
builder.append(getHexString(rawData[index]) + " ");
}
closeParagraph();
}
}
/**
* Decode ACTUAL UsbDescriptor sub classes and call type-specific report methods.
*/
@Override
public void report(UsbDescriptor descriptor) {
if (descriptor instanceof UsbDeviceDescriptor) {
tsReport((UsbDeviceDescriptor) descriptor);
} else if (descriptor instanceof UsbConfigDescriptor) {
tsReport((UsbConfigDescriptor) descriptor);
} else if (descriptor instanceof UsbInterfaceDescriptor) {
tsReport((UsbInterfaceDescriptor) descriptor);
} else if (descriptor instanceof UsbEndpointDescriptor) {
tsReport((UsbEndpointDescriptor) descriptor);
} else if (descriptor instanceof UsbHIDDescriptor) {
tsReport((UsbHIDDescriptor) descriptor);
} else if (descriptor instanceof UsbACAudioControlEndpoint) {
tsReport((UsbACAudioControlEndpoint) descriptor);
} else if (descriptor instanceof UsbACAudioStreamEndpoint) {
tsReport((UsbACAudioStreamEndpoint) descriptor);
} else if (descriptor instanceof UsbACHeader) {
tsReport((UsbACHeader) descriptor);
} else if (descriptor instanceof UsbACFeatureUnit) {
tsReport((UsbACFeatureUnit) descriptor);
} else if (descriptor instanceof UsbACInputTerminal) {
tsReport((UsbACInputTerminal) descriptor);
} else if (descriptor instanceof UsbACOutputTerminal) {
tsReport((UsbACOutputTerminal) descriptor);
} else if (descriptor instanceof UsbACMidiEndpoint) {
tsReport((UsbACMidiEndpoint) descriptor);
} else if (descriptor instanceof UsbACMixerUnit) {
tsReport((UsbACMixerUnit) descriptor);
} else if (descriptor instanceof UsbACSelectorUnit) {
tsReport((UsbACSelectorUnit) descriptor);
} else if (descriptor instanceof UsbASFormatI) {
tsReport((UsbASFormatI) descriptor);
} else if (descriptor instanceof UsbASFormatII) {
tsReport((UsbASFormatII) descriptor);
} else if (descriptor instanceof UsbASFormat) {
tsReport((UsbASFormat) descriptor);
} else if (descriptor instanceof UsbASGeneral) {
tsReport((UsbASGeneral) descriptor);
} else if (descriptor instanceof UsbInterfaceAssoc) {
tsReport((UsbInterfaceAssoc) descriptor);
} else if (descriptor instanceof UsbMSMidiHeader) {
tsReport((UsbMSMidiHeader) descriptor);
} else if (descriptor instanceof UsbMSMidiInputJack) {
tsReport((UsbMSMidiInputJack) descriptor);
} else if (descriptor instanceof UsbMSMidiOutputJack) {
tsReport((UsbMSMidiOutputJack) descriptor);
} else if (descriptor instanceof UsbUnknown) {
tsReport((UsbUnknown) descriptor);
} else if (descriptor instanceof UsbACInterface) {
tsReport((UsbACInterface) descriptor);
} else if (descriptor instanceof UsbDescriptor) {
tsReport((UsbDescriptor) descriptor);
}
}
//
// Type-specific report() implementations
//
private void tsReport(UsbDescriptor descriptor) {
int length = descriptor.getLength();
byte type = descriptor.getType();
int status = descriptor.getStatus();
String descTypeStr = UsbStrings.getDescriptorName(type);
writeParagraph(descTypeStr + ":" + type + " l:" + length + " s:" + status);
}
private void tsReport(UsbDeviceDescriptor descriptor) {
writeHeader(1, "Device len:" + descriptor.getLength());
openList();
int spec = descriptor.getSpec();
writeListItem("spec:" + getBCDString(spec));
byte devClass = descriptor.getDevClass();
String classStr = UsbStrings.getClassName(devClass);
byte devSubClass = descriptor.getDevSubClass();
String subClasStr = UsbStrings.getClassName(devSubClass);
writeListItem("class " + devClass + ":" + classStr + " subclass"
+ devSubClass + ":" + subClasStr);
writeListItem("vendorID:" + descriptor.getVendorID()
+ " prodID:" + descriptor.getProductID()
+ " prodRel:" + getBCDString(descriptor.getDeviceRelease()));
byte mfgIndex = descriptor.getMfgIndex();
String manufacturer = UsbDescriptor.getUsbDescriptorString(mConnection, mfgIndex);
byte productIndex = descriptor.getProductIndex();
String product = UsbDescriptor.getUsbDescriptorString(mConnection, productIndex);
writeListItem("mfg " + mfgIndex + ":" + manufacturer
+ " prod " + productIndex + ":" + product);
closeList();
}
private void tsReport(UsbConfigDescriptor descriptor) {
writeHeader(2, "Config #" + descriptor.getConfigValue()
+ " len:" + descriptor.getLength());
openList();
writeListItem(descriptor.getNumInterfaces() + " interfaces.");
writeListItem("attribs:" + getHexString(descriptor.getAttribs()));
closeList();
}
private void tsReport(UsbInterfaceDescriptor descriptor) {
byte usbClass = descriptor.getUsbClass();
byte usbSubclass = descriptor.getUsbSubclass();
String descr = UsbStrings.getDescriptorName(descriptor.getType());
String className = UsbStrings.getClassName(usbClass);
String subclassName = "";
if (usbClass == UsbDescriptor.CLASSID_AUDIO) {
subclassName = UsbStrings.getAudioSubclassName(usbSubclass);
}
writeHeader(2, descr + " #" + descriptor.getInterfaceNumber()
+ " len:" + descriptor.getLength());
String descrStr =
UsbDescriptor.getUsbDescriptorString(mConnection, descriptor.getDescrIndex());
if (descrStr.length() > 0) {
mStringBuilder.append("<br>" + descrStr);
}
openList();
writeListItem("class " + getHexString(usbClass) + ":" + className
+ " subclass " + getHexString(usbSubclass) + ":" + subclassName);
writeListItem("" + descriptor.getNumEndpoints() + " endpoints");
closeList();
}
private void tsReport(UsbEndpointDescriptor descriptor) {
writeHeader(3, "Endpoint " + getHexString(descriptor.getType())
+ " len:" + descriptor.getLength());
openList();
byte address = descriptor.getEndpointAddress();
writeListItem("address:"
+ getHexString(address & UsbEndpointDescriptor.MASK_ENDPOINT_ADDRESS)
+ ((address & UsbEndpointDescriptor.MASK_ENDPOINT_DIRECTION)
== UsbEndpointDescriptor.DIRECTION_OUTPUT ? " [out]" : " [in]"));
byte attributes = descriptor.getAttributes();
openListItem();
mStringBuilder.append("attribs:" + getHexString(attributes) + " ");
switch (attributes & UsbEndpointDescriptor.MASK_ATTRIBS_TRANSTYPE) {
case UsbEndpointDescriptor.TRANSTYPE_CONTROL:
mStringBuilder.append("Control");
break;
case UsbEndpointDescriptor.TRANSTYPE_ISO:
mStringBuilder.append("Iso");
break;
case UsbEndpointDescriptor.TRANSTYPE_BULK:
mStringBuilder.append("Bulk");
break;
case UsbEndpointDescriptor.TRANSTYPE_INTERRUPT:
mStringBuilder.append("Interrupt");
break;
}
closeListItem();
// These flags are only relevant for ISO transfer type
if ((attributes & UsbEndpointDescriptor.MASK_ATTRIBS_TRANSTYPE)
== UsbEndpointDescriptor.TRANSTYPE_ISO) {
openListItem();
mStringBuilder.append("sync:");
switch (attributes & UsbEndpointDescriptor.MASK_ATTRIBS_SYNCTYPE) {
case UsbEndpointDescriptor.SYNCTYPE_NONE:
mStringBuilder.append("NONE");
break;
case UsbEndpointDescriptor.SYNCTYPE_ASYNC:
mStringBuilder.append("ASYNC");
break;
case UsbEndpointDescriptor.SYNCTYPE_ADAPTSYNC:
mStringBuilder.append("ADAPTIVE ASYNC");
break;
}
closeListItem();
openListItem();
mStringBuilder.append("useage:");
switch (attributes & UsbEndpointDescriptor.MASK_ATTRIBS_USEAGE) {
case UsbEndpointDescriptor.USEAGE_DATA:
mStringBuilder.append("DATA");
break;
case UsbEndpointDescriptor.USEAGE_FEEDBACK:
mStringBuilder.append("FEEDBACK");
break;
case UsbEndpointDescriptor.USEAGE_EXPLICIT:
mStringBuilder.append("EXPLICIT FEEDBACK");
break;
case UsbEndpointDescriptor.USEAGE_RESERVED:
mStringBuilder.append("RESERVED");
break;
}
closeListItem();
}
writeListItem("package size:" + descriptor.getPacketSize());
writeListItem("interval:" + descriptor.getInterval());
closeList();
}
private void tsReport(UsbHIDDescriptor descriptor) {
String descr = UsbStrings.getDescriptorName(descriptor.getType());
writeHeader(2, descr + " len:" + descriptor.getLength());
openList();
writeListItem("spec:" + getBCDString(descriptor.getRelease()));
writeListItem("type:" + getBCDString(descriptor.getDescriptorType()));
writeListItem("descriptor.getNumDescriptors() descriptors len:"
+ descriptor.getDescriptorLen());
closeList();
}
private void tsReport(UsbACAudioControlEndpoint descriptor) {
writeHeader(3, "AC Audio Control Endpoint:" + getHexString(descriptor.getType())
+ " length:" + descriptor.getLength());
}
private void tsReport(UsbACAudioStreamEndpoint descriptor) {
writeHeader(3, "AC Audio Streaming Endpoint:"
+ getHexString(descriptor.getType())
+ " length:" + descriptor.getLength());
}
private void tsReport(UsbACHeader descriptor) {
tsReport((UsbACInterface) descriptor);
openList();
writeListItem("spec:" + getBCDString(descriptor.getADCRelease()));
int numInterfaces = descriptor.getNumInterfaces();
writeListItem("" + numInterfaces + " interfaces");
if (numInterfaces > 0) {
openListItem();
mStringBuilder.append("[");
byte[] interfaceNums = descriptor.getInterfaceNums();
if (numInterfaces != 0 && interfaceNums != null) {
for (int index = 0; index < numInterfaces; index++) {
mStringBuilder.append("" + interfaceNums[index]);
if (index < numInterfaces - 1) {
mStringBuilder.append(" ");
}
}
}
mStringBuilder.append("]");
closeListItem();
}
writeListItem("controls:" + getHexString(descriptor.getControls()));
closeList();
}
private void tsReport(UsbACFeatureUnit descriptor) {
tsReport((UsbACInterface) descriptor);
}
private void tsReport(UsbACInterface descriptor) {
String subClassName =
descriptor.getSubclass() == UsbDescriptor.AUDIO_AUDIOCONTROL
? "AC Control"
: "AC Streaming";
byte subtype = descriptor.getSubtype();
String subTypeStr = UsbStrings.getACControlInterfaceName(subtype);
writeHeader(4, subClassName + " - " + getHexString(subtype)
+ ":" + subTypeStr + " len:" + descriptor.getLength());
}
private void tsReport(UsbACTerminal descriptor) {
tsReport((UsbACInterface) descriptor);
}
private void tsReport(UsbACInputTerminal descriptor) {
tsReport((UsbACTerminal) descriptor);
openList();
writeListItem("ID:" + getHexString(descriptor.getTerminalID()));
int terminalType = descriptor.getTerminalType();
writeListItem("Type:<b>" + getHexString(terminalType) + ":"
+ UsbStrings.getTerminalName(terminalType) + "</b>");
writeListItem("AssocTerminal:" + getHexString(descriptor.getAssocTerminal()));
writeListItem("" + descriptor.getNrChannels() + " chans. config:"
+ getHexString(descriptor.getChannelConfig()));
closeList();
}
private void tsReport(UsbACOutputTerminal descriptor) {
tsReport((UsbACTerminal) descriptor);
openList();
writeListItem("ID:" + getHexString(descriptor.getTerminalID()));
int terminalType = descriptor.getTerminalType();
writeListItem("Type:<b>" + getHexString(terminalType) + ":"
+ UsbStrings.getTerminalName(terminalType) + "</b>");
writeListItem("AssocTerminal:" + getHexString(descriptor.getAssocTerminal()));
writeListItem("Source:" + getHexString(descriptor.getSourceID()));
closeList();
}
private void tsReport(UsbACMidiEndpoint descriptor) {
writeHeader(3, "AC Midi Endpoint:" + getHexString(descriptor.getType())
+ " length:" + descriptor.getLength());
openList();
writeListItem("" + descriptor.getNumJacks() + " jacks.");
closeList();
}
private void tsReport(UsbACMixerUnit descriptor) {
tsReport((UsbACInterface) descriptor);
openList();
writeListItem("Unit ID:" + getHexString(descriptor.getUnitID()));
byte numInputs = descriptor.getNumInputs();
byte[] inputIDs = descriptor.getInputIDs();
openListItem();
mStringBuilder.append("Num Inputs:" + numInputs + " [");
for (int input = 0; input < numInputs; input++) {
mStringBuilder.append("" + getHexString(inputIDs[input]));
if (input < numInputs - 1) {
mStringBuilder.append(" ");
}
}
mStringBuilder.append("]");
closeListItem();
writeListItem("Num Outputs:" + descriptor.getNumOutputs());
writeListItem("Chan Config:" + getHexString(descriptor.getChannelConfig()));
byte[] controls = descriptor.getControls();
openListItem();
mStringBuilder.append("controls:" + controls.length + " [");
for (int ctrl = 0; ctrl < controls.length; ctrl++) {
mStringBuilder.append("" + controls[ctrl]);
if (ctrl < controls.length - 1) {
mStringBuilder.append(" ");
}
}
mStringBuilder.append("]");
closeListItem();
closeList();
// byte mChanNameID; // First channel name string descriptor ID
// byte mNameID; // string descriptor ID of mixer name
}
private void tsReport(UsbACSelectorUnit descriptor) {
tsReport((UsbACInterface) descriptor);
}
private void tsReport(UsbASFormat descriptor) {
writeHeader(4, "AC Streaming Format "
+ (descriptor.getFormatType() == UsbASFormat.FORMAT_TYPE_I ? "I" : "II")
+ " - " + getHexString(descriptor.getSubtype()) + ":"
+ " len:" + descriptor.getLength());
}
private void tsReport(UsbASFormatI descriptor) {
tsReport((UsbASFormat) descriptor);
openList();
writeListItem("chans:" + descriptor.getNumChannels());
writeListItem("subframe size:" + descriptor.getSubframeSize());
writeListItem("bit resolution:" + descriptor.getBitResolution());
byte sampleFreqType = descriptor.getSampleFreqType();
int[] sampleRates = descriptor.getSampleRates();
writeListItem("sample freq type:" + sampleFreqType);
if (sampleFreqType == 0) {
openList();
writeListItem("min:" + sampleRates[0]);
writeListItem("max:" + sampleRates[1]);
closeList();
} else {
openList();
for (int index = 0; index < sampleFreqType; index++) {
writeListItem("" + sampleRates[index]);
}
closeList();
}
closeList();
}
private void tsReport(UsbASFormatII descriptor) {
tsReport((UsbASFormat) descriptor);
openList();
writeListItem("max bit rate:" + descriptor.getMaxBitRate());
writeListItem("samples per frame:" + descriptor.getMaxBitRate());
byte sampleFreqType = descriptor.getSamFreqType();
int[] sampleRates = descriptor.getSampleRates();
writeListItem("sample freq type:" + sampleFreqType);
if (sampleFreqType == 0) {
openList();
writeListItem("min:" + sampleRates[0]);
writeListItem("max:" + sampleRates[1]);
closeList();
} else {
openList();
for (int index = 0; index < sampleFreqType; index++) {
writeListItem("" + sampleRates[index]);
}
closeList();
}
closeList();
}
private void tsReport(UsbASGeneral descriptor) {
tsReport((UsbACInterface) descriptor);
openList();
int formatTag = descriptor.getFormatTag();
writeListItem("fmt:" + UsbStrings.getAudioFormatName(formatTag) + " - "
+ getHexString(formatTag));
closeList();
}
private void tsReport(UsbInterfaceAssoc descriptor) {
tsReport((UsbDescriptor) descriptor);
}
private void tsReport(UsbMSMidiHeader descriptor) {
writeHeader(3, "MS Midi Header:" + getHexString(descriptor.getType())
+ " subType:" + getHexString(descriptor.getSubclass())
+ " length:" + descriptor.getSubclass());
}
private void tsReport(UsbMSMidiInputJack descriptor) {
writeHeader(3, "MS Midi Input Jack:" + getHexString(descriptor.getType())
+ " subType:" + getHexString(descriptor.getSubclass())
+ " length:" + descriptor.getSubclass());
}
private void tsReport(UsbMSMidiOutputJack descriptor) {
writeHeader(3, "MS Midi Output Jack:" + getHexString(descriptor.getType())
+ " subType:" + getHexString(descriptor.getSubclass())
+ " length:" + descriptor.getSubclass());
}
private void tsReport(UsbUnknown descriptor) {
writeParagraph("<i><b>Unknown Descriptor " + getHexString(descriptor.getType())
+ " len:" + descriptor.getLength() + "</b></i>");
dumpHexArray(descriptor.getRawData(), mStringBuilder);
}
}

View File

@@ -0,0 +1,174 @@
/*
* 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;
/**
* @hide
* Defines a class for generating report data in a variety of potential formats.
*/
public abstract class ReportCanvas {
private static final String TAG = "ReportCanvas";
private final UsbDeviceConnection mConnection;
/**
* Constructor.
* @param connection The USB connection object used to retrieve strings
* from the USB device.
*/
public ReportCanvas(UsbDeviceConnection connection) {
mConnection = connection;
}
/**
* @returns the UsbDeviceConnection member (mConnection).
*/
public UsbDeviceConnection getConnection() {
return mConnection;
}
/**
* Writes a plain string to the output.
*/
public abstract void write(String text);
/**
* Opens a "header" formatted section in the output.
* @param level Specifies the logical level of the header.
*/
public abstract void openHeader(int level);
/**
* Closes a "header" formatted section in the output.
* @param level Specifies the logical level of the header.
*/
public abstract void closeHeader(int level);
/**
* Writes a "header" formatted string to the output.
* @param level Specifies the logical level of the header.
* @param text Specifies the text to display in the header.
*/
public void writeHeader(int level, String text) {
openHeader(level);
write(text);
closeHeader(level);
}
/**
* Opens a paragraph construct in the output.
* @param emphasis Specifies whether the text in the paragraph should
* be displayed with "emphasis" formatting.
*/
public abstract void openParagraph(boolean emphasis);
/**
* Closes a paragraph construct in the output.
*/
public abstract void closeParagraph();
/**
* Writes a paragraph construct to the output.
* @param text The text to display with "paragraph" formatting.
* @param emphasis Specifies whether the text in the paragraph should
* be displayed with "emphasis" formatting.
*/
public abstract void writeParagraph(String text, boolean emphasis);
/**
* Opens a "list" formatted section in the output.
*/
public abstract void openList();
/**
* Closes a "list" formatted section in the output.
*/
public abstract void closeList();
/**
* Opens a "list item" formatted section in the output.
*/
public abstract void openListItem();
/**
* Closes a "list item" formatted section in the output.
*/
public abstract void closeListItem();
/**
* Writes a "list item" formatted section in the output.
* @param text Specifies the text of the list item.
*/
public void writeListItem(String text) {
openListItem();
write(text);
closeListItem();
}
/*
* Data Formating Helpers
*/
/**
* Generates a hex representation of the specified byte value.
* @param value The value to format.
*/
//TODO Look into renaming the "getHexString()" functions to be more
// representative of the types they handle.
public static String getHexString(byte value) {
return "0x" + Integer.toHexString(((int) value) & 0xFF).toUpperCase();
}
/**
* Generates a string representing a USB Binary-Coded Decimal value.
* @param valueBCD The value to format.
*/
public static String getBCDString(int valueBCD) {
int major = (valueBCD >> 8) & 0x0F;
int minor = (valueBCD >> 4) & 0x0F;
int subminor = valueBCD & 0x0F;
return "" + major + "." + minor + subminor;
}
/**
* Generates a hex representation of the specified 16-bit integer value.
* @param value The value to format.
*/
//TODO Look into renaming the "getHexString()" functions to be more
// representative of the types they handle.
public static String getHexString(int value) {
int intValue = value & 0xFFFF;
return "0x" + Integer.toHexString(intValue).toUpperCase();
}
/**
* Writes out the specified byte array to the provided StringBuilder.
* @param rawData The byte values.
* @param builder The StringBuilder to write text into.
*/
public void dumpHexArray(byte[] rawData, StringBuilder builder) {
if (rawData != null) {
// Assume the type and Length and perhaps sub-type have been displayed
openParagraph(false);
for (int index = 0; index < rawData.length; index++) {
builder.append(getHexString(rawData[index]) + " ");
}
closeParagraph();
}
}
}

View File

@@ -1,40 +0,0 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.usb.descriptors.report;
import com.android.server.usb.descriptors.UsbDescriptor;
/**
* Declares the Reporter interface to provide HTML reporting for UsbDescriptor (sub)classes.
*
* NOTE: It is the responsibility of the implementor of this interface to correctly
* interpret/decode the SPECIFIC UsbDescriptor subclass (perhaps with 'instanceof') that is
* passed and handle that in the appropriate manner. This appears to be a
* not very object-oriented approach, and that is true. This approach DOES however move the
* complexity and 'plumbing' of reporting into the Reporter implementation and avoids needing
* a (trivial) type-specific call to 'report()' in each UsbDescriptor (sub)class, instead
* having just one in the top-level UsbDescriptor class. It also removes the need to add new
* type-specific 'report()' methods to be added to Reporter interface whenever a
* new UsbDescriptor subclass is defined. This seems like a pretty good trade-off.
*
* See HTMLReporter.java in this package for an example of type decoding.
*/
public interface Reporter {
/**
* Generate report for this UsbDescriptor descriptor
*/
void report(UsbDescriptor descriptor);
}

View File

@@ -16,12 +16,16 @@
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.
* @hide
*/
public interface Reporting {
/**
* Declares the report method that UsbDescriptor subclasses call.
* TBD
*/
void report(Reporter reporter);
void report(ReportCanvas canvas);
/**
* TBD
*/
void shortReport(ReportCanvas canvas);
}

View File

@@ -0,0 +1,108 @@
/*
* 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;
/**
* @hide
* A concrete implementation of ReportCanvas class which generates "Plain Text" output.
*/
public final class TextReportCanvas extends ReportCanvas {
private static final String TAG = "TextReportCanvas";
private final StringBuilder mStringBuilder;
private int mListIndent;
private static final int LIST_INDENT_AMNT = 2;
/**
* Constructor. Connects plain-text output to the provided StringBuilder.
* @param connection The USB connection object used to retrieve strings
* from the USB device.
* @param stringBuilder Generated output gets written into this object.
*/
public TextReportCanvas(UsbDeviceConnection connection, StringBuilder stringBuilder) {
super(connection);
mStringBuilder = stringBuilder;
}
@Override
public void write(String text) {
mStringBuilder.append(text);
}
@Override
public void openHeader(int level) {
mStringBuilder.append("[" + level + " - ");
}
@Override
public void closeHeader(int level) {
mStringBuilder.append("]\n");
}
@Override
public void openParagraph(boolean inRed) {
}
@Override
public void closeParagraph() {
mStringBuilder.append("\n");
}
@Override
public void writeParagraph(String text, boolean inRed) {
openParagraph(inRed);
if (inRed) {
mStringBuilder.append("*" + text + "*");
} else {
mStringBuilder.append(text);
}
closeParagraph();
}
private void writeListIndent() {
for (int space = 0; space < mListIndent; space++) {
mStringBuilder.append(" ");
}
}
@Override
public void openList() {
mListIndent += LIST_INDENT_AMNT;
writeListIndent();
mStringBuilder.append("---->\n");
}
@Override
public void closeList() {
writeListIndent();
mListIndent -= LIST_INDENT_AMNT;
mStringBuilder.append("<----\n");
}
@Override
public void openListItem() {
writeListIndent();
mStringBuilder.append(" - ");
}
@Override
public void closeListItem() {
mStringBuilder.append("\n");
}
}

View File

@@ -16,6 +16,7 @@
package com.android.server.usb.descriptors.report;
import com.android.server.usb.descriptors.UsbACInterface;
import com.android.server.usb.descriptors.UsbASFormat;
import com.android.server.usb.descriptors.UsbDescriptor;
import com.android.server.usb.descriptors.UsbTerminalTypes;
@@ -25,7 +26,7 @@ import java.util.HashMap;
* @hide
* A class to provide human-readable strings for various USB constants.
*/
public class UsbStrings {
public final class UsbStrings {
private static final String TAG = "UsbStrings";
private static HashMap<Byte, String> sDescriptorNames;
@@ -35,6 +36,7 @@ public class UsbStrings {
private static HashMap<Byte, String> sAudioSubclassNames;
private static HashMap<Integer, String> sAudioEncodingNames;
private static HashMap<Integer, String> sTerminalNames;
private static HashMap<Integer, String> sFormatNames;
private static void initDescriptorNames() {
sDescriptorNames = new HashMap<Byte, String>();
@@ -70,6 +72,11 @@ public class UsbStrings {
sACControlInterfaceNames.put(UsbACInterface.ACI_FEATURE_UNIT, "Feature Unit");
sACControlInterfaceNames.put(UsbACInterface.ACI_PROCESSING_UNIT, "Processing Unit");
sACControlInterfaceNames.put(UsbACInterface.ACI_EXTENSION_UNIT, "Extension Unit");
sACControlInterfaceNames.put(UsbACInterface.ACI_CLOCK_SOURCE, "Clock Source");
sACControlInterfaceNames.put(UsbACInterface.ACI_CLOCK_SELECTOR, "Clock Selector");
sACControlInterfaceNames.put(UsbACInterface.ACI_CLOCK_MULTIPLIER, "Clock Multiplier");
sACControlInterfaceNames.put(UsbACInterface.ACI_SAMPLE_RATE_CONVERTER,
"Sample Rate Converter");
}
private static void initACStreamingInterfaceNames() {
@@ -213,6 +220,29 @@ public class UsbStrings {
? name
: "Unknown Terminal Type 0x" + Integer.toHexString(terminalType);
}
private static void initFormatNames() {
sFormatNames = new HashMap<Integer, String>();
sFormatNames.put((int) UsbASFormat.FORMAT_TYPE_I, "FORMAT_TYPE_I");
sFormatNames.put((int) UsbASFormat.FORMAT_TYPE_II, "FORMAT_TYPE_II");
sFormatNames.put((int) UsbASFormat.FORMAT_TYPE_III, "FORMAT_TYPE_III");
sFormatNames.put((int) UsbASFormat.FORMAT_TYPE_IV, "FORMAT_TYPE_IV");
sFormatNames.put((int) UsbASFormat.EXT_FORMAT_TYPE_I, "EXT_FORMAT_TYPE_I");
sFormatNames.put((int) UsbASFormat.EXT_FORMAT_TYPE_II, "EXT_FORMAT_TYPE_II");
sFormatNames.put((int) UsbASFormat.EXT_FORMAT_TYPE_III, "EXT_FORMAT_TYPE_III");
}
/**
* Retrieves the name for the specified format (encoding) type ID.
*/
public static String getFormatName(int format) {
String name = sFormatNames.get(format);
return name != null
? name
: "Unknown Format Type 0x" + Integer.toHexString(format);
}
/**
* Initializes string tables.
*/
@@ -224,10 +254,11 @@ public class UsbStrings {
initAudioSubclassNames();
initAudioEncodingNames();
initTerminalNames();
initFormatNames();
}
/**
* Initializes string tables.
* Deinitializes string tables.
*/
public static void releaseUsbStrings() {
sDescriptorNames = null;
@@ -309,4 +340,11 @@ public class UsbStrings {
: "Unknown Format (encoding) ID [0x" + Integer.toHexString(formatID) + ":"
+ formatID + "]";
}
/**
* Retrieves the name for the specified USB audio interface subclass ID.
*/
public static String getACInterfaceSubclassName(byte subClassID) {
return subClassID == UsbDescriptor.AUDIO_AUDIOCONTROL ? "AC Control" : "AC Streaming";
}
}

View File

@@ -0,0 +1,46 @@
/*
* 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.tree;
import com.android.server.usb.descriptors.UsbACInterface;
import com.android.server.usb.descriptors.report.ReportCanvas;
/**
* @hide
* A tree node containing some sort-of Audio Class Descriptor.
*/
public final class UsbDescriptorsACInterfaceNode extends UsbDescriptorsTreeNode {
private static final String TAG = "UsbDescriptorsACInterfaceNode";
private final UsbACInterface mACInterface;
/**
* Constructor.
* @param acInterface The Audio Class Inteface object wrapped by this tree node.
*/
public UsbDescriptorsACInterfaceNode(UsbACInterface acInterface) {
mACInterface = acInterface;
}
@Override
public void report(ReportCanvas canvas) {
canvas.openListItem();
canvas.writeParagraph("AC Interface type:0x"
+ Integer.toHexString(mACInterface.getSubtype()), false);
mACInterface.report(canvas);
canvas.closeListItem();
}
}

View File

@@ -0,0 +1,63 @@
/*
* 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.tree;
import com.android.server.usb.descriptors.UsbConfigDescriptor;
import com.android.server.usb.descriptors.report.ReportCanvas;
import java.util.ArrayList;
/**
* @hide
* Represents a configuration in the descriptors tree.
*/
public final class UsbDescriptorsConfigNode extends UsbDescriptorsTreeNode {
private static final String TAG = "UsbDescriptorsConfigNode";
private final UsbConfigDescriptor mConfigDescriptor;
private final ArrayList<UsbDescriptorsInterfaceNode> mInterfaceNodes = new ArrayList<>();
/**
* Constructor.
* @param configDescriptor The Config Descriptor object wrapped by this tree node.
*/
public UsbDescriptorsConfigNode(UsbConfigDescriptor configDescriptor) {
mConfigDescriptor = configDescriptor;
}
/**
* Adds the inteface node logical contained in this configuration.
* @param interfaceNode The inteface treenode to assocate with this configuration.
*/
public void addInterfaceNode(UsbDescriptorsInterfaceNode interfaceNode) {
mInterfaceNodes.add(interfaceNode);
}
@Override
public void report(ReportCanvas canvas) {
mConfigDescriptor.report(canvas);
canvas.openList();
// Interfaces
for (UsbDescriptorsInterfaceNode node : mInterfaceNodes) {
node.report(canvas);
}
canvas.closeList();
}
}

View File

@@ -0,0 +1,56 @@
/*
* 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.tree;
import com.android.server.usb.descriptors.UsbDeviceDescriptor;
import com.android.server.usb.descriptors.report.ReportCanvas;
import java.util.ArrayList;
/**
* @hide
* A class to contain THE device descriptor at the root of the tree.
*/
public final class UsbDescriptorsDeviceNode extends UsbDescriptorsTreeNode {
private static final String TAG = "UsbDescriptorsDeviceNode";
private final UsbDeviceDescriptor mDeviceDescriptor;
private final ArrayList<UsbDescriptorsConfigNode> mConfigNodes = new ArrayList<>();
/**
* Constructor.
* @param deviceDescriptor The Device Descriptor object wrapped by this tree node.
*/
public UsbDescriptorsDeviceNode(UsbDeviceDescriptor deviceDescriptor) {
mDeviceDescriptor = deviceDescriptor;
}
/**
* Adds a Configuration node to the assocated device node.
*/
public void addConfigDescriptorNode(UsbDescriptorsConfigNode configNode) {
mConfigNodes.add(configNode);
}
@Override
public void report(ReportCanvas canvas) {
mDeviceDescriptor.report(canvas);
for (UsbDescriptorsConfigNode node : mConfigNodes) {
node.report(canvas);
}
}
}

View File

@@ -0,0 +1,42 @@
/*
* 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.tree;
import com.android.server.usb.descriptors.UsbEndpointDescriptor;
import com.android.server.usb.descriptors.report.ReportCanvas;
/**
* @hide
* Represents an endpoint in the descriptors tree.
*/
public final class UsbDescriptorsEndpointNode extends UsbDescriptorsTreeNode {
private static final String TAG = "UsbDescriptorsEndpointNode";
private final UsbEndpointDescriptor mEndpointDescriptor;
/**
* Constructor.
* @param endpointDescriptor The Device Descriptor object wrapped by this tree node.
*/
public UsbDescriptorsEndpointNode(UsbEndpointDescriptor endpointDescriptor) {
mEndpointDescriptor = endpointDescriptor;
}
@Override
public void report(ReportCanvas canvas) {
mEndpointDescriptor.report(canvas);
}
}

View File

@@ -0,0 +1,83 @@
/*
* 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.tree;
import com.android.server.usb.descriptors.UsbInterfaceDescriptor;
import com.android.server.usb.descriptors.report.ReportCanvas;
import java.util.ArrayList;
/**
* @hide
* Represents an interface in the descriptors tree.
*/
public final class UsbDescriptorsInterfaceNode extends UsbDescriptorsTreeNode {
private static final String TAG = "UsbDescriptorsInterfaceNode";
private final UsbInterfaceDescriptor mInterfaceDescriptor;
private final ArrayList<UsbDescriptorsEndpointNode> mEndpointNodes = new ArrayList<>();
private final ArrayList<UsbDescriptorsACInterfaceNode> mACInterfaceNodes = new ArrayList<>();
/**
* Constructor.
* @param interfaceDescriptor The Interface Descriptor object wrapped by this tree node.
*/
public UsbDescriptorsInterfaceNode(UsbInterfaceDescriptor interfaceDescriptor) {
mInterfaceDescriptor = interfaceDescriptor;
}
/**
* Adds an endpoint descriptor as a child of this interface node.
* @param endpointNode The endpoint descriptor node to add to this interface node.
*/
public void addEndpointNode(UsbDescriptorsEndpointNode endpointNode) {
mEndpointNodes.add(endpointNode);
}
/**
* Adds an Audio-class interface descriptor as a child of this interface node.
* @param acInterfaceNode The audio-class descriptor node to add to this interface node.
*/
public void addACInterfaceNode(UsbDescriptorsACInterfaceNode acInterfaceNode) {
mACInterfaceNodes.add(acInterfaceNode);
}
@Override
public void report(ReportCanvas canvas) {
mInterfaceDescriptor.report(canvas);
// Audio Class Interfaces
if (mACInterfaceNodes.size() > 0) {
canvas.writeParagraph("Audio Class Interfaces", false);
canvas.openList();
for (UsbDescriptorsACInterfaceNode node : mACInterfaceNodes) {
node.report(canvas);
}
canvas.closeList();
}
// Endpoints
if (mEndpointNodes.size() > 0) {
canvas.writeParagraph("Endpoints", false);
canvas.openList();
for (UsbDescriptorsEndpointNode node : mEndpointNodes) {
node.report(canvas);
}
canvas.closeList();
}
}
}

View File

@@ -0,0 +1,145 @@
/*
* 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.tree;
import com.android.server.usb.descriptors.UsbACInterface;
import com.android.server.usb.descriptors.UsbConfigDescriptor;
import com.android.server.usb.descriptors.UsbDescriptor;
import com.android.server.usb.descriptors.UsbDescriptorParser;
import com.android.server.usb.descriptors.UsbDeviceDescriptor;
import com.android.server.usb.descriptors.UsbEndpointDescriptor;
import com.android.server.usb.descriptors.UsbInterfaceDescriptor;
import com.android.server.usb.descriptors.report.ReportCanvas;
import java.util.ArrayList;
/*
* The general layout of the tree looks like this, though no guarentee about
* ordering of descriptors beyond the Device -> Config -> Interface.
*
* Device Descriptor
* +- Config Descriptor
* +- Interface Descriptor
* | +- Audio Class Interface
* | +- Audio Class Interface
* | +- Audio Class Interface
* | +- Endpoint Descriptor
* | +- Endpoint Descriptor
* +- Interface Descriptor
* +- Endpoint Descriptor
*/
/**
* @hide
*
* A class which builds a tree representation from the results of a (linear)
* parse of USB descriptors.
*
* @see {@link com.android.server.usb.descriptors.UsbDescriptorsParser UsbDescriptorsParser}
*/
public final class UsbDescriptorsTree {
private static final String TAG = "UsbDescriptorsTree";
private UsbDescriptorsDeviceNode mDeviceNode;
private UsbDescriptorsConfigNode mConfigNode; // being parsed
private UsbDescriptorsInterfaceNode mInterfaceNode; // being parsed
/**
* Adds THE device descriptor as the root of the tree.
*/
private void addDeviceDescriptor(UsbDeviceDescriptor deviceDescriptor) {
mDeviceNode = new UsbDescriptorsDeviceNode(deviceDescriptor);
}
/**
* Adds A config descriptor to the tree.
*/
private void addConfigDescriptor(UsbConfigDescriptor configDescriptor) {
mConfigNode = new UsbDescriptorsConfigNode(configDescriptor);
mDeviceNode.addConfigDescriptorNode(mConfigNode);
}
/**
* Adds AN interface descriptor to the current configuration in the tree.
*/
private void addInterfaceDescriptor(UsbInterfaceDescriptor interfaceDescriptor) {
mInterfaceNode = new UsbDescriptorsInterfaceNode(interfaceDescriptor);
mConfigNode.addInterfaceNode(mInterfaceNode);
}
/**
* Adds an endpoint descriptor to the current interface in the tree.
*/
private void addEndpointDescriptor(UsbEndpointDescriptor endpointDescriptor) {
mInterfaceNode.addEndpointNode(new UsbDescriptorsEndpointNode(endpointDescriptor));
}
/**
* Adds an audio-class interface descriptor to the current interface in the tree.
*/
private void addACInterface(UsbACInterface acInterface) {
mInterfaceNode.addACInterfaceNode(new UsbDescriptorsACInterfaceNode(acInterface));
}
/**
* Parses the linear descriptor list contained in the parser argument, into a tree
* representation corresponding to the logical structure of the USB descriptors.
*/
public void parse(UsbDescriptorParser parser) {
ArrayList<UsbDescriptor> descriptors = parser.getDescriptors();
for (int descrIndex = 0; descrIndex < descriptors.size(); descrIndex++) {
UsbDescriptor descriptor = descriptors.get(descrIndex);
switch (descriptor.getType()) {
//
// Basic Descriptors
//
case UsbDescriptor.DESCRIPTORTYPE_DEVICE:
addDeviceDescriptor((UsbDeviceDescriptor) descriptor);
break;
case UsbDescriptor.DESCRIPTORTYPE_CONFIG:
addConfigDescriptor((UsbConfigDescriptor) descriptor);
break;
case UsbDescriptor.DESCRIPTORTYPE_INTERFACE:
addInterfaceDescriptor((UsbInterfaceDescriptor) descriptor);
break;
case UsbDescriptor.DESCRIPTORTYPE_ENDPOINT:
addEndpointDescriptor((UsbEndpointDescriptor) descriptor);
break;
//
// Audio Class Descriptors
//
case UsbDescriptor.DESCRIPTORTYPE_AUDIO_INTERFACE:
addACInterface((UsbACInterface) descriptor);
break;
case UsbDescriptor.DESCRIPTORTYPE_AUDIO_ENDPOINT:
break;
}
}
}
/**
* Generate a report of the descriptors tree.
*/
public void report(ReportCanvas canvas) {
mDeviceNode.report(canvas);
}
}

View File

@@ -0,0 +1,41 @@
/*
* 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.tree;
import com.android.server.usb.descriptors.report.ReportCanvas;
import com.android.server.usb.descriptors.report.Reporting;
/**
* @hide
* A shared super class for UsbDescriptor tree nodes.
*/
public class UsbDescriptorsTreeNode implements Reporting {
private static final String TAG = "UsbDescriptorsTreeNode";
/**
* Implements generate a comprehehensive report of descriptor.
*/
@Override
public void report(ReportCanvas canvas) {
}
/**
* Implements generate an abreviated report of descriptor.
*/
@Override
public void shortReport(ReportCanvas canvas) {
}
}