1 /* 2 * Copyright (C) 2021 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 17 package com.android.cts.verifier.audio.audiolib; 18 19 import android.content.Context; 20 import android.hardware.usb.UsbDevice; 21 import android.hardware.usb.UsbManager; 22 import android.media.AudioDeviceInfo; 23 import android.media.AudioManager; 24 import android.media.audio.Flags; 25 import android.util.Log; 26 27 import java.util.Collection; 28 import java.util.HashMap; 29 import java.util.Set; 30 31 /** 32 * Utility methods for AudioDevices 33 */ 34 public class AudioDeviceUtils { 35 private static final String TAG = "AudioDeviceUtils"; 36 private static final boolean LOG = false; 37 38 /* 39 * Channel Mask Utilities 40 */ 41 private static final HashMap<Integer, String> sDeviceTypeStrings = 42 new HashMap<Integer, String>(); 43 initDeviceTypeStrings()44 private static void initDeviceTypeStrings() { 45 sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_UNKNOWN, "UNKNOWN"); 46 sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_BUILTIN_EARPIECE, "BUILTIN_EARPIECE"); 47 sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, "BUILTIN_SPEAKER"); 48 sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_WIRED_HEADSET, "WIRED_HEADSET"); 49 sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_WIRED_HEADPHONES, "WIRED_HEADPHONES"); 50 sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_LINE_ANALOG, "LINE_ANALOG"); 51 sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_LINE_DIGITAL, "LINE_DIGITAL"); 52 sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_BLUETOOTH_SCO, "BLUETOOTH_SCO"); 53 sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_BLUETOOTH_A2DP, "BLUETOOTH_A2DP"); 54 sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_HDMI, "HDMI"); 55 sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_HDMI_ARC, "HDMI_ARC"); 56 sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_USB_DEVICE, "USB_DEVICE"); 57 sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_USB_ACCESSORY, "USB_ACCESSORY"); 58 sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_DOCK, "DOCK"); 59 sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_FM, "FM"); 60 sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_BUILTIN_MIC, "BUILTIN_MIC"); 61 sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_FM_TUNER, "FM_TUNER"); 62 sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_TV_TUNER, "TV_TUNER"); 63 sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_TELEPHONY, "TELEPHONY"); 64 sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_AUX_LINE, "AUX_LINE"); 65 sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_IP, "IP"); 66 sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_BUS, "BUS"); 67 sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_USB_HEADSET, "USB_HEADSET"); 68 sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_HEARING_AID, "HEARING_AID"); 69 sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER_SAFE, 70 "BUILTIN_SPEAKER_SAFE"); 71 sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_REMOTE_SUBMIX, "REMOTE_SUBMIX"); 72 sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_BLE_HEADSET, "BLE_HEADSET"); 73 sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_BLE_SPEAKER, "BLE_SPEAKER"); 74 sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_ECHO_REFERENCE, "ECHO_REFERENCE"); 75 sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_HDMI_EARC, "HDMI_EARC"); 76 sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_BLE_BROADCAST, "BLE_BROADCAST"); 77 } 78 79 static { initDeviceTypeStrings()80 initDeviceTypeStrings(); 81 } 82 83 // return codes for various supports device methods() 84 // Does not 85 public static final int SUPPORTSDEVICE_NO = 0; 86 // Does 87 public static final int SUPPORTSDEVICE_YES = 1; 88 // AudioManager.getSupportedDeviceTypes() is not implemented 89 public static final int SUPPORTSDEVICE_UNDETERMINED = 2; 90 91 /** 92 * @param deviceType The AudioDeviceInfo type ID of the desired device. 93 * @return a human-readable full device type name. 94 */ getDeviceTypeName(int deviceType)95 public static String getDeviceTypeName(int deviceType) { 96 String typeName = sDeviceTypeStrings.get(deviceType); 97 return typeName != null ? "TYPE_" + typeName : "invalid type"; 98 } 99 100 /** 101 * @param deviceType The AudioDeviceInfo type ID of the desired device. 102 * @return a human-readable abreviated device type name. 103 */ getShortDeviceTypeName(int deviceType)104 public static String getShortDeviceTypeName(int deviceType) { 105 String typeName = sDeviceTypeStrings.get(deviceType); 106 return typeName != null ? typeName : "invalid type"; 107 } 108 109 /** 110 * @param deviceInfo 111 * @return A human-readable description of the specified DeviceInfo 112 */ formatDeviceName(AudioDeviceInfo deviceInfo)113 public static String formatDeviceName(AudioDeviceInfo deviceInfo) { 114 StringBuilder sb = new StringBuilder(); 115 if (deviceInfo != null) { 116 sb.append(deviceInfo.getProductName()); 117 sb.append(" - " + getDeviceTypeName(deviceInfo.getType())); 118 } else { 119 sb.append("null"); 120 } 121 122 return sb.toString(); 123 } 124 125 /** 126 * @param deviceInfo Specifies the audio device to characterize. 127 * @return true if the device is (probably) a Mic 128 */ isMicDevice(AudioDeviceInfo deviceInfo)129 public static boolean isMicDevice(AudioDeviceInfo deviceInfo) { 130 if (deviceInfo == null || !deviceInfo.isSource()) { 131 return false; 132 } 133 134 switch (deviceInfo.getType()) { 135 case AudioDeviceInfo.TYPE_BUILTIN_MIC: 136 case AudioDeviceInfo.TYPE_WIRED_HEADSET: 137 case AudioDeviceInfo.TYPE_USB_HEADSET: 138 return true; 139 140 default: 141 return false; 142 } 143 } 144 145 /** 146 * Determine device support for an analog headset. 147 * 148 * @param context The application context. 149 * @return the SUPPORTSDEVICE_ constant indicating support. 150 */ supportsAnalogHeadset(Context context)151 public static int supportsAnalogHeadset(Context context) { 152 if (LOG) { 153 Log.d(TAG, "supportsAnalogHeadset()"); 154 } 155 if (!Flags.supportedDeviceTypesApi()) { 156 return SUPPORTSDEVICE_UNDETERMINED; 157 } 158 159 // TYPE_LINE_ANALOG? 160 AudioManager audioManager = context.getSystemService(AudioManager.class); 161 162 Set<Integer> deviceTypeIds = 163 audioManager.getSupportedDeviceTypes(AudioManager.GET_DEVICES_OUTPUTS); 164 if (LOG) { 165 for (Integer type : deviceTypeIds) { 166 Log.d(TAG, " " + getDeviceTypeName(type)); 167 } 168 } 169 return deviceTypeIds.contains(AudioDeviceInfo.TYPE_WIRED_HEADSET) 170 ? SUPPORTSDEVICE_YES : SUPPORTSDEVICE_NO; 171 } 172 173 /** 174 * Determine device support for a USB audio interface. 175 * 176 * @param context The application context. 177 * @return the SUPPORTSDEVICE_ constant indicating support. 178 */ supportsUsbAudioInterface(Context context)179 public static int supportsUsbAudioInterface(Context context) { 180 if (LOG) { 181 Log.d(TAG, "supportsUsbAudioInterface()"); 182 } 183 if (!Flags.supportedDeviceTypesApi()) { 184 return SUPPORTSDEVICE_UNDETERMINED; 185 } 186 187 AudioManager audioManager = context.getSystemService(AudioManager.class); 188 Set<Integer> deviceTypeIds = 189 audioManager.getSupportedDeviceTypes(AudioManager.GET_DEVICES_OUTPUTS); 190 if (LOG) { 191 for (Integer type : deviceTypeIds) { 192 Log.d(TAG, " " + getDeviceTypeName(type)); 193 } 194 } 195 return deviceTypeIds.contains(AudioDeviceInfo.TYPE_USB_DEVICE) 196 ? SUPPORTSDEVICE_YES : SUPPORTSDEVICE_NO; 197 } 198 199 /** 200 * Determine device support for a USB headset peripheral. 201 * 202 * @param context The application context. 203 * @return the SUPPORTSDEVICE_ constant indicating support. 204 */ supportsUsbHeadset(Context context)205 public static int supportsUsbHeadset(Context context) { 206 if (LOG) { 207 Log.d(TAG, "supportsUsbHeadset()"); 208 } 209 if (!Flags.supportedDeviceTypesApi()) { 210 return SUPPORTSDEVICE_UNDETERMINED; 211 } 212 213 AudioManager audioManager = context.getSystemService(AudioManager.class); 214 Set<Integer> outputDeviceTypeIds = 215 audioManager.getSupportedDeviceTypes(AudioManager.GET_DEVICES_OUTPUTS); 216 if (LOG) { 217 Log.d(TAG, "Output Device Types:"); 218 for (Integer type : outputDeviceTypeIds) { 219 Log.d(TAG, " " + getDeviceTypeName(type)); 220 } 221 } 222 223 Set<Integer> inputDeviceTypeIds = 224 audioManager.getSupportedDeviceTypes(AudioManager.GET_DEVICES_INPUTS); 225 if (LOG) { 226 Log.d(TAG, "Input Device Types:"); 227 for (Integer type : inputDeviceTypeIds) { 228 Log.d(TAG, " " + getDeviceTypeName(type)); 229 } 230 } 231 232 if (outputDeviceTypeIds.contains(AudioDeviceInfo.TYPE_USB_HEADSET) 233 && inputDeviceTypeIds.contains(AudioDeviceInfo.TYPE_USB_HEADSET)) { 234 return SUPPORTSDEVICE_YES; 235 } else { 236 return SUPPORTSDEVICE_NO; 237 } 238 } 239 240 /** 241 * Determine device support for a USB interface or headset peripheral. 242 * 243 * @param context The application context. 244 * @return the SUPPORTSDEVICE_ constant indicating support. 245 */ supportsUsbAudio(Context context)246 public static int supportsUsbAudio(Context context) { 247 if (LOG) { 248 Log.d(TAG, "supportsUsbAudio()"); 249 } 250 int hasInterface = supportsUsbAudioInterface(context); 251 int hasHeadset = supportsUsbHeadset(context); 252 if (LOG) { 253 Log.d(TAG, " hasInterface:" + hasInterface + " hasHeadset:" + hasHeadset); 254 } 255 256 // At least one is YES, so YES. 257 if (hasInterface == SUPPORTSDEVICE_YES || hasHeadset == SUPPORTSDEVICE_YES) { 258 return SUPPORTSDEVICE_YES; 259 } 260 261 // Both are NO, so NO 262 if (hasInterface == SUPPORTSDEVICE_NO && hasHeadset == SUPPORTSDEVICE_NO) { 263 return SUPPORTSDEVICE_NO; 264 } 265 266 // Some mixture of NO and UNDETERMINED, so UNDETERMINED 267 return SUPPORTSDEVICE_UNDETERMINED; 268 } 269 270 // 271 // USB Device Support 272 // 273 private static final int USBVENDORID_GOOGLE = 6353; 274 private static final int USBPRODUCTID_GOOGLEADAPTER = 20532; 275 276 /** 277 * Returns the UsbDevice corresponding to any connected USB peripheral. 278 * @param context The Application Context. 279 * @return the UsbDevice corresponding to any connected USB peripheral. 280 */ getConnectedUsbDevice(Context context)281 public static UsbDevice getConnectedUsbDevice(Context context) { 282 UsbManager usbManager = context.getSystemService(UsbManager.class); 283 284 if (usbManager == null) { 285 Log.e(TAG, "Can't get UsbManager!"); 286 } else { 287 HashMap<String, UsbDevice> deviceList = usbManager.getDeviceList(); 288 Collection<UsbDevice> devices = deviceList.values(); 289 UsbDevice[] deviceArray = new UsbDevice[1]; 290 deviceArray = (UsbDevice[]) devices.toArray(deviceArray); 291 return deviceArray[0]; 292 } 293 294 return null; 295 } 296 297 /** 298 * Determines if the specified UsbDevice is a validated USB Audio headset adapter. 299 * At this time, only the Google, USB-C adapter has been determined to be fully compatible. 300 * @param usbDevice the device to test. 301 * @param displayWarning if true, display a warning dialog 302 * @param context The application context 303 * @return true if the specified UsbDevice is a valid USB Audio headset adapter. 304 */ isUsbHeadsetValidForTest(UsbDevice usbDevice, boolean displayWarning, Context context)305 public static boolean isUsbHeadsetValidForTest(UsbDevice usbDevice, 306 boolean displayWarning, Context context) { 307 boolean isValid = usbDevice != null 308 && usbDevice.getVendorId() == USBVENDORID_GOOGLE 309 && usbDevice.getProductId() == USBPRODUCTID_GOOGLEADAPTER; 310 311 if (!isValid && displayWarning) { 312 UsbDeviceWarningDialog warningDialog = new UsbDeviceWarningDialog(context); 313 warningDialog.show(); 314 } 315 316 return isValid; 317 } 318 319 /** 320 * Checks for any connected USB peripheral that is a valid USB Audio headset adapter. 321 * Displays a warning dialog if validity can not be determined. 322 * @param context The application context. 323 */ validateUsbDevice(Context context)324 public static void validateUsbDevice(Context context) { 325 AudioManager audioManager = context.getSystemService(AudioManager.class); 326 327 // Determine if the connected device is a USB Headset 328 AudioDeviceInfo inputUsbHeadset = null; 329 for (AudioDeviceInfo devInfo : audioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)) { 330 if (devInfo.getType() == AudioDeviceInfo.TYPE_USB_HEADSET) { 331 inputUsbHeadset = devInfo; 332 break; 333 } 334 } 335 336 AudioDeviceInfo outputUsbHeadset = null; 337 for (AudioDeviceInfo devInfo : audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)) { 338 if (devInfo.getType() == AudioDeviceInfo.TYPE_USB_HEADSET) { 339 outputUsbHeadset = devInfo; 340 break; 341 } 342 } 343 344 if (inputUsbHeadset != null && outputUsbHeadset != null) { 345 // Now see if it is the (fully-functional) Google adapter 346 UsbDevice usbDevice = AudioDeviceUtils.getConnectedUsbDevice(context); 347 if (usbDevice != null) { 348 AudioDeviceUtils.isUsbHeadsetValidForTest(usbDevice, true, context); 349 } 350 } 351 } 352 } 353