content: /*
   * 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 android.hardware.usb.UsbConfiguration;
  import android.hardware.usb.UsbDevice;
  import android.util.Log;
  
  import com.android.server.usb.descriptors.report.ReportCanvas;
  import com.android.server.usb.descriptors.report.UsbStrings;
  
  import java.util.ArrayList;
  
  /**
   * @hide
   * A USB Device Descriptor.
   * see usb11.pdf section 9.6.1
   */
  public final class UsbDeviceDescriptor extends UsbDescriptor {
      private static final String TAG = "UsbDeviceDescriptor";
      private static final boolean DEBUG = false;
  
      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 int mDevClass;      // 4:1 class code
      private int mDevSubClass;   // 5:1 subclass code
      private int mProtocol;      // 6:1 protocol
      private byte mPacketSize;   // 7:1 Maximum Packet Size for Zero Endpoint.
                                  // Valid Sizes are 8, 16, 32, 64
      private int mVendorID;      // 8:2 vendor ID
      private int mProductID;     // 10:2 product ID
      private int mDeviceRelease; // 12:2 Device Release number - BCD
      private byte mMfgIndex;     // 14:1 Index of Manufacturer String Descriptor
      private byte mProductIndex; // 15:1 Index of Product String Descriptor
      private byte mSerialIndex;  // 16:1 Index of Serial Number String Descriptor
      private byte mNumConfigs;   // 17:1 Number of Possible Configurations
  
      private ArrayList<UsbConfigDescriptor> mConfigDescriptors =
              new ArrayList<UsbConfigDescriptor>();
  
      UsbDeviceDescriptor(int length, byte type) {
          super(length, type);
          mHierarchyLevel = 1;
      }
  
      public int getSpec() {
          return mSpec;
      }
  
      public int getDevClass() {
          return mDevClass;
      }
  
      public int getDevSubClass() {
          return mDevSubClass;
      }
  
      public int getProtocol() {
          return mProtocol;
      }
  
      public byte getPacketSize() {
          return mPacketSize;
      }
  
      public int getVendorID() {
          return mVendorID;
      }
  
      public int getProductID() {
          return mProductID;
      }
  
      public int getDeviceRelease() {
          return mDeviceRelease;
      }
  
      // mDeviceRelease is binary-coded decimal, format DD.DD
      public String getDeviceReleaseString() {
          int hundredths = mDeviceRelease & 0xF;
          int tenths = (mDeviceRelease & 0xF0) >> 4;
          int ones = (mDeviceRelease & 0xF00) >> 8;
          int tens = (mDeviceRelease & 0xF000) >> 12;
          return String.format("%d.%d%d", tens * 10 + ones, tenths, hundredths);
      }
  
      public byte getMfgIndex() {
          return mMfgIndex;
      }
  
      public String getMfgString(UsbDescriptorParser p) {
          return p.getDescriptorString(mMfgIndex);
      }
  
      public byte getProductIndex() {
          return mProductIndex;
      }
  
      public String getProductString(UsbDescriptorParser p) {
          return p.getDescriptorString(mProductIndex);
      }
  
      public byte getSerialIndex() {
          return mSerialIndex;
      }
  
      public String getSerialString(UsbDescriptorParser p) {
          return p.getDescriptorString(mSerialIndex);
      }
  
      public byte getNumConfigs() {
          return mNumConfigs;
      }
  
      void addConfigDescriptor(UsbConfigDescriptor config) {
          mConfigDescriptors.add(config);
      }
  
      /**
       * @hide
       */
      public UsbDevice.Builder toAndroid(UsbDescriptorParser parser) {
          if (DEBUG) {
              Log.d(TAG, "toAndroid()");
          }
  
          String mfgName = getMfgString(parser);
          String prodName = getProductString(parser);
          if (DEBUG) {
              Log.d(TAG, "  mfgName:" + mfgName + " prodName:" + prodName);
          }
  
          String versionString = getDeviceReleaseString();
          String serialStr = getSerialString(parser);
          if (DEBUG) {
              Log.d(TAG, "  versionString:" + versionString + " serialStr:" + serialStr);
          }
  
          UsbConfiguration[] configs = new UsbConfiguration[mConfigDescriptors.size()];
          Log.d(TAG, "  " + configs.length + " configs");
          for (int index = 0; index < mConfigDescriptors.size(); index++) {
              configs[index] = mConfigDescriptors.get(index).toAndroid(parser);
          }
          UsbDevice.Builder device = new UsbDevice.Builder(parser.getDeviceAddr(), mVendorID,
                  mProductID, mDevClass, mDevSubClass, mProtocol, mfgName, prodName, versionString,
                  configs, serialStr);
  
          return device;
      }
  
      @Override
      public int parseRawDescriptors(ByteStream stream) {
          mSpec = stream.unpackUsbShort();
          mDevClass = stream.getUnsignedByte();
          mDevSubClass = stream.getUnsignedByte();
          mProtocol = stream.getUnsignedByte();
          mPacketSize = stream.getByte();
          mVendorID = stream.unpackUsbShort();
          mProductID = stream.unpackUsbShort();
          mDeviceRelease = stream.unpackUsbShort();
          mMfgIndex = stream.getByte();
          mProductIndex = stream.getByte();
          mSerialIndex = stream.getByte();
          mNumConfigs = stream.getByte();
  
          return mLength;
      }
  
      @Override
      public void report(ReportCanvas canvas) {
          super.report(canvas);
  
          canvas.openList();
  
          int spec = getSpec();
          canvas.writeListItem("Spec: " + ReportCanvas.getBCDString(spec));
  
          int devClass = getDevClass();
          String classStr = UsbStrings.getClassName(devClass);
          int devSubClass = getDevSubClass();
          String subClasStr = UsbStrings.getClassName(devSubClass);
          canvas.writeListItem("Class " + devClass + ": " + classStr + " Subclass"
                  + devSubClass + ": " + subClasStr);
          canvas.writeListItem("Vendor ID: " + ReportCanvas.getHexString(getVendorID())
                  + " Product ID: " + ReportCanvas.getHexString(getProductID())
                  + " Product Release: " + ReportCanvas.getBCDString(getDeviceRelease()));
  
          UsbDescriptorParser parser = canvas.getParser();
          byte mfgIndex = getMfgIndex();
          String manufacturer = parser.getDescriptorString(mfgIndex);
          byte productIndex = getProductIndex();
          String product = parser.getDescriptorString(productIndex);
  
          canvas.writeListItem("Manufacturer " + mfgIndex + ": " + manufacturer
                  + " Product " + productIndex + ": " + product);
          canvas.closeList();
      }
  }
  