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.UsbDevice; 19 import android.util.Log; 20 21 import java.util.ArrayList; 22 23 /** 24 * @hide 25 * Class for parsing a binary stream of USB Descriptors. 26 */ 27 public final class UsbDescriptorParser { 28 private static final String TAG = "UsbDescriptorParser"; 29 public static final boolean DEBUG = false; 30 31 private final String mDeviceAddr; 32 33 private static final int MS_MIDI_1_0 = 0x0100; 34 private static final int MS_MIDI_2_0 = 0x0200; 35 36 // Descriptor Objects 37 private static final int DESCRIPTORS_ALLOC_SIZE = 128; 38 private final ArrayList<UsbDescriptor> mDescriptors; 39 40 private UsbDeviceDescriptor mDeviceDescriptor; 41 private UsbConfigDescriptor mCurConfigDescriptor; 42 private UsbInterfaceDescriptor mCurInterfaceDescriptor; 43 private UsbEndpointDescriptor mCurEndpointDescriptor; 44 45 // The AudioClass spec implemented by the AudioClass Interfaces 46 // This may well be different than the overall USB Spec. 47 // Obtained from the first AudioClass Header descriptor. 48 private int mACInterfacesSpec = UsbDeviceDescriptor.USBSPEC_1_0; 49 50 // The VideoClass spec implemented by the VideoClass Interfaces 51 // This may well be different than the overall USB Spec. 52 // Obtained from the first VidieoClass Header descriptor. 53 private int mVCInterfacesSpec = UsbDeviceDescriptor.USBSPEC_1_0; 54 55 /** 56 * Connect this parser to an existing set of already parsed descriptors. 57 * This is useful for reporting. 58 */ UsbDescriptorParser(String deviceAddr, ArrayList<UsbDescriptor> descriptors)59 public UsbDescriptorParser(String deviceAddr, ArrayList<UsbDescriptor> descriptors) { 60 mDeviceAddr = deviceAddr; 61 mDescriptors = descriptors; 62 //TODO some error checking here.... 63 mDeviceDescriptor = (UsbDeviceDescriptor) descriptors.get(0); 64 } 65 66 /** 67 * Connect this parser to an byte array containing unparsed (raw) device descriptors 68 * to be parsed (and parse them). Useful for parsing a stored descriptor buffer. 69 */ UsbDescriptorParser(String deviceAddr, byte[] rawDescriptors)70 public UsbDescriptorParser(String deviceAddr, byte[] rawDescriptors) { 71 mDeviceAddr = deviceAddr; 72 mDescriptors = new ArrayList<UsbDescriptor>(DESCRIPTORS_ALLOC_SIZE); 73 parseDescriptors(rawDescriptors); 74 } 75 getDeviceAddr()76 public String getDeviceAddr() { 77 return mDeviceAddr; 78 } 79 80 /** 81 * @return the USB Spec value associated with the Device descriptor for the 82 * descriptors stream being parsed. 83 * 84 * @throws IllegalArgumentException 85 */ getUsbSpec()86 public int getUsbSpec() { 87 if (mDeviceDescriptor != null) { 88 return mDeviceDescriptor.getSpec(); 89 } else { 90 throw new IllegalArgumentException(); 91 } 92 } 93 setACInterfaceSpec(int spec)94 public void setACInterfaceSpec(int spec) { 95 mACInterfacesSpec = spec; 96 } 97 getACInterfaceSpec()98 public int getACInterfaceSpec() { 99 return mACInterfacesSpec; 100 } 101 setVCInterfaceSpec(int spec)102 public void setVCInterfaceSpec(int spec) { 103 mVCInterfacesSpec = spec; 104 } 105 getVCInterfaceSpec()106 public int getVCInterfaceSpec() { 107 return mVCInterfacesSpec; 108 } 109 110 private class UsbDescriptorsStreamFormatException extends Exception { 111 String mMessage; UsbDescriptorsStreamFormatException(String message)112 UsbDescriptorsStreamFormatException(String message) { 113 mMessage = message; 114 } 115 toString()116 public String toString() { 117 return "Descriptor Stream Format Exception: " + mMessage; 118 } 119 } 120 121 /** 122 * The probability (as returned by getHeadsetProbability() at which we conclude 123 * the peripheral is a headset. 124 */ 125 private static final float IN_HEADSET_TRIGGER = 0.75f; 126 private static final float OUT_HEADSET_TRIGGER = 0.75f; 127 allocDescriptor(ByteStream stream)128 private UsbDescriptor allocDescriptor(ByteStream stream) 129 throws UsbDescriptorsStreamFormatException { 130 stream.resetReadCount(); 131 132 int length = stream.getUnsignedByte(); 133 byte type = stream.getByte(); 134 135 UsbDescriptor.logDescriptorName(type, length); 136 137 UsbDescriptor descriptor = null; 138 switch (type) { 139 /* 140 * Standard 141 */ 142 case UsbDescriptor.DESCRIPTORTYPE_DEVICE: 143 descriptor = mDeviceDescriptor = new UsbDeviceDescriptor(length, type); 144 break; 145 146 case UsbDescriptor.DESCRIPTORTYPE_CONFIG: 147 descriptor = mCurConfigDescriptor = new UsbConfigDescriptor(length, type); 148 if (mDeviceDescriptor != null) { 149 mDeviceDescriptor.addConfigDescriptor(mCurConfigDescriptor); 150 } else { 151 Log.e(TAG, "Config Descriptor found with no associated Device Descriptor!"); 152 throw new UsbDescriptorsStreamFormatException( 153 "Config Descriptor found with no associated Device Descriptor!"); 154 } 155 break; 156 157 case UsbDescriptor.DESCRIPTORTYPE_INTERFACE: 158 descriptor = mCurInterfaceDescriptor = new UsbInterfaceDescriptor(length, type); 159 if (mCurConfigDescriptor != null) { 160 mCurConfigDescriptor.addInterfaceDescriptor(mCurInterfaceDescriptor); 161 } else { 162 Log.e(TAG, "Interface Descriptor found with no associated Config Descriptor!"); 163 throw new UsbDescriptorsStreamFormatException( 164 "Interface Descriptor found with no associated Config Descriptor!"); 165 } 166 break; 167 168 case UsbDescriptor.DESCRIPTORTYPE_ENDPOINT: 169 descriptor = mCurEndpointDescriptor = new UsbEndpointDescriptor(length, type); 170 if (mCurInterfaceDescriptor != null) { 171 mCurInterfaceDescriptor.addEndpointDescriptor( 172 (UsbEndpointDescriptor) descriptor); 173 } else { 174 Log.e(TAG, 175 "Endpoint Descriptor found with no associated Interface Descriptor!"); 176 throw new UsbDescriptorsStreamFormatException( 177 "Endpoint Descriptor found with no associated Interface Descriptor!"); 178 } 179 break; 180 181 /* 182 * HID 183 */ 184 case UsbDescriptor.DESCRIPTORTYPE_HID: 185 descriptor = new UsbHIDDescriptor(length, type); 186 break; 187 188 /* 189 * Other 190 */ 191 case UsbDescriptor.DESCRIPTORTYPE_INTERFACEASSOC: 192 descriptor = new UsbInterfaceAssoc(length, type); 193 break; 194 195 /* 196 * Various Class Specific 197 */ 198 case UsbDescriptor.DESCRIPTORTYPE_CLASSSPECIFIC_INTERFACE: 199 if (mCurInterfaceDescriptor != null) { 200 switch (mCurInterfaceDescriptor.getUsbClass()) { 201 case UsbDescriptor.CLASSID_AUDIO: 202 descriptor = 203 UsbACInterface.allocDescriptor(this, stream, length, type); 204 if (descriptor instanceof UsbMSMidiHeader) { 205 mCurInterfaceDescriptor.setMidiHeaderInterfaceDescriptor( 206 descriptor); 207 } 208 break; 209 210 case UsbDescriptor.CLASSID_VIDEO: 211 if (DEBUG) { 212 Log.d(TAG, " UsbDescriptor.CLASSID_VIDEO"); 213 } 214 descriptor = 215 UsbVCInterface.allocDescriptor(this, stream, length, type); 216 break; 217 218 case UsbDescriptor.CLASSID_AUDIOVIDEO: 219 if (DEBUG) { 220 Log.d(TAG, " UsbDescriptor.CLASSID_AUDIOVIDEO"); 221 } 222 break; 223 224 default: 225 Log.w(TAG, " Unparsed Class-specific"); 226 break; 227 } 228 } 229 break; 230 231 case UsbDescriptor.DESCRIPTORTYPE_CLASSSPECIFIC_ENDPOINT: 232 if (mCurInterfaceDescriptor != null) { 233 int subClass = mCurInterfaceDescriptor.getUsbClass(); 234 switch (subClass) { 235 case UsbDescriptor.CLASSID_AUDIO: { 236 Byte subType = stream.getByte(); 237 if (DEBUG) { 238 Log.d(TAG, "UsbDescriptor.CLASSID_AUDIO type:0x" 239 + Integer.toHexString(type)); 240 } 241 descriptor = UsbACEndpoint.allocDescriptor(this, length, type, 242 subType); 243 } 244 break; 245 246 case UsbDescriptor.CLASSID_VIDEO: { 247 Byte subType = stream.getByte(); 248 if (DEBUG) { 249 Log.d(TAG, "UsbDescriptor.CLASSID_VIDEO type:0x" 250 + Integer.toHexString(type)); 251 } 252 descriptor = UsbVCEndpoint.allocDescriptor(this, length, type, 253 subType); 254 } 255 break; 256 257 case UsbDescriptor.CLASSID_AUDIOVIDEO: 258 if (DEBUG) { 259 Log.d(TAG, "UsbDescriptor.CLASSID_AUDIOVIDEO type:0x" 260 + Integer.toHexString(type)); 261 } 262 break; 263 264 default: 265 Log.w(TAG, " Unparsed Class-specific Endpoint:0x" 266 + Integer.toHexString(subClass)); 267 break; 268 } 269 if (mCurEndpointDescriptor != null && descriptor != null) { 270 mCurEndpointDescriptor.setClassSpecificEndpointDescriptor(descriptor); 271 } 272 } 273 break; 274 275 default: 276 break; 277 } 278 279 if (descriptor == null) { 280 // Unknown Descriptor 281 descriptor = new UsbUnknown(length, type); 282 } 283 284 return descriptor; 285 } 286 getDeviceDescriptor()287 public UsbDeviceDescriptor getDeviceDescriptor() { 288 return mDeviceDescriptor; 289 } 290 getCurInterface()291 public UsbInterfaceDescriptor getCurInterface() { 292 return mCurInterfaceDescriptor; 293 } 294 295 /** 296 * @hide 297 */ parseDescriptors(byte[] descriptors)298 public void parseDescriptors(byte[] descriptors) { 299 ByteStream stream = new ByteStream(descriptors); 300 while (stream.available() > 0) { 301 UsbDescriptor descriptor = null; 302 try { 303 descriptor = allocDescriptor(stream); 304 } catch (Exception ex) { 305 Log.e(TAG, "Exception allocating USB descriptor.", ex); 306 } 307 308 if (descriptor != null) { 309 // Parse 310 try { 311 descriptor.parseRawDescriptors(stream); 312 313 // Clean up 314 descriptor.postParse(stream); 315 } catch (Exception ex) { 316 // Clean up, compute error status 317 descriptor.postParse(stream); 318 319 // Report 320 Log.w(TAG, "Exception parsing USB descriptors. type:0x" + descriptor.getType() 321 + " status:" + descriptor.getStatus()); 322 if (DEBUG) { 323 // Show full stack trace if debugging 324 Log.e(TAG, "Exception parsing USB descriptors.", ex); 325 } 326 StackTraceElement[] stackElems = ex.getStackTrace(); 327 if (stackElems.length > 0) { 328 Log.i(TAG, " class:" + stackElems[0].getClassName() 329 + " @ " + stackElems[0].getLineNumber()); 330 } 331 if (stackElems.length > 1) { 332 Log.i(TAG, " class:" + stackElems[1].getClassName() 333 + " @ " + stackElems[1].getLineNumber()); 334 } 335 336 // Finish up 337 descriptor.setStatus(UsbDescriptor.STATUS_PARSE_EXCEPTION); 338 } finally { 339 mDescriptors.add(descriptor); 340 } 341 } 342 } 343 if (DEBUG) { 344 Log.d(TAG, "parseDescriptors() - end " + mDescriptors.size() + " descriptors."); 345 } 346 } 347 getRawDescriptors()348 public byte[] getRawDescriptors() { 349 return getRawDescriptors_native(mDeviceAddr); 350 } 351 getRawDescriptors_native(String deviceAddr)352 private native byte[] getRawDescriptors_native(String deviceAddr); 353 354 /** 355 * @hide 356 */ getDescriptorString(int stringId)357 public String getDescriptorString(int stringId) { 358 return getDescriptorString_native(mDeviceAddr, stringId); 359 } 360 getDescriptorString_native(String deviceAddr, int stringId)361 private native String getDescriptorString_native(String deviceAddr, int stringId); 362 getParsingSpec()363 public int getParsingSpec() { 364 return mDeviceDescriptor != null ? mDeviceDescriptor.getSpec() : 0; 365 } 366 getDescriptors()367 public ArrayList<UsbDescriptor> getDescriptors() { 368 return mDescriptors; 369 } 370 371 /** 372 * @hide 373 */ toAndroidUsbDeviceBuilder()374 public UsbDevice.Builder toAndroidUsbDeviceBuilder() { 375 if (mDeviceDescriptor == null) { 376 Log.e(TAG, "toAndroidUsbDevice() ERROR - No Device Descriptor"); 377 return null; 378 } 379 380 UsbDevice.Builder builder = mDeviceDescriptor.toAndroid(this); 381 if (builder == null) { 382 Log.e(TAG, "toAndroidUsbDevice() ERROR Creating Device"); 383 } 384 return builder; 385 } 386 387 /** 388 * @hide 389 */ getDescriptors(byte type)390 public ArrayList<UsbDescriptor> getDescriptors(byte type) { 391 ArrayList<UsbDescriptor> list = new ArrayList<UsbDescriptor>(); 392 for (UsbDescriptor descriptor : mDescriptors) { 393 if (descriptor.getType() == type) { 394 list.add(descriptor); 395 } 396 } 397 return list; 398 } 399 400 /** 401 * @hide 402 */ getInterfaceDescriptorsForClass(int usbClass)403 public ArrayList<UsbDescriptor> getInterfaceDescriptorsForClass(int usbClass) { 404 ArrayList<UsbDescriptor> list = new ArrayList<UsbDescriptor>(); 405 for (UsbDescriptor descriptor : mDescriptors) { 406 // ensure that this isn't an unrecognized DESCRIPTORTYPE_INTERFACE 407 if (descriptor.getType() == UsbDescriptor.DESCRIPTORTYPE_INTERFACE) { 408 if (descriptor instanceof UsbInterfaceDescriptor) { 409 UsbInterfaceDescriptor intrDesc = (UsbInterfaceDescriptor) descriptor; 410 if (intrDesc.getUsbClass() == usbClass) { 411 list.add(descriptor); 412 } 413 } else { 414 Log.w(TAG, "Unrecognized Interface l: " + descriptor.getLength() 415 + " t:0x" + Integer.toHexString(descriptor.getType())); 416 } 417 } 418 } 419 return list; 420 } 421 422 /** 423 * @hide 424 */ getACInterfaceDescriptors(byte subtype, int subclass)425 public ArrayList<UsbDescriptor> getACInterfaceDescriptors(byte subtype, int subclass) { 426 ArrayList<UsbDescriptor> list = new ArrayList<UsbDescriptor>(); 427 for (UsbDescriptor descriptor : mDescriptors) { 428 if (descriptor.getType() == UsbDescriptor.DESCRIPTORTYPE_CLASSSPECIFIC_INTERFACE) { 429 // ensure that this isn't an unrecognized DESCRIPTORTYPE_CLASSSPECIFIC_INTERFACE 430 if (descriptor instanceof UsbACInterface) { 431 UsbACInterface acDescriptor = (UsbACInterface) descriptor; 432 if (acDescriptor.getSubtype() == subtype 433 && acDescriptor.getSubclass() == subclass) { 434 list.add(descriptor); 435 } 436 } else { 437 Log.w(TAG, "Unrecognized Audio Interface len: " + descriptor.getLength() 438 + " type:0x" + Integer.toHexString(descriptor.getType())); 439 } 440 } 441 } 442 return list; 443 } 444 445 /* 446 * Attribute predicates 447 */ 448 /** 449 * @hide 450 */ hasInput()451 public boolean hasInput() { 452 if (DEBUG) { 453 Log.d(TAG, "---- hasInput()"); 454 } 455 ArrayList<UsbDescriptor> acDescriptors = 456 getACInterfaceDescriptors(UsbACInterface.ACI_INPUT_TERMINAL, 457 UsbACInterface.AUDIO_AUDIOCONTROL); 458 boolean hasInput = false; 459 for (UsbDescriptor descriptor : acDescriptors) { 460 if (descriptor instanceof UsbACTerminal) { 461 UsbACTerminal inDescr = (UsbACTerminal) descriptor; 462 // Check for input and bi-directional terminal types 463 int type = inDescr.getTerminalType(); 464 if (DEBUG) { 465 Log.d(TAG, " type:0x" + Integer.toHexString(type)); 466 } 467 int terminalCategory = type & ~0xFF; 468 if (terminalCategory != UsbTerminalTypes.TERMINAL_USB_UNDEFINED 469 && terminalCategory != UsbTerminalTypes.TERMINAL_OUT_UNDEFINED) { 470 // If not explicitly a USB connection or output, it could be an input. 471 hasInput = true; 472 break; 473 } 474 } else { 475 Log.w(TAG, "Undefined Audio Input terminal l: " + descriptor.getLength() 476 + " t:0x" + Integer.toHexString(descriptor.getType())); 477 } 478 } 479 480 if (DEBUG) { 481 Log.d(TAG, "hasInput() = " + hasInput); 482 } 483 return hasInput; 484 } 485 486 /** 487 * @hide 488 */ hasOutput()489 public boolean hasOutput() { 490 if (DEBUG) { 491 Log.d(TAG, "---- hasOutput()"); 492 } 493 ArrayList<UsbDescriptor> acDescriptors = 494 getACInterfaceDescriptors(UsbACInterface.ACI_OUTPUT_TERMINAL, 495 UsbACInterface.AUDIO_AUDIOCONTROL); 496 boolean hasOutput = false; 497 for (UsbDescriptor descriptor : acDescriptors) { 498 if (descriptor instanceof UsbACTerminal) { 499 UsbACTerminal outDescr = (UsbACTerminal) descriptor; 500 // Check for output and bi-directional terminal types 501 int type = outDescr.getTerminalType(); 502 if (DEBUG) { 503 Log.d(TAG, " type:0x" + Integer.toHexString(type)); 504 } 505 int terminalCategory = type & ~0xFF; 506 if (terminalCategory != UsbTerminalTypes.TERMINAL_USB_UNDEFINED 507 && terminalCategory != UsbTerminalTypes.TERMINAL_IN_UNDEFINED) { 508 // If not explicitly a USB connection or input, it could be an output. 509 hasOutput = true; 510 break; 511 } 512 } else { 513 Log.w(TAG, "Undefined Audio Input terminal l: " + descriptor.getLength() 514 + " t:0x" + Integer.toHexString(descriptor.getType())); 515 } 516 } 517 if (DEBUG) { 518 Log.d(TAG, "hasOutput() = " + hasOutput); 519 } 520 return hasOutput; 521 } 522 523 /** 524 * @hide 525 */ hasMic()526 public boolean hasMic() { 527 boolean hasMic = false; 528 529 ArrayList<UsbDescriptor> acDescriptors = 530 getACInterfaceDescriptors(UsbACInterface.ACI_INPUT_TERMINAL, 531 UsbACInterface.AUDIO_AUDIOCONTROL); 532 for (UsbDescriptor descriptor : acDescriptors) { 533 if (descriptor instanceof UsbACTerminal) { 534 UsbACTerminal inDescr = (UsbACTerminal) descriptor; 535 if (inDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_IN_MIC 536 || inDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_BIDIR_HEADSET 537 || inDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_BIDIR_UNDEFINED 538 || inDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_EXTERN_LINE) { 539 hasMic = true; 540 break; 541 } 542 } else { 543 Log.w(TAG, "Undefined Audio Input terminal l: " + descriptor.getLength() 544 + " t:0x" + Integer.toHexString(descriptor.getType())); 545 } 546 } 547 return hasMic; 548 } 549 550 /** 551 * @hide 552 */ hasSpeaker()553 public boolean hasSpeaker() { 554 boolean hasSpeaker = false; 555 556 ArrayList<UsbDescriptor> acDescriptors = 557 getACInterfaceDescriptors(UsbACInterface.ACI_OUTPUT_TERMINAL, 558 UsbACInterface.AUDIO_AUDIOCONTROL); 559 for (UsbDescriptor descriptor : acDescriptors) { 560 if (descriptor instanceof UsbACTerminal) { 561 UsbACTerminal outDescr = (UsbACTerminal) descriptor; 562 if (outDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_OUT_SPEAKER 563 || outDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_OUT_HEADPHONES 564 || outDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_BIDIR_HEADSET) { 565 hasSpeaker = true; 566 break; 567 } 568 } else { 569 Log.w(TAG, "Undefined Audio Output terminal l: " + descriptor.getLength() 570 + " t:0x" + Integer.toHexString(descriptor.getType())); 571 } 572 } 573 574 return hasSpeaker; 575 } 576 577 /** 578 *@ hide 579 */ hasAudioInterface()580 public boolean hasAudioInterface() { 581 ArrayList<UsbDescriptor> descriptors = 582 getInterfaceDescriptorsForClass(UsbDescriptor.CLASSID_AUDIO); 583 return !descriptors.isEmpty(); 584 } 585 586 /** 587 * Returns true only if there is a terminal whose subtype and terminal type are the same as 588 * the given values. 589 * @hide 590 */ hasAudioTerminal(int subType, int terminalType)591 public boolean hasAudioTerminal(int subType, int terminalType) { 592 for (UsbDescriptor descriptor : mDescriptors) { 593 if (descriptor instanceof UsbACTerminal) { 594 if (((UsbACTerminal) descriptor).getSubclass() == UsbDescriptor.AUDIO_AUDIOCONTROL 595 && ((UsbACTerminal) descriptor).getSubtype() == subType 596 && ((UsbACTerminal) descriptor).getTerminalType() == terminalType) { 597 return true; 598 } 599 } 600 } 601 return false; 602 } 603 604 /** 605 * Returns true only if there is an interface whose subtype is the same as the given one and 606 * terminal type is different from the given one. 607 * @hide 608 */ hasAudioTerminalExcludeType(int subType, int excludedTerminalType)609 public boolean hasAudioTerminalExcludeType(int subType, int excludedTerminalType) { 610 for (UsbDescriptor descriptor : mDescriptors) { 611 if (descriptor instanceof UsbACTerminal) { 612 if (((UsbACTerminal) descriptor).getSubclass() == UsbDescriptor.AUDIO_AUDIOCONTROL 613 && ((UsbACTerminal) descriptor).getSubtype() == subType 614 && ((UsbACTerminal) descriptor).getTerminalType() != excludedTerminalType) { 615 return true; 616 } 617 } 618 } 619 return false; 620 } 621 622 /** 623 * @hide 624 */ hasAudioPlayback()625 public boolean hasAudioPlayback() { 626 return hasAudioTerminalExcludeType( 627 UsbACInterface.ACI_OUTPUT_TERMINAL, UsbTerminalTypes.TERMINAL_USB_STREAMING) 628 && hasAudioTerminal( 629 UsbACInterface.ACI_INPUT_TERMINAL, UsbTerminalTypes.TERMINAL_USB_STREAMING); 630 } 631 632 /** 633 * @hide 634 */ hasAudioCapture()635 public boolean hasAudioCapture() { 636 return hasAudioTerminalExcludeType( 637 UsbACInterface.ACI_INPUT_TERMINAL, UsbTerminalTypes.TERMINAL_USB_STREAMING) 638 && hasAudioTerminal( 639 UsbACInterface.ACI_OUTPUT_TERMINAL, 640 UsbTerminalTypes.TERMINAL_USB_STREAMING); 641 } 642 643 /** 644 * @hide 645 */ hasVideoCapture()646 public boolean hasVideoCapture() { 647 for (UsbDescriptor descriptor : mDescriptors) { 648 if (descriptor instanceof UsbVCInputTerminal) { 649 return true; 650 } 651 } 652 return false; 653 } 654 655 /** 656 * @hide 657 */ hasVideoPlayback()658 public boolean hasVideoPlayback() { 659 for (UsbDescriptor descriptor : mDescriptors) { 660 if (descriptor instanceof UsbVCOutputTerminal) { 661 return true; 662 } 663 } 664 return false; 665 } 666 667 /** 668 * @hide 669 */ hasHIDInterface()670 public boolean hasHIDInterface() { 671 ArrayList<UsbDescriptor> descriptors = 672 getInterfaceDescriptorsForClass(UsbDescriptor.CLASSID_HID); 673 return !descriptors.isEmpty(); 674 } 675 676 /** 677 * @hide 678 */ hasStorageInterface()679 public boolean hasStorageInterface() { 680 ArrayList<UsbDescriptor> descriptors = 681 getInterfaceDescriptorsForClass(UsbDescriptor.CLASSID_STORAGE); 682 return !descriptors.isEmpty(); 683 } 684 685 /** 686 * @hide 687 */ hasMIDIInterface()688 public boolean hasMIDIInterface() { 689 ArrayList<UsbDescriptor> descriptors = 690 getInterfaceDescriptorsForClass(UsbDescriptor.CLASSID_AUDIO); 691 for (UsbDescriptor descriptor : descriptors) { 692 // enusure that this isn't an unrecognized interface descriptor 693 if (descriptor instanceof UsbInterfaceDescriptor) { 694 UsbInterfaceDescriptor interfaceDescriptor = (UsbInterfaceDescriptor) descriptor; 695 if (interfaceDescriptor.getUsbSubclass() == UsbDescriptor.AUDIO_MIDISTREAMING) { 696 return true; 697 } 698 } else { 699 Log.w(TAG, "Undefined Audio Class Interface l: " + descriptor.getLength() 700 + " t:0x" + Integer.toHexString(descriptor.getType())); 701 } 702 } 703 return false; 704 } 705 706 /** 707 * @hide 708 */ containsUniversalMidiDeviceEndpoint()709 public boolean containsUniversalMidiDeviceEndpoint() { 710 ArrayList<UsbInterfaceDescriptor> interfaceDescriptors = 711 findUniversalMidiInterfaceDescriptors(); 712 return doesInterfaceContainEndpoint(interfaceDescriptors); 713 } 714 715 /** 716 * @hide 717 */ containsLegacyMidiDeviceEndpoint()718 public boolean containsLegacyMidiDeviceEndpoint() { 719 ArrayList<UsbInterfaceDescriptor> interfaceDescriptors = 720 findLegacyMidiInterfaceDescriptors(); 721 return doesInterfaceContainEndpoint(interfaceDescriptors); 722 } 723 724 /** 725 * @hide 726 */ doesInterfaceContainEndpoint( ArrayList<UsbInterfaceDescriptor> interfaceDescriptors)727 public boolean doesInterfaceContainEndpoint( 728 ArrayList<UsbInterfaceDescriptor> interfaceDescriptors) { 729 int outputCount = 0; 730 int inputCount = 0; 731 for (int interfaceIndex = 0; interfaceIndex < interfaceDescriptors.size(); 732 interfaceIndex++) { 733 UsbInterfaceDescriptor interfaceDescriptor = interfaceDescriptors.get(interfaceIndex); 734 for (int endpointIndex = 0; endpointIndex < interfaceDescriptor.getNumEndpoints(); 735 endpointIndex++) { 736 UsbEndpointDescriptor endpoint = 737 interfaceDescriptor.getEndpointDescriptor(endpointIndex); 738 // 0 is output, 1 << 7 is input. 739 if (endpoint.getDirection() == 0) { 740 outputCount++; 741 } else { 742 inputCount++; 743 } 744 } 745 } 746 return (outputCount > 0) || (inputCount > 0); 747 } 748 749 /** 750 * @hide 751 */ findUniversalMidiInterfaceDescriptors()752 public ArrayList<UsbInterfaceDescriptor> findUniversalMidiInterfaceDescriptors() { 753 return findMidiInterfaceDescriptors(MS_MIDI_2_0); 754 } 755 756 /** 757 * @hide 758 */ findLegacyMidiInterfaceDescriptors()759 public ArrayList<UsbInterfaceDescriptor> findLegacyMidiInterfaceDescriptors() { 760 return findMidiInterfaceDescriptors(MS_MIDI_1_0); 761 } 762 763 /** 764 * @hide 765 */ findMidiInterfaceDescriptors(int type)766 private ArrayList<UsbInterfaceDescriptor> findMidiInterfaceDescriptors(int type) { 767 int count = 0; 768 ArrayList<UsbDescriptor> descriptors = 769 getInterfaceDescriptorsForClass(UsbDescriptor.CLASSID_AUDIO); 770 ArrayList<UsbInterfaceDescriptor> midiInterfaces = 771 new ArrayList<UsbInterfaceDescriptor>(); 772 773 for (UsbDescriptor descriptor : descriptors) { 774 // ensure that this isn't an unrecognized interface descriptor 775 if (descriptor instanceof UsbInterfaceDescriptor) { 776 UsbInterfaceDescriptor interfaceDescriptor = (UsbInterfaceDescriptor) descriptor; 777 if (interfaceDescriptor.getUsbSubclass() == UsbDescriptor.AUDIO_MIDISTREAMING) { 778 UsbDescriptor midiHeaderDescriptor = 779 interfaceDescriptor.getMidiHeaderInterfaceDescriptor(); 780 if (midiHeaderDescriptor != null) { 781 if (midiHeaderDescriptor instanceof UsbMSMidiHeader) { 782 UsbMSMidiHeader midiHeader = 783 (UsbMSMidiHeader) midiHeaderDescriptor; 784 if (midiHeader.getMidiStreamingClass() == type) { 785 midiInterfaces.add(interfaceDescriptor); 786 } 787 } 788 } 789 } 790 } else { 791 Log.w(TAG, "Undefined Audio Class Interface l: " + descriptor.getLength() 792 + " t:0x" + Integer.toHexString(descriptor.getType())); 793 } 794 } 795 return midiInterfaces; 796 } 797 798 /** 799 * @hide 800 */ calculateMidiInterfaceDescriptorsCount()801 public int calculateMidiInterfaceDescriptorsCount() { 802 int count = 0; 803 ArrayList<UsbDescriptor> descriptors = 804 getInterfaceDescriptorsForClass(UsbDescriptor.CLASSID_AUDIO); 805 for (UsbDescriptor descriptor : descriptors) { 806 // ensure that this isn't an unrecognized interface descriptor 807 if (descriptor instanceof UsbInterfaceDescriptor) { 808 UsbInterfaceDescriptor interfaceDescriptor = (UsbInterfaceDescriptor) descriptor; 809 if (interfaceDescriptor.getUsbSubclass() == UsbDescriptor.AUDIO_MIDISTREAMING) { 810 UsbDescriptor midiHeaderDescriptor = 811 interfaceDescriptor.getMidiHeaderInterfaceDescriptor(); 812 if (midiHeaderDescriptor != null) { 813 if (midiHeaderDescriptor instanceof UsbMSMidiHeader) { 814 UsbMSMidiHeader midiHeader = 815 (UsbMSMidiHeader) midiHeaderDescriptor; 816 count++; 817 } 818 } 819 } 820 } else { 821 Log.w(TAG, "Undefined Audio Class Interface l: " + descriptor.getLength() 822 + " t:0x" + Integer.toHexString(descriptor.getType())); 823 } 824 } 825 return count; 826 } 827 828 /** 829 * @hide 830 */ calculateNumLegacyMidiPorts(boolean isOutput)831 private int calculateNumLegacyMidiPorts(boolean isOutput) { 832 // Only look at the first config. 833 UsbConfigDescriptor configDescriptor = null; 834 for (UsbDescriptor descriptor : mDescriptors) { 835 if (descriptor.getType() == UsbDescriptor.DESCRIPTORTYPE_CONFIG) { 836 if (descriptor instanceof UsbConfigDescriptor) { 837 configDescriptor = (UsbConfigDescriptor) descriptor; 838 break; 839 } else { 840 Log.w(TAG, "Unrecognized Config l: " + descriptor.getLength() 841 + " t:0x" + Integer.toHexString(descriptor.getType())); 842 } 843 } 844 } 845 if (configDescriptor == null) { 846 Log.w(TAG, "Config not found"); 847 return 0; 848 } 849 850 ArrayList<UsbInterfaceDescriptor> legacyMidiInterfaceDescriptors = 851 new ArrayList<UsbInterfaceDescriptor>(); 852 for (UsbInterfaceDescriptor interfaceDescriptor 853 : configDescriptor.getInterfaceDescriptors()) { 854 if (interfaceDescriptor.getUsbClass() == UsbDescriptor.CLASSID_AUDIO) { 855 if (interfaceDescriptor.getUsbSubclass() == UsbDescriptor.AUDIO_MIDISTREAMING) { 856 UsbDescriptor midiHeaderDescriptor = 857 interfaceDescriptor.getMidiHeaderInterfaceDescriptor(); 858 if (midiHeaderDescriptor != null) { 859 if (midiHeaderDescriptor instanceof UsbMSMidiHeader) { 860 UsbMSMidiHeader midiHeader = 861 (UsbMSMidiHeader) midiHeaderDescriptor; 862 if (midiHeader.getMidiStreamingClass() == MS_MIDI_1_0) { 863 legacyMidiInterfaceDescriptors.add(interfaceDescriptor); 864 } 865 } 866 } 867 } 868 } 869 } 870 871 int count = 0; 872 for (UsbInterfaceDescriptor interfaceDescriptor : legacyMidiInterfaceDescriptors) { 873 for (int i = 0; i < interfaceDescriptor.getNumEndpoints(); i++) { 874 UsbEndpointDescriptor endpoint = 875 interfaceDescriptor.getEndpointDescriptor(i); 876 // 0 is output, 1 << 7 is input. 877 if ((endpoint.getDirection() == 0) == isOutput) { 878 UsbDescriptor classSpecificEndpointDescriptor = 879 endpoint.getClassSpecificEndpointDescriptor(); 880 if (classSpecificEndpointDescriptor != null 881 && (classSpecificEndpointDescriptor instanceof UsbACMidi10Endpoint)) { 882 UsbACMidi10Endpoint midiEndpoint = 883 (UsbACMidi10Endpoint) classSpecificEndpointDescriptor; 884 count += midiEndpoint.getNumJacks(); 885 } 886 } 887 } 888 } 889 return count; 890 } 891 892 /** 893 * @hide 894 */ calculateNumLegacyMidiInputs()895 public int calculateNumLegacyMidiInputs() { 896 return calculateNumLegacyMidiPorts(false /*isOutput*/); 897 } 898 899 /** 900 * @hide 901 */ calculateNumLegacyMidiOutputs()902 public int calculateNumLegacyMidiOutputs() { 903 return calculateNumLegacyMidiPorts(true /*isOutput*/); 904 } 905 906 /** 907 * @hide 908 */ getInputHeadsetProbability()909 public float getInputHeadsetProbability() { 910 if (hasMIDIInterface()) { 911 return 0.0f; 912 } 913 914 float probability = 0.0f; 915 916 // Look for a microphone 917 boolean hasMic = hasMic(); 918 919 // Look for a "speaker" 920 boolean hasSpeaker = hasSpeaker(); 921 922 if (hasMic && hasSpeaker) { 923 probability += 0.75f; 924 } 925 926 if (hasMic && hasHIDInterface()) { 927 probability += 0.25f; 928 } 929 930 return probability; 931 } 932 933 /** 934 * getInputHeadsetProbability() reports a probability of a USB Input peripheral being a 935 * headset. The probability range is between 0.0f (definitely NOT a headset) and 936 * 1.0f (definitely IS a headset). A probability of 0.75f seems sufficient 937 * to count on the peripheral being a headset. 938 */ isInputHeadset()939 public boolean isInputHeadset() { 940 return getInputHeadsetProbability() >= IN_HEADSET_TRIGGER; 941 } 942 943 // TODO: Up/Downmix process descriptor is not yet parsed, which may affect the result here. getMaximumChannelCount()944 private int getMaximumChannelCount() { 945 int maxChannelCount = 0; 946 for (UsbDescriptor descriptor : mDescriptors) { 947 if (descriptor instanceof UsbAudioChannelCluster) { 948 maxChannelCount = Math.max(maxChannelCount, 949 ((UsbAudioChannelCluster) descriptor).getChannelCount()); 950 } 951 } 952 return maxChannelCount; 953 } 954 955 /** 956 * @hide 957 */ getOutputHeadsetLikelihood()958 public float getOutputHeadsetLikelihood() { 959 if (hasMIDIInterface()) { 960 return 0.0f; 961 } 962 963 float likelihood = 0.0f; 964 ArrayList<UsbDescriptor> acDescriptors; 965 966 // Look for a "speaker" 967 boolean hasSpeaker = false; 968 boolean hasAssociatedInputTerminal = false; 969 boolean hasHeadphoneOrHeadset = false; 970 acDescriptors = 971 getACInterfaceDescriptors(UsbACInterface.ACI_OUTPUT_TERMINAL, 972 UsbACInterface.AUDIO_AUDIOCONTROL); 973 for (UsbDescriptor descriptor : acDescriptors) { 974 if (descriptor instanceof UsbACTerminal) { 975 UsbACTerminal outDescr = (UsbACTerminal) descriptor; 976 if (outDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_OUT_SPEAKER) { 977 hasSpeaker = true; 978 if (outDescr.getAssocTerminal() != 0x0) { 979 hasAssociatedInputTerminal = true; 980 } 981 } else if (outDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_OUT_HEADPHONES 982 || outDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_BIDIR_HEADSET) { 983 hasHeadphoneOrHeadset = true; 984 } 985 } else { 986 Log.w(TAG, "Undefined Audio Output terminal l: " + descriptor.getLength() 987 + " t:0x" + Integer.toHexString(descriptor.getType())); 988 } 989 } 990 991 if (hasHeadphoneOrHeadset) { 992 likelihood += 0.75f; 993 } else if (hasSpeaker) { 994 // The device only reports output terminal as speaker. Try to figure out if the device 995 // is a headset or not by checking if it has associated input terminal and if multiple 996 // channels are supported or not. 997 likelihood += 0.5f; 998 if (hasAssociatedInputTerminal) { 999 likelihood += 0.25f; 1000 } 1001 if (getMaximumChannelCount() > 2) { 1002 // When multiple channels are supported, it is less likely to be a headset. 1003 likelihood -= 0.25f; 1004 } 1005 } 1006 1007 if ((hasHeadphoneOrHeadset || hasSpeaker) && hasHIDInterface()) { 1008 likelihood += 0.25f; 1009 } 1010 1011 return likelihood; 1012 } 1013 1014 /** 1015 * getOutputHeadsetProbability() reports a probability of a USB Output peripheral being a 1016 * headset. The probability range is between 0.0f (definitely NOT a headset) and 1017 * 1.0f (definitely IS a headset). A probability of 0.75f seems sufficient 1018 * to count on the peripheral being a headset. 1019 */ isOutputHeadset()1020 public boolean isOutputHeadset() { 1021 return getOutputHeadsetLikelihood() >= OUT_HEADSET_TRIGGER; 1022 } 1023 1024 /** 1025 * isDock() indicates if the connected USB output peripheral is a docking station with 1026 * audio output. 1027 * A valid audio dock must declare only one audio output control terminal of type 1028 * TERMINAL_EXTERN_DIGITAL. 1029 */ isDock()1030 public boolean isDock() { 1031 if (hasMIDIInterface() || hasHIDInterface()) { 1032 return false; 1033 } 1034 1035 ArrayList<UsbDescriptor> acDescriptors = 1036 getACInterfaceDescriptors(UsbACInterface.ACI_OUTPUT_TERMINAL, 1037 UsbACInterface.AUDIO_AUDIOCONTROL); 1038 1039 if (acDescriptors.size() != 1) { 1040 return false; 1041 } 1042 1043 if (acDescriptors.get(0) instanceof UsbACTerminal) { 1044 UsbACTerminal outDescr = (UsbACTerminal) acDescriptors.get(0); 1045 if (outDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_EXTERN_DIGITAL) { 1046 return true; 1047 } 1048 } else { 1049 Log.w(TAG, "Undefined Audio Output terminal l: " + acDescriptors.get(0).getLength() 1050 + " t:0x" + Integer.toHexString(acDescriptors.get(0).getType())); 1051 } 1052 return false; 1053 } 1054 1055 } 1056