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_CLASSSPECIFIC_INTERFACE = 0x24;    // 36
82     public static final byte DESCRIPTORTYPE_CLASSSPECIFIC_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     }
getStatusString()166     public String getStatusString() {
167         return sStatusStrings[mStatus];
168     }
169 
getRawData()170     public byte[] getRawData() {
171         return mRawData;
172     }
173 
174     /**
175      * Called by the parser for any necessary cleanup.
176      */
postParse(ByteStream stream)177     public void postParse(ByteStream stream) {
178         // Status
179         int bytesRead = stream.getReadCount();
180         if (bytesRead < mLength) {
181             // Too cold...
182             stream.advance(mLength - bytesRead);
183             mStatus = STATUS_PARSED_UNDERRUN;
184             mOverUnderRunCount = mLength - bytesRead;
185             Log.w(TAG, "UNDERRUN t:0x" + Integer.toHexString(mType)
186                     + " r: " + bytesRead + " < l: " + mLength);
187         } else if (bytesRead > mLength) {
188             // Too hot...
189             stream.reverse(bytesRead - mLength);
190             mStatus = STATUS_PARSED_OVERRUN;
191             mOverUnderRunCount = bytesRead - mLength;
192             Log.w(TAG, "OVERRRUN t:0x" + Integer.toHexString(mType)
193                     + " r: " + bytesRead + " > l: " + mLength);
194         } else {
195             // Just right!
196             mStatus = STATUS_PARSED_OK;
197         }
198     }
199 
200     /**
201      * Reads data fields from specified raw-data stream.
202      */
parseRawDescriptors(ByteStream stream)203     public int parseRawDescriptors(ByteStream stream) {
204         int numRead = stream.getReadCount();
205         int dataLen = mLength - numRead;
206         if (dataLen > 0) {
207             mRawData = new byte[dataLen];
208             for (int index = 0; index < dataLen; index++) {
209                 mRawData[index] = stream.getByte();
210             }
211         }
212         return mLength;
213     }
214 
215     /**
216      * Gets a string for the specified index from the USB Device's string descriptors.
217      */
getUsbDescriptorString(UsbDeviceConnection connection, byte strIndex)218     public static String getUsbDescriptorString(UsbDeviceConnection connection, byte strIndex) {
219         String usbStr = "";
220         if (strIndex != 0) {
221             try {
222                 int rdo = connection.controlTransfer(
223                         UsbConstants.USB_DIR_IN | UsbConstants.USB_TYPE_STANDARD,
224                         REQUEST_GET_DESCRIPTOR,
225                         (DESCRIPTORTYPE_STRING << 8) | strIndex,
226                         0,
227                         sStringBuffer,
228                         0xFF,
229                         USB_CONTROL_TRANSFER_TIMEOUT_MS);
230                 if (rdo >= 0) {
231                     usbStr = new String(sStringBuffer, 2, rdo - 2, "UTF-16LE");
232                 } else {
233                     usbStr = "?";
234                 }
235             } catch (Exception e) {
236                 Log.e(TAG, "Can not communicate with USB device", e);
237             }
238         }
239         return usbStr;
240     }
241 
reportParseStatus(ReportCanvas canvas)242     private void reportParseStatus(ReportCanvas canvas) {
243         int status = getStatus();
244         switch (status) {
245             case UsbDescriptor.STATUS_PARSED_OK:
246                 break;  // no need to report
247 
248             case UsbDescriptor.STATUS_UNPARSED:
249             case UsbDescriptor.STATUS_PARSED_UNDERRUN:
250             case UsbDescriptor.STATUS_PARSED_OVERRUN:
251                 canvas.writeParagraph("status: " + getStatusString()
252                         + " [" + getOverUnderRunCount() + "]", true);
253                 break;
254         }
255     }
256 
257     @Override
report(ReportCanvas canvas)258     public void report(ReportCanvas canvas) {
259         String descTypeStr = UsbStrings.getDescriptorName(getType());
260         String text = descTypeStr + ": " + ReportCanvas.getHexString(getType())
261                 + " Len: " + getLength();
262         if (mHierarchyLevel != 0) {
263             canvas.writeHeader(mHierarchyLevel, text);
264         } else {
265             canvas.writeParagraph(text, false);
266         }
267 
268         if (getStatus() != STATUS_PARSED_OK) {
269             reportParseStatus(canvas);
270         }
271     }
272 
273     @Override
shortReport(ReportCanvas canvas)274     public void shortReport(ReportCanvas canvas) {
275         String descTypeStr = UsbStrings.getDescriptorName(getType());
276         String text = descTypeStr + ": " + ReportCanvas.getHexString(getType())
277                 + " Len: " + getLength();
278         canvas.writeParagraph(text, false);
279     }
280 
281     /*
282      * Logging Helpers
283      */
getDescriptorName(byte descriptorType, int descriptorLength)284     static String getDescriptorName(byte descriptorType, int descriptorLength) {
285         String name = UsbStrings.getDescriptorName(descriptorType);
286         if (name != null) {
287             return name;
288         } else {
289             return "Unknown Descriptor Type " + descriptorType
290                 + " 0x" + Integer.toHexString(descriptorType)
291                 + " length:" + descriptorLength;
292         }
293     }
294 
logDescriptorName(byte descriptorType, int descriptorLength)295     static void logDescriptorName(byte descriptorType, int descriptorLength) {
296         if (UsbDescriptorParser.DEBUG) {
297             Log.d(TAG, "----> " + getDescriptorName(descriptorType, descriptorLength));
298         }
299     }
300 }
301