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     // USB control transfer timeout
130     public static final int USB_CONTROL_TRANSFER_TIMEOUT_MS = 200;
131 
132     /**
133      * @throws IllegalArgumentException
134      */
UsbDescriptor(int length, byte type)135     UsbDescriptor(int length, byte type) {
136         // a descriptor has at least a length byte and type byte
137         // one could imagine an empty one otherwise
138         if (length < 2) {
139             // huh?
140             throw new IllegalArgumentException();
141         }
142 
143         mLength = length;
144         mType = type;
145     }
146 
getLength()147     public int getLength() {
148         return mLength;
149     }
150 
getType()151     public byte getType() {
152         return mType;
153     }
154 
getStatus()155     public int getStatus() {
156         return mStatus;
157     }
158 
setStatus(int status)159     public void setStatus(int status) {
160         mStatus = status;
161     }
162 
getOverUnderRunCount()163     public int getOverUnderRunCount() {
164         return mOverUnderRunCount;
165     }
166 
getStatusString()167     public String getStatusString() {
168         return sStatusStrings[mStatus];
169     }
170 
getRawData()171     public byte[] getRawData() {
172         return mRawData;
173     }
174 
175     /**
176      * Called by the parser for any necessary cleanup.
177      */
postParse(ByteStream stream)178     public void postParse(ByteStream stream) {
179         // Status
180         int bytesRead = stream.getReadCount();
181         if (bytesRead < mLength) {
182             // Too cold...
183             stream.advance(mLength - bytesRead);
184             mStatus = STATUS_PARSED_UNDERRUN;
185             mOverUnderRunCount = mLength - bytesRead;
186             Log.w(TAG, "UNDERRUN t:0x" + Integer.toHexString(mType)
187                     + " r: " + bytesRead + " < l: " + mLength);
188         } else if (bytesRead > mLength) {
189             // Too hot...
190             stream.reverse(bytesRead - mLength);
191             mStatus = STATUS_PARSED_OVERRUN;
192             mOverUnderRunCount = bytesRead - mLength;
193             Log.w(TAG, "OVERRRUN t:0x" + Integer.toHexString(mType)
194                     + " r: " + bytesRead + " > l: " + mLength);
195         } else {
196             // Just right!
197             mStatus = STATUS_PARSED_OK;
198         }
199     }
200 
201     /**
202      * Reads data fields from specified raw-data stream.
203      */
parseRawDescriptors(ByteStream stream)204     public int parseRawDescriptors(ByteStream stream) {
205         int numRead = stream.getReadCount();
206         int dataLen = mLength - numRead;
207         if (dataLen > 0) {
208             mRawData = new byte[dataLen];
209             for (int index = 0; index < dataLen; index++) {
210                 mRawData[index] = stream.getByte();
211             }
212         }
213         return mLength;
214     }
215 
216     /**
217      * Gets a string for the specified index from the USB Device's string descriptors.
218      */
getUsbDescriptorString(UsbDeviceConnection connection, byte strIndex)219     public static String getUsbDescriptorString(UsbDeviceConnection connection, byte strIndex) {
220         String usbStr = "";
221         if (strIndex != 0) {
222             try {
223                 int rdo = connection.controlTransfer(
224                         UsbConstants.USB_DIR_IN | UsbConstants.USB_TYPE_STANDARD,
225                         REQUEST_GET_DESCRIPTOR,
226                         (DESCRIPTORTYPE_STRING << 8) | strIndex,
227                         0,
228                         sStringBuffer,
229                         0xFF,
230                         USB_CONTROL_TRANSFER_TIMEOUT_MS);
231                 if (rdo >= 0) {
232                     usbStr = new String(sStringBuffer, 2, rdo - 2, "UTF-16LE");
233                 } else {
234                     usbStr = "?";
235                 }
236             } catch (Exception e) {
237                 Log.e(TAG, "Can not communicate with USB device", e);
238             }
239         }
240         return usbStr;
241     }
242 
reportParseStatus(ReportCanvas canvas)243     private void reportParseStatus(ReportCanvas canvas) {
244         int status = getStatus();
245         switch (status) {
246             case UsbDescriptor.STATUS_PARSED_OK:
247                 break;  // no need to report
248 
249             case UsbDescriptor.STATUS_UNPARSED:
250             case UsbDescriptor.STATUS_PARSED_UNDERRUN:
251             case UsbDescriptor.STATUS_PARSED_OVERRUN:
252                 canvas.writeParagraph("status: " + getStatusString()
253                         + " [" + getOverUnderRunCount() + "]", true);
254                 break;
255         }
256     }
257 
258     @Override
report(ReportCanvas canvas)259     public void report(ReportCanvas canvas) {
260         String descTypeStr = UsbStrings.getDescriptorName(getType());
261         String text = descTypeStr + ": " + ReportCanvas.getHexString(getType())
262                 + " Len: " + getLength();
263         if (mHierarchyLevel != 0) {
264             canvas.writeHeader(mHierarchyLevel, text);
265         } else {
266             canvas.writeParagraph(text, false);
267         }
268 
269         if (getStatus() != STATUS_PARSED_OK) {
270             reportParseStatus(canvas);
271         }
272     }
273 
274     @Override
shortReport(ReportCanvas canvas)275     public void shortReport(ReportCanvas canvas) {
276         String descTypeStr = UsbStrings.getDescriptorName(getType());
277         String text = descTypeStr + ": " + ReportCanvas.getHexString(getType())
278                 + " Len: " + getLength();
279         canvas.writeParagraph(text, false);
280     }
281 }
282