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 // Descriptor Objects 34 private static final int DESCRIPTORS_ALLOC_SIZE = 128; 35 private final ArrayList<UsbDescriptor> mDescriptors; 36 37 private UsbDeviceDescriptor mDeviceDescriptor; 38 private UsbConfigDescriptor mCurConfigDescriptor; 39 private UsbInterfaceDescriptor mCurInterfaceDescriptor; 40 41 // The AudioClass spec implemented by the AudioClass Interfaces 42 // This may well be different than the overall USB Spec. 43 // Obtained from the first AudioClass Header descriptor. 44 private int mACInterfacesSpec = UsbDeviceDescriptor.USBSPEC_1_0; 45 46 // The VideoClass spec implemented by the VideoClass Interfaces 47 // This may well be different than the overall USB Spec. 48 // Obtained from the first VidieoClass Header descriptor. 49 private int mVCInterfacesSpec = UsbDeviceDescriptor.USBSPEC_1_0; 50 51 /** 52 * Connect this parser to an existing set of already parsed descriptors. 53 * This is useful for reporting. 54 */ UsbDescriptorParser(String deviceAddr, ArrayList<UsbDescriptor> descriptors)55 public UsbDescriptorParser(String deviceAddr, ArrayList<UsbDescriptor> descriptors) { 56 mDeviceAddr = deviceAddr; 57 mDescriptors = descriptors; 58 //TODO some error checking here.... 59 mDeviceDescriptor = (UsbDeviceDescriptor) descriptors.get(0); 60 } 61 62 /** 63 * Connect this parser to an byte array containing unparsed (raw) device descriptors 64 * to be parsed (and parse them). Useful for parsing a stored descriptor buffer. 65 */ UsbDescriptorParser(String deviceAddr, byte[] rawDescriptors)66 public UsbDescriptorParser(String deviceAddr, byte[] rawDescriptors) { 67 mDeviceAddr = deviceAddr; 68 mDescriptors = new ArrayList<UsbDescriptor>(DESCRIPTORS_ALLOC_SIZE); 69 parseDescriptors(rawDescriptors); 70 } 71 getDeviceAddr()72 public String getDeviceAddr() { 73 return mDeviceAddr; 74 } 75 76 /** 77 * @return the USB Spec value associated with the Device descriptor for the 78 * descriptors stream being parsed. 79 * 80 * @throws IllegalArgumentException 81 */ getUsbSpec()82 public int getUsbSpec() { 83 if (mDeviceDescriptor != null) { 84 return mDeviceDescriptor.getSpec(); 85 } else { 86 throw new IllegalArgumentException(); 87 } 88 } 89 setACInterfaceSpec(int spec)90 public void setACInterfaceSpec(int spec) { 91 mACInterfacesSpec = spec; 92 } 93 getACInterfaceSpec()94 public int getACInterfaceSpec() { 95 return mACInterfacesSpec; 96 } 97 setVCInterfaceSpec(int spec)98 public void setVCInterfaceSpec(int spec) { 99 mVCInterfacesSpec = spec; 100 } 101 getVCInterfaceSpec()102 public int getVCInterfaceSpec() { 103 return mVCInterfacesSpec; 104 } 105 106 private class UsbDescriptorsStreamFormatException extends Exception { 107 String mMessage; UsbDescriptorsStreamFormatException(String message)108 UsbDescriptorsStreamFormatException(String message) { 109 mMessage = message; 110 } 111 toString()112 public String toString() { 113 return "Descriptor Stream Format Exception: " + mMessage; 114 } 115 } 116 117 /** 118 * The probability (as returned by getHeadsetProbability() at which we conclude 119 * the peripheral is a headset. 120 */ 121 private static final float IN_HEADSET_TRIGGER = 0.75f; 122 private static final float OUT_HEADSET_TRIGGER = 0.75f; 123 allocDescriptor(ByteStream stream)124 private UsbDescriptor allocDescriptor(ByteStream stream) 125 throws UsbDescriptorsStreamFormatException { 126 stream.resetReadCount(); 127 128 int length = stream.getUnsignedByte(); 129 byte type = stream.getByte(); 130 131 UsbDescriptor.logDescriptorName(type, length); 132 133 UsbDescriptor descriptor = null; 134 switch (type) { 135 /* 136 * Standard 137 */ 138 case UsbDescriptor.DESCRIPTORTYPE_DEVICE: 139 descriptor = mDeviceDescriptor = new UsbDeviceDescriptor(length, type); 140 break; 141 142 case UsbDescriptor.DESCRIPTORTYPE_CONFIG: 143 descriptor = mCurConfigDescriptor = new UsbConfigDescriptor(length, type); 144 if (mDeviceDescriptor != null) { 145 mDeviceDescriptor.addConfigDescriptor(mCurConfigDescriptor); 146 } else { 147 Log.e(TAG, "Config Descriptor found with no associated Device Descriptor!"); 148 throw new UsbDescriptorsStreamFormatException( 149 "Config Descriptor found with no associated Device Descriptor!"); 150 } 151 break; 152 153 case UsbDescriptor.DESCRIPTORTYPE_INTERFACE: 154 descriptor = mCurInterfaceDescriptor = new UsbInterfaceDescriptor(length, type); 155 if (mCurConfigDescriptor != null) { 156 mCurConfigDescriptor.addInterfaceDescriptor(mCurInterfaceDescriptor); 157 } else { 158 Log.e(TAG, "Interface Descriptor found with no associated Config Descriptor!"); 159 throw new UsbDescriptorsStreamFormatException( 160 "Interface Descriptor found with no associated Config Descriptor!"); 161 } 162 break; 163 164 case UsbDescriptor.DESCRIPTORTYPE_ENDPOINT: 165 descriptor = new UsbEndpointDescriptor(length, type); 166 if (mCurInterfaceDescriptor != null) { 167 mCurInterfaceDescriptor.addEndpointDescriptor( 168 (UsbEndpointDescriptor) descriptor); 169 } else { 170 Log.e(TAG, 171 "Endpoint Descriptor found with no associated Interface Descriptor!"); 172 throw new UsbDescriptorsStreamFormatException( 173 "Endpoint Descriptor found with no associated Interface Descriptor!"); 174 } 175 break; 176 177 /* 178 * HID 179 */ 180 case UsbDescriptor.DESCRIPTORTYPE_HID: 181 descriptor = new UsbHIDDescriptor(length, type); 182 break; 183 184 /* 185 * Other 186 */ 187 case UsbDescriptor.DESCRIPTORTYPE_INTERFACEASSOC: 188 descriptor = new UsbInterfaceAssoc(length, type); 189 break; 190 191 /* 192 * Various Class Specific 193 */ 194 case UsbDescriptor.DESCRIPTORTYPE_CLASSSPECIFIC_INTERFACE: 195 if (mCurInterfaceDescriptor != null) { 196 switch (mCurInterfaceDescriptor.getUsbClass()) { 197 case UsbDescriptor.CLASSID_AUDIO: 198 descriptor = UsbACInterface.allocDescriptor(this, stream, length, type); 199 break; 200 201 case UsbDescriptor.CLASSID_VIDEO: 202 if (DEBUG) { 203 Log.d(TAG, " UsbDescriptor.CLASSID_VIDEO"); 204 } 205 descriptor = UsbVCInterface.allocDescriptor(this, stream, length, type); 206 break; 207 208 case UsbDescriptor.CLASSID_AUDIOVIDEO: 209 if (DEBUG) { 210 Log.d(TAG, " UsbDescriptor.CLASSID_AUDIOVIDEO"); 211 } 212 break; 213 214 default: 215 Log.w(TAG, " Unparsed Class-specific"); 216 break; 217 } 218 } 219 break; 220 221 case UsbDescriptor.DESCRIPTORTYPE_CLASSSPECIFIC_ENDPOINT: 222 if (mCurInterfaceDescriptor != null) { 223 int subClass = mCurInterfaceDescriptor.getUsbClass(); 224 switch (subClass) { 225 case UsbDescriptor.CLASSID_AUDIO: 226 descriptor = UsbACEndpoint.allocDescriptor(this, length, type); 227 break; 228 229 case UsbDescriptor.CLASSID_VIDEO: { 230 Byte subtype = stream.getByte(); 231 if (DEBUG) { 232 Log.d(TAG, "UsbDescriptor.CLASSID_VIDEO type:0x" 233 + Integer.toHexString(type)); 234 } 235 descriptor = UsbVCEndpoint.allocDescriptor(this, length, type, subtype); 236 } 237 break; 238 239 case UsbDescriptor.CLASSID_AUDIOVIDEO: 240 if (DEBUG) { 241 Log.d(TAG, "UsbDescriptor.CLASSID_AUDIOVIDEO type:0x" 242 + Integer.toHexString(type)); 243 } 244 break; 245 246 default: 247 Log.w(TAG, " Unparsed Class-specific Endpoint:0x" 248 + Integer.toHexString(subClass)); 249 break; 250 } 251 } 252 break; 253 254 default: 255 break; 256 } 257 258 if (descriptor == null) { 259 // Unknown Descriptor 260 descriptor = new UsbUnknown(length, type); 261 } 262 263 return descriptor; 264 } 265 getDeviceDescriptor()266 public UsbDeviceDescriptor getDeviceDescriptor() { 267 return mDeviceDescriptor; 268 } 269 getCurInterface()270 public UsbInterfaceDescriptor getCurInterface() { 271 return mCurInterfaceDescriptor; 272 } 273 274 /** 275 * @hide 276 */ parseDescriptors(byte[] descriptors)277 public void parseDescriptors(byte[] descriptors) { 278 ByteStream stream = new ByteStream(descriptors); 279 while (stream.available() > 0) { 280 UsbDescriptor descriptor = null; 281 try { 282 descriptor = allocDescriptor(stream); 283 } catch (Exception ex) { 284 Log.e(TAG, "Exception allocating USB descriptor.", ex); 285 } 286 287 if (descriptor != null) { 288 // Parse 289 try { 290 descriptor.parseRawDescriptors(stream); 291 292 // Clean up 293 descriptor.postParse(stream); 294 } catch (Exception ex) { 295 // Clean up, compute error status 296 descriptor.postParse(stream); 297 298 // Report 299 Log.w(TAG, "Exception parsing USB descriptors. type:0x" + descriptor.getType() 300 + " status:" + descriptor.getStatus()); 301 if (DEBUG) { 302 // Show full stack trace if debugging 303 Log.e(TAG, "Exception parsing USB descriptors.", ex); 304 } 305 StackTraceElement[] stackElems = ex.getStackTrace(); 306 if (stackElems.length > 0) { 307 Log.i(TAG, " class:" + stackElems[0].getClassName() 308 + " @ " + stackElems[0].getLineNumber()); 309 } 310 if (stackElems.length > 1) { 311 Log.i(TAG, " class:" + stackElems[1].getClassName() 312 + " @ " + stackElems[1].getLineNumber()); 313 } 314 315 // Finish up 316 descriptor.setStatus(UsbDescriptor.STATUS_PARSE_EXCEPTION); 317 } finally { 318 mDescriptors.add(descriptor); 319 } 320 } 321 } 322 if (DEBUG) { 323 Log.d(TAG, "parseDescriptors() - end " + mDescriptors.size() + " descriptors."); 324 } 325 } 326 getRawDescriptors()327 public byte[] getRawDescriptors() { 328 return getRawDescriptors_native(mDeviceAddr); 329 } 330 getRawDescriptors_native(String deviceAddr)331 private native byte[] getRawDescriptors_native(String deviceAddr); 332 333 /** 334 * @hide 335 */ getDescriptorString(int stringId)336 public String getDescriptorString(int stringId) { 337 return getDescriptorString_native(mDeviceAddr, stringId); 338 } 339 getDescriptorString_native(String deviceAddr, int stringId)340 private native String getDescriptorString_native(String deviceAddr, int stringId); 341 getParsingSpec()342 public int getParsingSpec() { 343 return mDeviceDescriptor != null ? mDeviceDescriptor.getSpec() : 0; 344 } 345 getDescriptors()346 public ArrayList<UsbDescriptor> getDescriptors() { 347 return mDescriptors; 348 } 349 350 /** 351 * @hide 352 */ toAndroidUsbDeviceBuilder()353 public UsbDevice.Builder toAndroidUsbDeviceBuilder() { 354 if (mDeviceDescriptor == null) { 355 Log.e(TAG, "toAndroidUsbDevice() ERROR - No Device Descriptor"); 356 return null; 357 } 358 359 UsbDevice.Builder builder = mDeviceDescriptor.toAndroid(this); 360 if (builder == null) { 361 Log.e(TAG, "toAndroidUsbDevice() ERROR Creating Device"); 362 } 363 return builder; 364 } 365 366 /** 367 * @hide 368 */ getDescriptors(byte type)369 public ArrayList<UsbDescriptor> getDescriptors(byte type) { 370 ArrayList<UsbDescriptor> list = new ArrayList<UsbDescriptor>(); 371 for (UsbDescriptor descriptor : mDescriptors) { 372 if (descriptor.getType() == type) { 373 list.add(descriptor); 374 } 375 } 376 return list; 377 } 378 379 /** 380 * @hide 381 */ getInterfaceDescriptorsForClass(int usbClass)382 public ArrayList<UsbDescriptor> getInterfaceDescriptorsForClass(int usbClass) { 383 ArrayList<UsbDescriptor> list = new ArrayList<UsbDescriptor>(); 384 for (UsbDescriptor descriptor : mDescriptors) { 385 // ensure that this isn't an unrecognized DESCRIPTORTYPE_INTERFACE 386 if (descriptor.getType() == UsbDescriptor.DESCRIPTORTYPE_INTERFACE) { 387 if (descriptor instanceof UsbInterfaceDescriptor) { 388 UsbInterfaceDescriptor intrDesc = (UsbInterfaceDescriptor) descriptor; 389 if (intrDesc.getUsbClass() == usbClass) { 390 list.add(descriptor); 391 } 392 } else { 393 Log.w(TAG, "Unrecognized Interface l: " + descriptor.getLength() 394 + " t:0x" + Integer.toHexString(descriptor.getType())); 395 } 396 } 397 } 398 return list; 399 } 400 401 /** 402 * @hide 403 */ getACInterfaceDescriptors(byte subtype, int subclass)404 public ArrayList<UsbDescriptor> getACInterfaceDescriptors(byte subtype, int subclass) { 405 ArrayList<UsbDescriptor> list = new ArrayList<UsbDescriptor>(); 406 for (UsbDescriptor descriptor : mDescriptors) { 407 if (descriptor.getType() == UsbDescriptor.DESCRIPTORTYPE_CLASSSPECIFIC_INTERFACE) { 408 // ensure that this isn't an unrecognized DESCRIPTORTYPE_CLASSSPECIFIC_INTERFACE 409 if (descriptor instanceof UsbACInterface) { 410 UsbACInterface acDescriptor = (UsbACInterface) descriptor; 411 if (acDescriptor.getSubtype() == subtype 412 && acDescriptor.getSubclass() == subclass) { 413 list.add(descriptor); 414 } 415 } else { 416 Log.w(TAG, "Unrecognized Audio Interface len: " + descriptor.getLength() 417 + " type:0x" + Integer.toHexString(descriptor.getType())); 418 } 419 } 420 } 421 return list; 422 } 423 424 /* 425 * Attribute predicates 426 */ 427 /** 428 * @hide 429 */ hasInput()430 public boolean hasInput() { 431 if (DEBUG) { 432 Log.d(TAG, "---- hasInput()"); 433 } 434 ArrayList<UsbDescriptor> acDescriptors = 435 getACInterfaceDescriptors(UsbACInterface.ACI_INPUT_TERMINAL, 436 UsbACInterface.AUDIO_AUDIOCONTROL); 437 boolean hasInput = false; 438 for (UsbDescriptor descriptor : acDescriptors) { 439 if (descriptor instanceof UsbACTerminal) { 440 UsbACTerminal inDescr = (UsbACTerminal) descriptor; 441 // Check for input and bi-directional terminal types 442 int type = inDescr.getTerminalType(); 443 if (DEBUG) { 444 Log.d(TAG, " type:0x" + Integer.toHexString(type)); 445 } 446 int terminalCategory = type & ~0xFF; 447 if (terminalCategory != UsbTerminalTypes.TERMINAL_USB_UNDEFINED 448 && terminalCategory != UsbTerminalTypes.TERMINAL_OUT_UNDEFINED) { 449 // If not explicitly a USB connection or output, it could be an input. 450 hasInput = true; 451 break; 452 } 453 } else { 454 Log.w(TAG, "Undefined Audio Input terminal l: " + descriptor.getLength() 455 + " t:0x" + Integer.toHexString(descriptor.getType())); 456 } 457 } 458 459 if (DEBUG) { 460 Log.d(TAG, "hasInput() = " + hasInput); 461 } 462 return hasInput; 463 } 464 465 /** 466 * @hide 467 */ hasOutput()468 public boolean hasOutput() { 469 if (DEBUG) { 470 Log.d(TAG, "---- hasOutput()"); 471 } 472 ArrayList<UsbDescriptor> acDescriptors = 473 getACInterfaceDescriptors(UsbACInterface.ACI_OUTPUT_TERMINAL, 474 UsbACInterface.AUDIO_AUDIOCONTROL); 475 boolean hasOutput = false; 476 for (UsbDescriptor descriptor : acDescriptors) { 477 if (descriptor instanceof UsbACTerminal) { 478 UsbACTerminal outDescr = (UsbACTerminal) descriptor; 479 // Check for output and bi-directional terminal types 480 int type = outDescr.getTerminalType(); 481 if (DEBUG) { 482 Log.d(TAG, " type:0x" + Integer.toHexString(type)); 483 } 484 int terminalCategory = type & ~0xFF; 485 if (terminalCategory != UsbTerminalTypes.TERMINAL_USB_UNDEFINED 486 && terminalCategory != UsbTerminalTypes.TERMINAL_IN_UNDEFINED) { 487 // If not explicitly a USB connection or input, it could be an output. 488 hasOutput = true; 489 break; 490 } 491 } else { 492 Log.w(TAG, "Undefined Audio Input terminal l: " + descriptor.getLength() 493 + " t:0x" + Integer.toHexString(descriptor.getType())); 494 } 495 } 496 if (DEBUG) { 497 Log.d(TAG, "hasOutput() = " + hasOutput); 498 } 499 return hasOutput; 500 } 501 502 /** 503 * @hide 504 */ hasMic()505 public boolean hasMic() { 506 boolean hasMic = false; 507 508 ArrayList<UsbDescriptor> acDescriptors = 509 getACInterfaceDescriptors(UsbACInterface.ACI_INPUT_TERMINAL, 510 UsbACInterface.AUDIO_AUDIOCONTROL); 511 for (UsbDescriptor descriptor : acDescriptors) { 512 if (descriptor instanceof UsbACTerminal) { 513 UsbACTerminal inDescr = (UsbACTerminal) descriptor; 514 if (inDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_IN_MIC 515 || inDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_BIDIR_HEADSET 516 || inDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_BIDIR_UNDEFINED 517 || inDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_EXTERN_LINE) { 518 hasMic = true; 519 break; 520 } 521 } else { 522 Log.w(TAG, "Undefined Audio Input terminal l: " + descriptor.getLength() 523 + " t:0x" + Integer.toHexString(descriptor.getType())); 524 } 525 } 526 return hasMic; 527 } 528 529 /** 530 * @hide 531 */ hasSpeaker()532 public boolean hasSpeaker() { 533 boolean hasSpeaker = false; 534 535 ArrayList<UsbDescriptor> acDescriptors = 536 getACInterfaceDescriptors(UsbACInterface.ACI_OUTPUT_TERMINAL, 537 UsbACInterface.AUDIO_AUDIOCONTROL); 538 for (UsbDescriptor descriptor : acDescriptors) { 539 if (descriptor instanceof UsbACTerminal) { 540 UsbACTerminal outDescr = (UsbACTerminal) descriptor; 541 if (outDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_OUT_SPEAKER 542 || outDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_OUT_HEADPHONES 543 || outDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_BIDIR_HEADSET) { 544 hasSpeaker = true; 545 break; 546 } 547 } else { 548 Log.w(TAG, "Undefined Audio Output terminal l: " + descriptor.getLength() 549 + " t:0x" + Integer.toHexString(descriptor.getType())); 550 } 551 } 552 553 return hasSpeaker; 554 } 555 556 /** 557 *@ hide 558 */ hasAudioInterface()559 public boolean hasAudioInterface() { 560 ArrayList<UsbDescriptor> descriptors = 561 getInterfaceDescriptorsForClass(UsbDescriptor.CLASSID_AUDIO); 562 return !descriptors.isEmpty(); 563 } 564 565 /** 566 * @hide 567 */ hasAudioTerminal(int subType)568 public boolean hasAudioTerminal(int subType) { 569 for (UsbDescriptor descriptor : mDescriptors) { 570 if (descriptor instanceof UsbACInterface) { 571 if (((UsbACInterface) descriptor).getSubclass() 572 == UsbDescriptor.AUDIO_AUDIOCONTROL 573 && ((UsbACInterface) descriptor).getSubtype() 574 == subType) { 575 return true; 576 } 577 } 578 } 579 return false; 580 } 581 582 /** 583 * @hide 584 */ hasAudioPlayback()585 public boolean hasAudioPlayback() { 586 return hasAudioTerminal(UsbACInterface.ACI_OUTPUT_TERMINAL); 587 } 588 589 /** 590 * @hide 591 */ hasAudioCapture()592 public boolean hasAudioCapture() { 593 return hasAudioTerminal(UsbACInterface.ACI_INPUT_TERMINAL); 594 } 595 596 /** 597 * @hide 598 */ hasVideoCapture()599 public boolean hasVideoCapture() { 600 for (UsbDescriptor descriptor : mDescriptors) { 601 if (descriptor instanceof UsbVCInputTerminal) { 602 return true; 603 } 604 } 605 return false; 606 } 607 608 /** 609 * @hide 610 */ hasVideoPlayback()611 public boolean hasVideoPlayback() { 612 for (UsbDescriptor descriptor : mDescriptors) { 613 if (descriptor instanceof UsbVCOutputTerminal) { 614 return true; 615 } 616 } 617 return false; 618 } 619 620 /** 621 * @hide 622 */ hasHIDInterface()623 public boolean hasHIDInterface() { 624 ArrayList<UsbDescriptor> descriptors = 625 getInterfaceDescriptorsForClass(UsbDescriptor.CLASSID_HID); 626 return !descriptors.isEmpty(); 627 } 628 629 /** 630 * @hide 631 */ hasStorageInterface()632 public boolean hasStorageInterface() { 633 ArrayList<UsbDescriptor> descriptors = 634 getInterfaceDescriptorsForClass(UsbDescriptor.CLASSID_STORAGE); 635 return !descriptors.isEmpty(); 636 } 637 638 /** 639 * @hide 640 */ hasMIDIInterface()641 public boolean hasMIDIInterface() { 642 ArrayList<UsbDescriptor> descriptors = 643 getInterfaceDescriptorsForClass(UsbDescriptor.CLASSID_AUDIO); 644 for (UsbDescriptor descriptor : descriptors) { 645 // enusure that this isn't an unrecognized interface descriptor 646 if (descriptor instanceof UsbInterfaceDescriptor) { 647 UsbInterfaceDescriptor interfaceDescr = (UsbInterfaceDescriptor) descriptor; 648 if (interfaceDescr.getUsbSubclass() == UsbDescriptor.AUDIO_MIDISTREAMING) { 649 return true; 650 } 651 } else { 652 Log.w(TAG, "Undefined Audio Class Interface l: " + descriptor.getLength() 653 + " t:0x" + Integer.toHexString(descriptor.getType())); 654 } 655 } 656 return false; 657 } 658 659 /** 660 * @hide 661 */ getInputHeadsetProbability()662 public float getInputHeadsetProbability() { 663 if (hasMIDIInterface()) { 664 return 0.0f; 665 } 666 667 float probability = 0.0f; 668 669 // Look for a microphone 670 boolean hasMic = hasMic(); 671 672 // Look for a "speaker" 673 boolean hasSpeaker = hasSpeaker(); 674 675 if (hasMic && hasSpeaker) { 676 probability += 0.75f; 677 } 678 679 if (hasMic && hasHIDInterface()) { 680 probability += 0.25f; 681 } 682 683 return probability; 684 } 685 686 /** 687 * getInputHeadsetProbability() reports a probability of a USB Input peripheral being a 688 * headset. The probability range is between 0.0f (definitely NOT a headset) and 689 * 1.0f (definitely IS a headset). A probability of 0.75f seems sufficient 690 * to count on the peripheral being a headset. 691 */ isInputHeadset()692 public boolean isInputHeadset() { 693 return getInputHeadsetProbability() >= IN_HEADSET_TRIGGER; 694 } 695 696 /** 697 * @hide 698 */ getOutputHeadsetProbability()699 public float getOutputHeadsetProbability() { 700 if (hasMIDIInterface()) { 701 return 0.0f; 702 } 703 704 float probability = 0.0f; 705 ArrayList<UsbDescriptor> acDescriptors; 706 707 // Look for a "speaker" 708 boolean hasSpeaker = false; 709 acDescriptors = 710 getACInterfaceDescriptors(UsbACInterface.ACI_OUTPUT_TERMINAL, 711 UsbACInterface.AUDIO_AUDIOCONTROL); 712 for (UsbDescriptor descriptor : acDescriptors) { 713 if (descriptor instanceof UsbACTerminal) { 714 UsbACTerminal outDescr = (UsbACTerminal) descriptor; 715 if (outDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_OUT_SPEAKER 716 || outDescr.getTerminalType() 717 == UsbTerminalTypes.TERMINAL_OUT_HEADPHONES 718 || outDescr.getTerminalType() == UsbTerminalTypes.TERMINAL_BIDIR_HEADSET) { 719 hasSpeaker = true; 720 break; 721 } 722 } else { 723 Log.w(TAG, "Undefined Audio Output terminal l: " + descriptor.getLength() 724 + " t:0x" + Integer.toHexString(descriptor.getType())); 725 } 726 } 727 728 if (hasSpeaker) { 729 probability += 0.75f; 730 } 731 732 if (hasSpeaker && hasHIDInterface()) { 733 probability += 0.25f; 734 } 735 736 return probability; 737 } 738 739 /** 740 * getOutputHeadsetProbability() reports a probability of a USB Output peripheral being a 741 * headset. The probability range is between 0.0f (definitely NOT a headset) and 742 * 1.0f (definitely IS a headset). A probability of 0.75f seems sufficient 743 * to count on the peripheral being a headset. 744 */ isOutputHeadset()745 public boolean isOutputHeadset() { 746 return getOutputHeadsetProbability() >= OUT_HEADSET_TRIGGER; 747 } 748 749 } 750