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