Home | History | Annotate | Download | only in descriptors
      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      */
    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 
    144     public int getLength() {
    145         return mLength;
    146     }
    147 
    148     public byte getType() {
    149         return mType;
    150     }
    151 
    152     public int getStatus() {
    153         return mStatus;
    154     }
    155 
    156     public void setStatus(int status) {
    157         mStatus = status;
    158     }
    159 
    160     public int getOverUnderRunCount() {
    161         return mOverUnderRunCount;
    162     }
    163 
    164     public String getStatusString() {
    165         return sStatusStrings[mStatus];
    166     }
    167 
    168     public byte[] getRawData() {
    169         return mRawData;
    170     }
    171 
    172     /**
    173      * Called by the parser for any necessary cleanup.
    174      */
    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      */
    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      */
    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 
    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
    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
    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