1 /* 2 * Copyright (C) 2023 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.server.telecom; 18 19 import static com.android.server.telecom.CallAudioRouteAdapter.BT_AUDIO_CONNECTED; 20 import static com.android.server.telecom.CallAudioRouteAdapter.BT_AUDIO_DISCONNECTED; 21 import static com.android.server.telecom.CallAudioRouteAdapter.PENDING_ROUTE_FAILED; 22 import static com.android.server.telecom.CallAudioRouteAdapter.SPEAKER_OFF; 23 import static com.android.server.telecom.CallAudioRouteAdapter.SPEAKER_ON; 24 25 import android.annotation.IntDef; 26 import android.bluetooth.BluetoothDevice; 27 import android.bluetooth.BluetoothStatusCodes; 28 import android.media.AudioDeviceInfo; 29 import android.media.AudioManager; 30 import android.telecom.Log; 31 import android.util.Pair; 32 33 import com.android.internal.annotations.VisibleForTesting; 34 import com.android.server.telecom.bluetooth.BluetoothRouteManager; 35 36 import java.lang.annotation.Retention; 37 import java.lang.annotation.RetentionPolicy; 38 import java.util.ArrayList; 39 import java.util.HashMap; 40 import java.util.List; 41 import java.util.Objects; 42 import java.util.Set; 43 import java.util.concurrent.CompletableFuture; 44 import java.util.concurrent.ExecutionException; 45 import java.util.concurrent.RejectedExecutionException; 46 import java.util.concurrent.ScheduledExecutorService; 47 import java.util.concurrent.ScheduledThreadPoolExecutor; 48 import java.util.concurrent.TimeUnit; 49 50 public class AudioRoute { 51 public static class Factory { 52 private final ScheduledExecutorService mScheduledExecutorService = 53 new ScheduledThreadPoolExecutor(1); 54 private CompletableFuture<AudioRoute> mAudioRouteFuture; create(@udioRouteType int type, String bluetoothAddress, AudioManager audioManager)55 public AudioRoute create(@AudioRouteType int type, String bluetoothAddress, 56 AudioManager audioManager) throws RuntimeException { 57 mAudioRouteFuture = new CompletableFuture(); 58 createRetry(type, bluetoothAddress, audioManager, MAX_CONNECTION_RETRIES); 59 try { 60 return mAudioRouteFuture.get(); 61 } catch (InterruptedException | ExecutionException e) { 62 throw new RuntimeException("Error when creating requested audio route"); 63 } 64 } createRetry(@udioRouteType int type, String bluetoothAddress, AudioManager audioManager, int retryCount)65 private void createRetry(@AudioRouteType int type, String bluetoothAddress, 66 AudioManager audioManager, int retryCount) { 67 // Early exit if exceeded max number of retries (and complete the future). 68 if (retryCount == 0) { 69 mAudioRouteFuture.complete(null); 70 return; 71 } 72 73 Log.i(this, "creating AudioRoute with type %s and address %s, retry count %d", 74 DEVICE_TYPE_STRINGS.get(type), bluetoothAddress, retryCount); 75 AudioDeviceInfo routeInfo = null; 76 List<AudioDeviceInfo> infos = audioManager.getAvailableCommunicationDevices(); 77 List<Integer> possibleInfoTypes = AUDIO_ROUTE_TYPE_TO_DEVICE_INFO_TYPE.get(type); 78 for (AudioDeviceInfo info : infos) { 79 Log.i(this, "type: " + info.getType()); 80 if (possibleInfoTypes != null && possibleInfoTypes.contains(info.getType())) { 81 if (BT_AUDIO_ROUTE_TYPES.contains(type)) { 82 if (bluetoothAddress.equals(info.getAddress())) { 83 routeInfo = info; 84 break; 85 } 86 } else { 87 routeInfo = info; 88 break; 89 } 90 } 91 } 92 // Try connecting BT device anyway (to handle wearables not showing as available 93 // communication device or LE device not showing up since it may not be the lead 94 // device). 95 if (routeInfo == null && bluetoothAddress == null) { 96 try { 97 mScheduledExecutorService.schedule( 98 () -> createRetry(type, bluetoothAddress, audioManager, retryCount - 1), 99 RETRY_TIME_DELAY, TimeUnit.MILLISECONDS); 100 } catch (RejectedExecutionException e) { 101 Log.e(this, e, "Could not schedule retry for audio routing."); 102 } 103 } else { 104 mAudioRouteFuture.complete(new AudioRoute(type, bluetoothAddress, routeInfo)); 105 } 106 } 107 } 108 109 private static final long RETRY_TIME_DELAY = 500L; 110 private static final int MAX_CONNECTION_RETRIES = 2; 111 public static final int TYPE_INVALID = 0; 112 public static final int TYPE_EARPIECE = 1; 113 public static final int TYPE_WIRED = 2; 114 public static final int TYPE_SPEAKER = 3; 115 public static final int TYPE_DOCK = 4; 116 public static final int TYPE_BLUETOOTH_SCO = 5; 117 public static final int TYPE_BLUETOOTH_HA = 6; 118 public static final int TYPE_BLUETOOTH_LE = 7; 119 public static final int TYPE_STREAMING = 8; 120 @IntDef(prefix = "TYPE", value = { 121 TYPE_INVALID, 122 TYPE_EARPIECE, 123 TYPE_WIRED, 124 TYPE_SPEAKER, 125 TYPE_DOCK, 126 TYPE_BLUETOOTH_SCO, 127 TYPE_BLUETOOTH_HA, 128 TYPE_BLUETOOTH_LE, 129 TYPE_STREAMING 130 }) 131 @Retention(RetentionPolicy.SOURCE) 132 public @interface AudioRouteType {} 133 134 private @AudioRouteType int mAudioRouteType; 135 private String mBluetoothAddress; 136 private AudioDeviceInfo mInfo; 137 public static final Set<Integer> BT_AUDIO_DEVICE_INFO_TYPES = Set.of( 138 AudioDeviceInfo.TYPE_BLE_HEADSET, 139 AudioDeviceInfo.TYPE_BLE_SPEAKER, 140 AudioDeviceInfo.TYPE_BLE_BROADCAST, 141 AudioDeviceInfo.TYPE_HEARING_AID, 142 AudioDeviceInfo.TYPE_BLUETOOTH_SCO 143 ); 144 145 public static final Set<Integer> BT_AUDIO_ROUTE_TYPES = Set.of( 146 AudioRoute.TYPE_BLUETOOTH_SCO, 147 AudioRoute.TYPE_BLUETOOTH_HA, 148 AudioRoute.TYPE_BLUETOOTH_LE 149 ); 150 151 public static final HashMap<Integer, String> DEVICE_TYPE_STRINGS; 152 static { 153 DEVICE_TYPE_STRINGS = new HashMap<>(); DEVICE_TYPE_STRINGS.put(TYPE_EARPIECE, "TYPE_EARPIECE")154 DEVICE_TYPE_STRINGS.put(TYPE_EARPIECE, "TYPE_EARPIECE"); DEVICE_TYPE_STRINGS.put(TYPE_WIRED, "TYPE_WIRED_HEADSET")155 DEVICE_TYPE_STRINGS.put(TYPE_WIRED, "TYPE_WIRED_HEADSET"); DEVICE_TYPE_STRINGS.put(TYPE_SPEAKER, "TYPE_SPEAKER")156 DEVICE_TYPE_STRINGS.put(TYPE_SPEAKER, "TYPE_SPEAKER"); DEVICE_TYPE_STRINGS.put(TYPE_DOCK, "TYPE_DOCK")157 DEVICE_TYPE_STRINGS.put(TYPE_DOCK, "TYPE_DOCK"); DEVICE_TYPE_STRINGS.put(TYPE_BLUETOOTH_SCO, "TYPE_BLUETOOTH_SCO")158 DEVICE_TYPE_STRINGS.put(TYPE_BLUETOOTH_SCO, "TYPE_BLUETOOTH_SCO"); DEVICE_TYPE_STRINGS.put(TYPE_BLUETOOTH_HA, "TYPE_BLUETOOTH_HA")159 DEVICE_TYPE_STRINGS.put(TYPE_BLUETOOTH_HA, "TYPE_BLUETOOTH_HA"); DEVICE_TYPE_STRINGS.put(TYPE_BLUETOOTH_LE, "TYPE_BLUETOOTH_LE")160 DEVICE_TYPE_STRINGS.put(TYPE_BLUETOOTH_LE, "TYPE_BLUETOOTH_LE"); DEVICE_TYPE_STRINGS.put(TYPE_STREAMING, "TYPE_STREAMING")161 DEVICE_TYPE_STRINGS.put(TYPE_STREAMING, "TYPE_STREAMING"); 162 } 163 164 public static final HashMap<Integer, Integer> DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE; 165 static { 166 DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE = new HashMap<>(); DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_BUILTIN_EARPIECE, TYPE_EARPIECE)167 DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_BUILTIN_EARPIECE, 168 TYPE_EARPIECE); DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, TYPE_SPEAKER)169 DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, TYPE_SPEAKER); DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_WIRED_HEADSET, TYPE_WIRED)170 DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_WIRED_HEADSET, TYPE_WIRED); DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_WIRED_HEADPHONES, TYPE_WIRED)171 DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_WIRED_HEADPHONES, TYPE_WIRED); DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_BLUETOOTH_SCO, TYPE_BLUETOOTH_SCO)172 DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_BLUETOOTH_SCO, 173 TYPE_BLUETOOTH_SCO); DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_USB_DEVICE, TYPE_WIRED)174 DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_USB_DEVICE, TYPE_WIRED); DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_USB_ACCESSORY, TYPE_WIRED)175 DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_USB_ACCESSORY, TYPE_WIRED); DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_DOCK, TYPE_DOCK)176 DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_DOCK, TYPE_DOCK); DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_USB_HEADSET, TYPE_WIRED)177 DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_USB_HEADSET, TYPE_WIRED); DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_HEARING_AID, TYPE_BLUETOOTH_HA)178 DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_HEARING_AID, 179 TYPE_BLUETOOTH_HA); DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_BLE_HEADSET, TYPE_BLUETOOTH_LE)180 DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_BLE_HEADSET, 181 TYPE_BLUETOOTH_LE); DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_BLE_SPEAKER, TYPE_BLUETOOTH_LE)182 DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_BLE_SPEAKER, 183 TYPE_BLUETOOTH_LE); DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_BLE_BROADCAST, TYPE_BLUETOOTH_LE)184 DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_BLE_BROADCAST, 185 TYPE_BLUETOOTH_LE); DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_DOCK_ANALOG, TYPE_DOCK)186 DEVICE_INFO_TYPETO_AUDIO_ROUTE_TYPE.put(AudioDeviceInfo.TYPE_DOCK_ANALOG, TYPE_DOCK); 187 } 188 189 private static final HashMap<Integer, List<Integer>> AUDIO_ROUTE_TYPE_TO_DEVICE_INFO_TYPE; 190 static { 191 AUDIO_ROUTE_TYPE_TO_DEVICE_INFO_TYPE = new HashMap<>(); 192 List<Integer> earpieceDeviceInfoTypes = new ArrayList<>(); 193 earpieceDeviceInfoTypes.add(AudioDeviceInfo.TYPE_BUILTIN_EARPIECE); AUDIO_ROUTE_TYPE_TO_DEVICE_INFO_TYPE.put(TYPE_EARPIECE, earpieceDeviceInfoTypes)194 AUDIO_ROUTE_TYPE_TO_DEVICE_INFO_TYPE.put(TYPE_EARPIECE, earpieceDeviceInfoTypes); 195 196 List<Integer> wiredDeviceInfoTypes = new ArrayList<>(); 197 wiredDeviceInfoTypes.add(AudioDeviceInfo.TYPE_WIRED_HEADSET); 198 wiredDeviceInfoTypes.add(AudioDeviceInfo.TYPE_WIRED_HEADPHONES); 199 wiredDeviceInfoTypes.add(AudioDeviceInfo.TYPE_USB_DEVICE); 200 wiredDeviceInfoTypes.add(AudioDeviceInfo.TYPE_USB_ACCESSORY); 201 wiredDeviceInfoTypes.add(AudioDeviceInfo.TYPE_USB_HEADSET); AUDIO_ROUTE_TYPE_TO_DEVICE_INFO_TYPE.put(TYPE_WIRED, wiredDeviceInfoTypes)202 AUDIO_ROUTE_TYPE_TO_DEVICE_INFO_TYPE.put(TYPE_WIRED, wiredDeviceInfoTypes); 203 204 List<Integer> speakerDeviceInfoTypes = new ArrayList<>(); 205 speakerDeviceInfoTypes.add(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER); AUDIO_ROUTE_TYPE_TO_DEVICE_INFO_TYPE.put(TYPE_SPEAKER, speakerDeviceInfoTypes)206 AUDIO_ROUTE_TYPE_TO_DEVICE_INFO_TYPE.put(TYPE_SPEAKER, speakerDeviceInfoTypes); 207 208 List<Integer> dockDeviceInfoTypes = new ArrayList<>(); 209 dockDeviceInfoTypes.add(AudioDeviceInfo.TYPE_DOCK); 210 dockDeviceInfoTypes.add(AudioDeviceInfo.TYPE_DOCK_ANALOG); AUDIO_ROUTE_TYPE_TO_DEVICE_INFO_TYPE.put(TYPE_DOCK, dockDeviceInfoTypes)211 AUDIO_ROUTE_TYPE_TO_DEVICE_INFO_TYPE.put(TYPE_DOCK, dockDeviceInfoTypes); 212 213 List<Integer> bluetoothScoDeviceInfoTypes = new ArrayList<>(); 214 bluetoothScoDeviceInfoTypes.add(AudioDeviceInfo.TYPE_BLUETOOTH_A2DP); 215 bluetoothScoDeviceInfoTypes.add(AudioDeviceInfo.TYPE_BLUETOOTH_SCO); AUDIO_ROUTE_TYPE_TO_DEVICE_INFO_TYPE.put(TYPE_BLUETOOTH_SCO, bluetoothScoDeviceInfoTypes)216 AUDIO_ROUTE_TYPE_TO_DEVICE_INFO_TYPE.put(TYPE_BLUETOOTH_SCO, bluetoothScoDeviceInfoTypes); 217 218 List<Integer> bluetoothHearingAidDeviceInfoTypes = new ArrayList<>(); 219 bluetoothHearingAidDeviceInfoTypes.add(AudioDeviceInfo.TYPE_HEARING_AID); AUDIO_ROUTE_TYPE_TO_DEVICE_INFO_TYPE.put(TYPE_BLUETOOTH_HA, bluetoothHearingAidDeviceInfoTypes)220 AUDIO_ROUTE_TYPE_TO_DEVICE_INFO_TYPE.put(TYPE_BLUETOOTH_HA, 221 bluetoothHearingAidDeviceInfoTypes); 222 223 List<Integer> bluetoothLeDeviceInfoTypes = new ArrayList<>(); 224 bluetoothLeDeviceInfoTypes.add(AudioDeviceInfo.TYPE_BLE_HEADSET); 225 bluetoothLeDeviceInfoTypes.add(AudioDeviceInfo.TYPE_BLE_SPEAKER); 226 bluetoothLeDeviceInfoTypes.add(AudioDeviceInfo.TYPE_BLE_BROADCAST); AUDIO_ROUTE_TYPE_TO_DEVICE_INFO_TYPE.put(TYPE_BLUETOOTH_LE, bluetoothLeDeviceInfoTypes)227 AUDIO_ROUTE_TYPE_TO_DEVICE_INFO_TYPE.put(TYPE_BLUETOOTH_LE, bluetoothLeDeviceInfoTypes); 228 } 229 getType()230 public int getType() { 231 return mAudioRouteType; 232 } 233 getBluetoothAddress()234 String getBluetoothAddress() { 235 return mBluetoothAddress; 236 } 237 238 // Invoked when entered pending route whose dest route is this route onDestRouteAsPendingRoute(boolean active, PendingAudioRoute pendingAudioRoute, BluetoothDevice device, AudioManager audioManager, BluetoothRouteManager bluetoothRouteManager, boolean isScoAudioConnected)239 void onDestRouteAsPendingRoute(boolean active, PendingAudioRoute pendingAudioRoute, 240 BluetoothDevice device, AudioManager audioManager, 241 BluetoothRouteManager bluetoothRouteManager, boolean isScoAudioConnected) { 242 Log.i(this, "onDestRouteAsPendingRoute: active (%b), type (%d)", active, mAudioRouteType); 243 if (pendingAudioRoute.isActive() && !active) { 244 clearCommunicationDevice(pendingAudioRoute, bluetoothRouteManager, audioManager); 245 } else if (active) { 246 // Handle BT routing case. 247 if (BT_AUDIO_ROUTE_TYPES.contains(mAudioRouteType)) { 248 boolean connectedBtAudio = connectBtAudio(pendingAudioRoute, device, 249 audioManager, bluetoothRouteManager); 250 // Special handling for SCO case. 251 if (mAudioRouteType == TYPE_BLUETOOTH_SCO) { 252 // Check if the communication device was set for the device, even if 253 // BluetoothHeadset#connectAudio reports that the SCO connection wasn't 254 // successfully established. 255 if (connectedBtAudio || isScoAudioConnected) { 256 pendingAudioRoute.setCommunicationDeviceType(mAudioRouteType); 257 if (!isScoAudioConnected) { 258 pendingAudioRoute.addMessage(BT_AUDIO_CONNECTED, mBluetoothAddress); 259 } 260 } else { 261 pendingAudioRoute.onMessageReceived(new Pair<>(PENDING_ROUTE_FAILED, 262 mBluetoothAddress), mBluetoothAddress); 263 } 264 return; 265 } 266 } else if (mAudioRouteType == TYPE_SPEAKER) { 267 pendingAudioRoute.addMessage(SPEAKER_ON, null); 268 } 269 270 boolean result = false; 271 List<AudioDeviceInfo> devices = audioManager.getAvailableCommunicationDevices(); 272 for (AudioDeviceInfo deviceInfo : devices) { 273 // It's possible for the AudioDeviceInfo to be updated for the BT device so adjust 274 // mInfo accordingly. 275 if (BT_AUDIO_ROUTE_TYPES.contains(mAudioRouteType) && mBluetoothAddress 276 .equals(deviceInfo.getAddress())) { 277 mInfo = deviceInfo; 278 } 279 if (deviceInfo.equals(mInfo)) { 280 result = audioManager.setCommunicationDevice(mInfo); 281 if (result) { 282 pendingAudioRoute.setCommunicationDeviceType(mAudioRouteType); 283 } 284 Log.i(this, "Result of setting communication device for audio " 285 + "route (%s) - %b", this, result); 286 break; 287 } 288 } 289 290 // It's possible that BluetoothStateReceiver needs to report that the device is active 291 // before being able to successfully set the communication device. Refrain from sending 292 // pending route failed message for BT route until the second attempt fails. 293 if (!result && !BT_AUDIO_ROUTE_TYPES.contains(mAudioRouteType)) { 294 pendingAudioRoute.onMessageReceived(new Pair<>(PENDING_ROUTE_FAILED, null), null); 295 } 296 } 297 } 298 299 // Takes care of cleaning up original audio route (i.e. clearCommunicationDevice, 300 // sending SPEAKER_OFF, or disconnecting SCO). onOrigRouteAsPendingRoute(boolean active, PendingAudioRoute pendingAudioRoute, AudioManager audioManager, BluetoothRouteManager bluetoothRouteManager)301 void onOrigRouteAsPendingRoute(boolean active, PendingAudioRoute pendingAudioRoute, 302 AudioManager audioManager, BluetoothRouteManager bluetoothRouteManager) { 303 Log.i(this, "onOrigRouteAsPendingRoute: active (%b), type (%d)", active, mAudioRouteType); 304 if (active) { 305 if (mAudioRouteType == TYPE_SPEAKER) { 306 pendingAudioRoute.addMessage(SPEAKER_OFF, null); 307 } 308 int result = clearCommunicationDevice(pendingAudioRoute, bluetoothRouteManager, 309 audioManager); 310 // Only send BT_AUDIO_DISCONNECTED for SCO if disconnect was successful. 311 if (mAudioRouteType == TYPE_BLUETOOTH_SCO && result == BluetoothStatusCodes.SUCCESS) { 312 pendingAudioRoute.addMessage(BT_AUDIO_DISCONNECTED, mBluetoothAddress); 313 } 314 } 315 } 316 317 @VisibleForTesting AudioRoute(@udioRouteType int type, String bluetoothAddress, AudioDeviceInfo info)318 public AudioRoute(@AudioRouteType int type, String bluetoothAddress, AudioDeviceInfo info) { 319 mAudioRouteType = type; 320 mBluetoothAddress = bluetoothAddress; 321 mInfo = info; 322 } 323 324 @Override equals(Object obj)325 public boolean equals(Object obj) { 326 if (obj == null) { 327 return false; 328 } 329 if (!(obj instanceof AudioRoute otherRoute)) { 330 return false; 331 } 332 if (mAudioRouteType != otherRoute.getType()) { 333 return false; 334 } 335 return !BT_AUDIO_ROUTE_TYPES.contains(mAudioRouteType) || mBluetoothAddress.equals( 336 otherRoute.getBluetoothAddress()); 337 } 338 339 @Override hashCode()340 public int hashCode() { 341 return Objects.hash(mAudioRouteType, mBluetoothAddress); 342 } 343 344 @Override toString()345 public String toString() { 346 return getClass().getSimpleName() + "[Type=" + DEVICE_TYPE_STRINGS.get(mAudioRouteType) 347 + ", Address=" + ((mBluetoothAddress != null) ? mBluetoothAddress : "invalid") 348 + "]"; 349 } 350 connectBtAudio(PendingAudioRoute pendingAudioRoute, BluetoothDevice device, AudioManager audioManager, BluetoothRouteManager bluetoothRouteManager)351 private boolean connectBtAudio(PendingAudioRoute pendingAudioRoute, BluetoothDevice device, 352 AudioManager audioManager, BluetoothRouteManager bluetoothRouteManager) { 353 // Ensure that if another BT device was set, it is disconnected before connecting 354 // the new one. 355 AudioRoute currentRoute = pendingAudioRoute.getOrigRoute(); 356 if (currentRoute.getBluetoothAddress() != null && 357 !currentRoute.getBluetoothAddress().equals(device.getAddress())) { 358 clearCommunicationDevice(pendingAudioRoute, bluetoothRouteManager, audioManager); 359 } 360 361 // Connect to the device (explicit handling for HFP devices). 362 boolean success = false; 363 if (device != null) { 364 success = bluetoothRouteManager.getDeviceManager() 365 .connectAudio(device, mAudioRouteType); 366 } 367 368 Log.i(this, "connectBtAudio: routeToConnectTo = %s, successful = %b", 369 this, success); 370 return success; 371 } 372 clearCommunicationDevice(PendingAudioRoute pendingAudioRoute, BluetoothRouteManager bluetoothRouteManager, AudioManager audioManager)373 int clearCommunicationDevice(PendingAudioRoute pendingAudioRoute, 374 BluetoothRouteManager bluetoothRouteManager, AudioManager audioManager) { 375 // Try to see if there's a previously set device for communication that should be cleared. 376 // This only serves to help in the SCO case to ensure that we disconnect the headset. 377 if (pendingAudioRoute.getCommunicationDeviceType() == AudioRoute.TYPE_INVALID) { 378 return -1; 379 } 380 381 int result = BluetoothStatusCodes.SUCCESS; 382 if (pendingAudioRoute.getCommunicationDeviceType() == TYPE_BLUETOOTH_SCO) { 383 Log.i(this, "Disconnecting SCO device."); 384 result = bluetoothRouteManager.getDeviceManager().disconnectSco(); 385 } else { 386 Log.i(this, "Clearing communication device for audio type %d.", 387 pendingAudioRoute.getCommunicationDeviceType()); 388 audioManager.clearCommunicationDevice(); 389 } 390 391 if (result == BluetoothStatusCodes.SUCCESS) { 392 pendingAudioRoute.setCommunicationDeviceType(AudioRoute.TYPE_INVALID); 393 } 394 return result; 395 } 396 } 397