OSDN Git Service

Adding USB audio-class 2.0 spec descriptors.
authorPaul McLean <pmclean@google.com>
Fri, 7 Jul 2017 14:14:52 +0000 (08:14 -0600)
committerPaul McLean <pmclean@google.com>
Wed, 30 Aug 2017 16:29:16 +0000 (10:29 -0600)
Added USB audio-class 2.0 format type descriptors.
Added Tree parsing.
Rearchitected reporting (again) to better support reuse and scale.

Bug: 64301464

Test: Run code against representative USB audio peripherals and verify
that each correctly handles or ignores all descriptors and the correct
device semantics (headset / not-headset) are still calculated.

Change-Id: I1b3b01ab1cd804ed876bf6427c3afba15eea6a6e

57 files changed:
services/usb/java/com/android/server/usb/descriptors/ByteStream.java
services/usb/java/com/android/server/usb/descriptors/Usb10ACHeader.java [moved from services/usb/java/com/android/server/usb/descriptors/UsbACHeader.java with 60% similarity]
services/usb/java/com/android/server/usb/descriptors/Usb10ACInputTerminal.java [moved from services/usb/java/com/android/server/usb/descriptors/UsbACInputTerminal.java with 70% similarity]
services/usb/java/com/android/server/usb/descriptors/Usb10ACMixerUnit.java [new file with mode: 0644]
services/usb/java/com/android/server/usb/descriptors/Usb10ACOutputTerminal.java [moved from services/usb/java/com/android/server/usb/descriptors/UsbACOutputTerminal.java with 71% similarity]
services/usb/java/com/android/server/usb/descriptors/Usb10ASFormatI.java [moved from services/usb/java/com/android/server/usb/descriptors/UsbASFormatI.java with 62% similarity]
services/usb/java/com/android/server/usb/descriptors/Usb10ASFormatII.java [moved from services/usb/java/com/android/server/usb/descriptors/UsbASFormatII.java with 66% similarity]
services/usb/java/com/android/server/usb/descriptors/Usb10ASGeneral.java [moved from services/usb/java/com/android/server/usb/descriptors/UsbASGeneral.java with 68% similarity]
services/usb/java/com/android/server/usb/descriptors/Usb20ACHeader.java [new file with mode: 0644]
services/usb/java/com/android/server/usb/descriptors/Usb20ACInputTerminal.java [new file with mode: 0644]
services/usb/java/com/android/server/usb/descriptors/Usb20ACMixerUnit.java [new file with mode: 0644]
services/usb/java/com/android/server/usb/descriptors/Usb20ACOutputTerminal.java [new file with mode: 0644]
services/usb/java/com/android/server/usb/descriptors/Usb20ASFormatI.java [new file with mode: 0644]
services/usb/java/com/android/server/usb/descriptors/Usb20ASFormatII.java [new file with mode: 0644]
services/usb/java/com/android/server/usb/descriptors/Usb20ASFormatIIEx.java [new file with mode: 0644]
services/usb/java/com/android/server/usb/descriptors/Usb20ASFormatIII.java [new file with mode: 0644]
services/usb/java/com/android/server/usb/descriptors/Usb20ASGeneral.java [new file with mode: 0644]
services/usb/java/com/android/server/usb/descriptors/UsbACAudioControlEndpoint.java
services/usb/java/com/android/server/usb/descriptors/UsbACAudioStreamEndpoint.java
services/usb/java/com/android/server/usb/descriptors/UsbACEndpoint.java
services/usb/java/com/android/server/usb/descriptors/UsbACFeatureUnit.java
services/usb/java/com/android/server/usb/descriptors/UsbACHeaderInterface.java [new file with mode: 0644]
services/usb/java/com/android/server/usb/descriptors/UsbACInterface.java
services/usb/java/com/android/server/usb/descriptors/UsbACInterfaceUnparsed.java [new file with mode: 0644]
services/usb/java/com/android/server/usb/descriptors/UsbACMidiEndpoint.java
services/usb/java/com/android/server/usb/descriptors/UsbACMixerUnit.java
services/usb/java/com/android/server/usb/descriptors/UsbACSelectorUnit.java
services/usb/java/com/android/server/usb/descriptors/UsbACTerminal.java
services/usb/java/com/android/server/usb/descriptors/UsbASFormat.java
services/usb/java/com/android/server/usb/descriptors/UsbBinaryParser.java
services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java
services/usb/java/com/android/server/usb/descriptors/UsbDescriptor.java
services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
services/usb/java/com/android/server/usb/descriptors/UsbDeviceDescriptor.java
services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java
services/usb/java/com/android/server/usb/descriptors/UsbHIDDescriptor.java
services/usb/java/com/android/server/usb/descriptors/UsbInterfaceAssoc.java
services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
services/usb/java/com/android/server/usb/descriptors/UsbMSMidiHeader.java
services/usb/java/com/android/server/usb/descriptors/UsbMSMidiInputJack.java
services/usb/java/com/android/server/usb/descriptors/UsbMSMidiOutputJack.java
services/usb/java/com/android/server/usb/descriptors/UsbTerminalTypes.java
services/usb/java/com/android/server/usb/descriptors/UsbUnknown.java
services/usb/java/com/android/server/usb/descriptors/report/HTMLReportCanvas.java [new file with mode: 0644]
services/usb/java/com/android/server/usb/descriptors/report/HTMLReporter.java [deleted file]
services/usb/java/com/android/server/usb/descriptors/report/ReportCanvas.java [new file with mode: 0644]
services/usb/java/com/android/server/usb/descriptors/report/Reporter.java [deleted file]
services/usb/java/com/android/server/usb/descriptors/report/Reporting.java
services/usb/java/com/android/server/usb/descriptors/report/TextReportCanvas.java [new file with mode: 0644]
services/usb/java/com/android/server/usb/descriptors/report/UsbStrings.java
services/usb/java/com/android/server/usb/descriptors/tree/UsbDescriptorsACInterfaceNode.java [new file with mode: 0644]
services/usb/java/com/android/server/usb/descriptors/tree/UsbDescriptorsConfigNode.java [new file with mode: 0644]
services/usb/java/com/android/server/usb/descriptors/tree/UsbDescriptorsDeviceNode.java [new file with mode: 0644]
services/usb/java/com/android/server/usb/descriptors/tree/UsbDescriptorsEndpointNode.java [new file with mode: 0644]
services/usb/java/com/android/server/usb/descriptors/tree/UsbDescriptorsInterfaceNode.java [new file with mode: 0644]
services/usb/java/com/android/server/usb/descriptors/tree/UsbDescriptorsTree.java [new file with mode: 0644]
services/usb/java/com/android/server/usb/descriptors/tree/UsbDescriptorsTreeNode.java [new file with mode: 0644]

index d678931..1e823b6 100644 (file)
  */
 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 */
@@ -104,6 +108,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
      * 0, essentially making the returned value *unsigned*.
@@ -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,16 +149,32 @@ 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.
      * @throws IndexOutOfBoundsException
  */
 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();
+    }
 }
  */
 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();
+    }
 }
diff --git a/services/usb/java/com/android/server/usb/descriptors/Usb10ACMixerUnit.java b/services/usb/java/com/android/server/usb/descriptors/Usb10ACMixerUnit.java
new file mode 100644 (file)
index 0000000..d348664
--- /dev/null
@@ -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();
+    }
+}
  */
 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();
+    }
 }
  */
 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,11 +53,24 @@ 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();
         mSubframeSize = 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();
+    }
 }
  */
 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();
+    }
+
 }
  */
 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();
+    }
 }
diff --git a/services/usb/java/com/android/server/usb/descriptors/Usb20ACHeader.java b/services/usb/java/com/android/server/usb/descriptors/Usb20ACHeader.java
new file mode 100644 (file)
index 0000000..eefae3d
--- /dev/null
@@ -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();
+    }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/Usb20ACInputTerminal.java b/services/usb/java/com/android/server/usb/descriptors/Usb20ACInputTerminal.java
new file mode 100644 (file)
index 0000000..3e2ac39
--- /dev/null
@@ -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
+                                // Terminal’s 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();
+    }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/Usb20ACMixerUnit.java b/services/usb/java/com/android/server/usb/descriptors/Usb20ACMixerUnit.java
new file mode 100644 (file)
index 0000000..1b267a6
--- /dev/null
@@ -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;
+    }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/Usb20ACOutputTerminal.java b/services/usb/java/com/android/server/usb/descriptors/Usb20ACOutputTerminal.java
new file mode 100644 (file)
index 0000000..67478aa
--- /dev/null
@@ -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();
+    }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/Usb20ASFormatI.java b/services/usb/java/com/android/server/usb/descriptors/Usb20ASFormatI.java
new file mode 100644 (file)
index 0000000..c031996
--- /dev/null
@@ -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();
+    }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/Usb20ASFormatII.java b/services/usb/java/com/android/server/usb/descriptors/Usb20ASFormatII.java
new file mode 100644 (file)
index 0000000..dc44ff0
--- /dev/null
@@ -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();
+    }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/Usb20ASFormatIIEx.java b/services/usb/java/com/android/server/usb/descriptors/Usb20ASFormatIIEx.java
new file mode 100644 (file)
index 0000000..d7dfba3
--- /dev/null
@@ -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();
+    }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/Usb20ASFormatIII.java b/services/usb/java/com/android/server/usb/descriptors/Usb20ASFormatIII.java
new file mode 100644 (file)
index 0000000..b44a216
--- /dev/null
@@ -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();
+    }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/Usb20ASGeneral.java b/services/usb/java/com/android/server/usb/descriptors/Usb20ASGeneral.java
new file mode 100644 (file)
index 0000000..18d48a0
--- /dev/null
@@ -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();
+    }
+}
index 96fcc6a..6e1ce07 100644 (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;
index d387883..d351902 100644 (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) {
index 223496a..4a6839d 100644 (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) {
index 739fe55..ab3903b 100644 (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;
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACHeaderInterface.java b/services/usb/java/com/android/server/usb/descriptors/UsbACHeaderInterface.java
new file mode 100644 (file)
index 0000000..01a355e
--- /dev/null
@@ -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();
+    }
+}
index 0ab7fcc..df6c53f 100644 (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();
+    }
 }
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbACInterfaceUnparsed.java b/services/usb/java/com/android/server/usb/descriptors/UsbACInterfaceUnparsed.java
new file mode 100644 (file)
index 0000000..9e00a79
--- /dev/null
@@ -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);
+    }
+}
index 9c07242..9c31457 100644 (file)
  */
 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();
+    }
+}
\ No newline at end of file
index 552b5ae..88faed9 100644 (file)
  */
 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;
     }
index b1f60bd..b16bc57 100644 (file)
  */
 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
+//    }
 }
index ea80208..2836508 100644 (file)
  */
 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();
+    }
 }
index d7c84c6..305ae2f 100644 (file)
  */
 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.
+        int acInterfaceSpec = parser.getACInterfaceSpec();
+
         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);
+            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()));
+    }
 }
index 185cee2..9710ac6 100644 (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++) {
index 8ae6d0f..75279c6 100644 (file)
  */
 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();
+    }
 }
index 63b2d7f..8c7565b 100644 (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);
+    }
 }
index d4a0ac4..ad7bde5 100644 (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,9 +32,35 @@ 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;
     }
 
index 90848ca..c8fa694 100644 (file)
  */
 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();
+    }
 }
index def6700..6322fbe 100644 (file)
  */
 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 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 TRANSTYPE_CONTROL = 0x00;
+    public static final byte TRANSTYPE_ISO = 0x01;
+    public static final byte TRANSTYPE_BULK = 0x02;
+    public static final byte TRANSTYPE_INTERRUPT = 0x03;
+
+    public static final byte MASK_ATTRIBS_SYNCTYPE = 0b00001100;
+    public static final byte SYNCTYPE_NONE = 0b00000000;
+    public static final byte SYNCTYPE_ASYNC = 0b00000100;
+    public static final byte SYNCTYPE_ADAPTSYNC = 0b00001000;
+    public static final byte SYNCTYPE_RESERVED = 0b00001100;
+
+    public static final byte MASK_ATTRIBS_USEAGE = 0b00110000;
+    public static final byte USEAGE_DATA = 0b00000000;
+    public static final byte USEAGE_FEEDBACK = 0b00010000;
+    public static final byte USEAGE_EXPLICIT = 0b00100000;
+    public static final byte USEAGE_RESERVED = 0b00110000;
 
     private byte mEndpointAddress;  // 2:1 Endpoint Address
                                     // Bits 0..3b Endpoint Number.
@@ -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();
+    }
 }
index 56c07ec..b4cc87e 100644 (file)
  */
 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();
+    }
 }
index 4b18a01..d680e54 100644 (file)
  */
 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);
+//
+//    }
 }
index 21b5e0c..4eef6ca 100644 (file)
  */
 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();
+    }
 }
index 4452b23..85a3e68 100644 (file)
  */
 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());
+    }
 }
index 2d33ba7..1d5cbf2 100644 (file)
  */
 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());
+    }
 }
index bd2dc11..9f50240 100644 (file)
  */
 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());
+    }
 }
index b521462..9bd6cb9 100644 (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;
index a6fe8bb..6e6dccf 100644 (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);
diff --git a/services/usb/java/com/android/server/usb/descriptors/report/HTMLReportCanvas.java b/services/usb/java/com/android/server/usb/descriptors/report/HTMLReportCanvas.java
new file mode 100644 (file)
index 0000000..99ebcca
--- /dev/null
@@ -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>");
+    }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/report/HTMLReporter.java b/services/usb/java/com/android/server/usb/descriptors/report/HTMLReporter.java
deleted file mode 100644 (file)
index c98789d..0000000
+++ /dev/null
@@ -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);
-    }
-}
diff --git a/services/usb/java/com/android/server/usb/descriptors/report/ReportCanvas.java b/services/usb/java/com/android/server/usb/descriptors/report/ReportCanvas.java
new file mode 100644 (file)
index 0000000..9e0adf5
--- /dev/null
@@ -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();
+        }
+    }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/report/Reporter.java b/services/usb/java/com/android/server/usb/descriptors/report/Reporter.java
deleted file mode 100644 (file)
index 2944c10..0000000
+++ /dev/null
@@ -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);
-}
index c13111b..be7c12e 100644 (file)
 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);
 }
diff --git a/services/usb/java/com/android/server/usb/descriptors/report/TextReportCanvas.java b/services/usb/java/com/android/server/usb/descriptors/report/TextReportCanvas.java
new file mode 100644 (file)
index 0000000..33746ba
--- /dev/null
@@ -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");
+    }
+}
index 0461150..ff58a26 100644 (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";
+    }
 }
diff --git a/services/usb/java/com/android/server/usb/descriptors/tree/UsbDescriptorsACInterfaceNode.java b/services/usb/java/com/android/server/usb/descriptors/tree/UsbDescriptorsACInterfaceNode.java
new file mode 100644 (file)
index 0000000..49caca5
--- /dev/null
@@ -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();
+    }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/tree/UsbDescriptorsConfigNode.java b/services/usb/java/com/android/server/usb/descriptors/tree/UsbDescriptorsConfigNode.java
new file mode 100644 (file)
index 0000000..64f9496
--- /dev/null
@@ -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();
+    }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/tree/UsbDescriptorsDeviceNode.java b/services/usb/java/com/android/server/usb/descriptors/tree/UsbDescriptorsDeviceNode.java
new file mode 100644 (file)
index 0000000..898a06e
--- /dev/null
@@ -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);
+        }
+    }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/tree/UsbDescriptorsEndpointNode.java b/services/usb/java/com/android/server/usb/descriptors/tree/UsbDescriptorsEndpointNode.java
new file mode 100644 (file)
index 0000000..7286417
--- /dev/null
@@ -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);
+    }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/tree/UsbDescriptorsInterfaceNode.java b/services/usb/java/com/android/server/usb/descriptors/tree/UsbDescriptorsInterfaceNode.java
new file mode 100644 (file)
index 0000000..d49d88d
--- /dev/null
@@ -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();
+        }
+    }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/tree/UsbDescriptorsTree.java b/services/usb/java/com/android/server/usb/descriptors/tree/UsbDescriptorsTree.java
new file mode 100644 (file)
index 0000000..1aa30fa
--- /dev/null
@@ -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);
+    }
+}
diff --git a/services/usb/java/com/android/server/usb/descriptors/tree/UsbDescriptorsTreeNode.java b/services/usb/java/com/android/server/usb/descriptors/tree/UsbDescriptorsTreeNode.java
new file mode 100644 (file)
index 0000000..aca3cd9
--- /dev/null
@@ -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) {
+    }
+}