1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.server.usb.descriptors;
17 
18 import android.hardware.usb.UsbConstants;
19 import android.hardware.usb.UsbDeviceConnection;
20 import android.util.Log;
21 
22 import com.android.server.usb.descriptors.report.ReportCanvas;
23 import com.android.server.usb.descriptors.report.Reporting;
24 import com.android.server.usb.descriptors.report.UsbStrings;
25 
26 /*
27  * Some notes about UsbDescriptor and its subclasses.
28  *
29  * It is assumed that the user of the UsbDescriptorParser knows what they are doing
30  * so NO PROTECTION is implemented against "improper" use. Such uses are specifically:
31  * allocating a UsbDescriptor (subclass) object outside of the context of parsing/reading
32  * a rawdescriptor stream and perhaps accessing fields which have not been inialized (by
33  * parsing/reading or course).
34  */
35 
36 /**
37  * @hide
38  * Common superclass for all USB Descriptors.
39  */
40 public abstract class UsbDescriptor implements Reporting {
41     private static final String TAG = "UsbDescriptor";
42 
43     protected int mHierarchyLevel;
44 
45     protected final int mLength;    // 0:1 bLength Number Size of the Descriptor in Bytes (18 bytes)
46                                     // we store this as an int because Java bytes are SIGNED.
47     protected final byte mType;     // 1:1 bDescriptorType Constant Device Descriptor (0x01)
48 
49     private byte[] mRawData;
50 
51     private static final int SIZE_STRINGBUFFER = 256;
52     private static byte[] sStringBuffer = new byte[SIZE_STRINGBUFFER];
53 
54     // Status
55     public static final int STATUS_UNPARSED         = 0;
56     public static final int STATUS_PARSED_OK        = 1;
57     public static final int STATUS_PARSED_UNDERRUN  = 2;
58     public static final int STATUS_PARSED_OVERRUN   = 3;
59     public static final int STATUS_PARSE_EXCEPTION  = 4;
60 
61     private int mStatus = STATUS_UNPARSED;
62 
63     private static String[] sStatusStrings = {
64             "UNPARSED", "PARSED - OK", "PARSED - UNDERRUN", "PARSED - OVERRUN"};
65 
66     private int mOverUnderRunCount;
67 
68     // Descriptor Type IDs
69     public static final byte DESCRIPTORTYPE_DEVICE = 0x01;            // 1
70     public static final byte DESCRIPTORTYPE_CONFIG = 0x02;            // 2
71     public static final byte DESCRIPTORTYPE_STRING = 0x03;            // 3
72     public static final byte DESCRIPTORTYPE_INTERFACE = 0x04;         // 4
73     public static final byte DESCRIPTORTYPE_ENDPOINT = 0x05;          // 5
74     public static final byte DESCRIPTORTYPE_INTERFACEASSOC = 0x0B;    // 11
75     public static final byte DESCRIPTORTYPE_BOS = 0x0F;               // 15
76     public static final byte DESCRIPTORTYPE_CAPABILITY = 0x10;        // 16
77 
78     public static final byte DESCRIPTORTYPE_HID = 0x21;                // 33
79     public static final byte DESCRIPTORTYPE_REPORT = 0x22;             // 34
80     public static final byte DESCRIPTORTYPE_PHYSICAL = 0x23;           // 35
81     public static final byte DESCRIPTORTYPE_AUDIO_INTERFACE = 0x24;    // 36
82     public static final byte DESCRIPTORTYPE_AUDIO_ENDPOINT = 0x25;     // 37
83     public static final byte DESCRIPTORTYPE_HUB = 0x29;                // 41
84     public static final byte DESCRIPTORTYPE_SUPERSPEED_HUB = 0x2A;     // 42
85     public static final byte DESCRIPTORTYPE_ENDPOINT_COMPANION = 0x30; // 48
86 
87     // Class IDs
88     public static final int CLASSID_DEVICE  =      0x00;
89     public static final int CLASSID_AUDIO =        0x01;
90     public static final int CLASSID_COM =          0x02;
91     public static final int CLASSID_HID =          0x03;
92     // public static final int CLASSID_??? =       0x04;
93     public static final int CLASSID_PHYSICAL =     0x05;
94     public static final int CLASSID_IMAGE =        0x06;
95     public static final int CLASSID_PRINTER =      0x07;
96     public static final int CLASSID_STORAGE =      0x08;
97     public static final int CLASSID_HUB =          0x09;
98     public static final int CLASSID_CDC_CONTROL =  0x0A;
99     public static final int CLASSID_SMART_CARD =   0x0B;
100     //public static final int CLASSID_??? =        0x0C;
101     public static final int CLASSID_SECURITY =     0x0D;
102     public static final int CLASSID_VIDEO =        0x0E;
103     public static final int CLASSID_HEALTHCARE =   0x0F;
104     public static final int CLASSID_AUDIOVIDEO =   0x10;
105     public static final int CLASSID_BILLBOARD =    0x11;
106     public static final int CLASSID_TYPECBRIDGE =  0x12;
107     public static final int CLASSID_DIAGNOSTIC =   0xDC;
108     public static final int CLASSID_WIRELESS =     0xE0;
109     public static final int CLASSID_MISC =         0xEF;
110     public static final int CLASSID_APPSPECIFIC =  0xFE;
111     public static final int CLASSID_VENDSPECIFIC = 0xFF;
112 
113     // Audio Subclass codes
114     public static final int AUDIO_SUBCLASS_UNDEFINED   = 0x00;
115     public static final int AUDIO_AUDIOCONTROL         = 0x01;
116     public static final int AUDIO_AUDIOSTREAMING       = 0x02;
117     public static final int AUDIO_MIDISTREAMING        = 0x03;
118 
119     // Request IDs
120     public static final int REQUEST_GET_STATUS         = 0x00;
121     public static final int REQUEST_CLEAR_FEATURE      = 0x01;
122     public static final int REQUEST_SET_FEATURE        = 0x03;
123     public static final int REQUEST_GET_ADDRESS        = 0x05;
124     public static final int REQUEST_GET_DESCRIPTOR     = 0x06;
125     public static final int REQUEST_SET_DESCRIPTOR     = 0x07;
126     public static final int REQUEST_GET_CONFIGURATION  = 0x08;
127     public static final int REQUEST_SET_CONFIGURATION  = 0x09;
128 
129     /**
130      * @throws IllegalArgumentException
131      */
UsbDescriptor(int length, byte type)132     UsbDescriptor(int length, byte type) {
133         // a descriptor has at least a length byte and type byte
134         // one could imagine an empty one otherwise
135         if (length < 2) {
136             // huh?
137             throw new IllegalArgumentException();
138         }
139 
140         mLength = length;
141         mType = type;
142     }
143 
getLength()144     public int getLength() {
145         return mLength;
146     }
147 
getType()148     public byte getType() {
149         return mType;
150     }
151 
getStatus()152     public int getStatus() {
153         return mStatus;
154     }
155 
setStatus(int status)156     public void setStatus(int status) {
157         mStatus = status;
158     }
159 
getOverUnderRunCount()160     public int getOverUnderRunCount() {
161         return mOverUnderRunCount;
162     }
163 
getStatusString()164     public String getStatusString() {
165         return sStatusStrings[mStatus];
166     }
167 
getRawData()168     public byte[] getRawData() {
169         return mRawData;
170     }
171 
172     /**
173      * Called by the parser for any necessary cleanup.
174      */
postParse(ByteStream stream)175     public void postParse(ByteStream stream) {
176         // Status
177         int bytesRead = stream.getReadCount();
178         if (bytesRead < mLength) {
179             // Too cold...
180             stream.advance(mLength - bytesRead);
181             mStatus = STATUS_PARSED_UNDERRUN;
182             mOverUnderRunCount = mLength - bytesRead;
183             Log.w(TAG, "UNDERRUN t:0x" + Integer.toHexString(mType)
184                     + " r: " + bytesRead + " < l: " + mLength);
185         } else if (bytesRead > mLength) {
186             // Too hot...
187             stream.reverse(bytesRead - mLength);
188             mStatus = STATUS_PARSED_OVERRUN;
189             mOverUnderRunCount = bytesRead - mLength;
190             Log.w(TAG, "OVERRRUN t:0x" + Integer.toHexString(mType)
191                     + " r: " + bytesRead + " > l: " + mLength);
192         } else {
193             // Just right!
194             mStatus = STATUS_PARSED_OK;
195         }
196     }
197 
198     /**
199      * Reads data fields from specified raw-data stream.
200      */
parseRawDescriptors(ByteStream stream)201     public int parseRawDescriptors(ByteStream stream) {
202         int numRead = stream.getReadCount();
203         int dataLen = mLength - numRead;
204         if (dataLen > 0) {
205             mRawData = new byte[dataLen];
206             for (int index = 0; index < dataLen; index++) {
207                 mRawData[index] = stream.getByte();
208             }
209         }
210         return mLength;
211     }
212 
213     /**
214      * Gets a string for the specified index from the USB Device's string descriptors.
215      */
getUsbDescriptorString(UsbDeviceConnection connection, byte strIndex)216     public static String getUsbDescriptorString(UsbDeviceConnection connection, byte strIndex) {
217         String usbStr = "";
218         if (strIndex != 0) {
219             try {
220                 int rdo = connection.controlTransfer(
221                         UsbConstants.USB_DIR_IN | UsbConstants.USB_TYPE_STANDARD,
222                         REQUEST_GET_DESCRIPTOR,
223                         (DESCRIPTORTYPE_STRING << 8) | strIndex,
224                         0,
225                         sStringBuffer,
226                         0xFF,
227                         0);
228                 if (rdo >= 0) {
229                     usbStr = new String(sStringBuffer, 2, rdo - 2, "UTF-16LE");
230                 } else {
231                     usbStr = "?";
232                 }
233             } catch (Exception e) {
234                 Log.e(TAG, "Can not communicate with USB device", e);
235             }
236         }
237         return usbStr;
238     }
239 
reportParseStatus(ReportCanvas canvas)240     private void reportParseStatus(ReportCanvas canvas) {
241         int status = getStatus();
242         switch (status) {
243             case UsbDescriptor.STATUS_PARSED_OK:
244                 break;  // no need to report
245 
246             case UsbDescriptor.STATUS_UNPARSED:
247             case UsbDescriptor.STATUS_PARSED_UNDERRUN:
248             case UsbDescriptor.STATUS_PARSED_OVERRUN:
249                 canvas.writeParagraph("status: " + getStatusString()
250                         + " [" + getOverUnderRunCount() + "]", true);
251                 break;
252         }
253     }
254 
255     @Override
report(ReportCanvas canvas)256     public void report(ReportCanvas canvas) {
257         String descTypeStr = UsbStrings.getDescriptorName(getType());
258         String text = descTypeStr + ": " + ReportCanvas.getHexString(getType())
259                 + " Len: " + getLength();
260         if (mHierarchyLevel != 0) {
261             canvas.writeHeader(mHierarchyLevel, text);
262         } else {
263             canvas.writeParagraph(text, false);
264         }
265 
266         if (getStatus() != STATUS_PARSED_OK) {
267             reportParseStatus(canvas);
268         }
269     }
270 
271     @Override
shortReport(ReportCanvas canvas)272     public void shortReport(ReportCanvas canvas) {
273         String descTypeStr = UsbStrings.getDescriptorName(getType());
274         String text = descTypeStr + ": " + ReportCanvas.getHexString(getType())
275                 + " Len: " + getLength();
276         canvas.writeParagraph(text, false);
277     }
278 }
279