1 /* 2 * Copyright 2019 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.audio; 17 18 import static android.media.AudioManager.AUDIO_DEVICE_CATEGORY_UNKNOWN; 19 import static android.media.AudioSystem.DEVICE_IN_ALL_SCO_SET; 20 import static android.media.AudioSystem.DEVICE_OUT_ALL_A2DP_SET; 21 import static android.media.AudioSystem.DEVICE_OUT_ALL_BLE_SET; 22 import static android.media.AudioSystem.DEVICE_OUT_ALL_SCO_SET; 23 import static android.media.AudioSystem.DEVICE_OUT_BLE_HEADSET; 24 import static android.media.AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP; 25 import static android.media.AudioSystem.DEVICE_OUT_HEARING_AID; 26 import static android.media.AudioSystem.isBluetoothA2dpOutDevice; 27 import static android.media.AudioSystem.isBluetoothDevice; 28 import static android.media.AudioSystem.isBluetoothLeOutDevice; 29 import static android.media.AudioSystem.isBluetoothOutDevice; 30 import static android.media.AudioSystem.isBluetoothScoOutDevice; 31 import static android.media.audio.Flags.automaticBtDeviceType; 32 33 import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE; 34 35 import android.annotation.NonNull; 36 import android.annotation.Nullable; 37 import android.bluetooth.BluetoothAdapter; 38 import android.bluetooth.BluetoothDevice; 39 import android.bluetooth.BluetoothLeAudio; 40 import android.bluetooth.BluetoothProfile; 41 import android.content.Intent; 42 import android.media.AudioDeviceAttributes; 43 import android.media.AudioDeviceInfo; 44 import android.media.AudioDevicePort; 45 import android.media.AudioFormat; 46 import android.media.AudioManager; 47 import android.media.AudioManager.AudioDeviceCategory; 48 import android.media.AudioPort; 49 import android.media.AudioRoutesInfo; 50 import android.media.AudioSystem; 51 import android.media.IAudioRoutesObserver; 52 import android.media.ICapturePresetDevicesRoleDispatcher; 53 import android.media.IStrategyNonDefaultDevicesDispatcher; 54 import android.media.IStrategyPreferredDevicesDispatcher; 55 import android.media.MediaMetrics; 56 import android.media.MediaRecorder.AudioSource; 57 import android.media.Utils; 58 import android.media.audiopolicy.AudioProductStrategy; 59 import android.media.permission.ClearCallingIdentityContext; 60 import android.media.permission.SafeCloseable; 61 import android.os.Binder; 62 import android.os.Bundle; 63 import android.os.RemoteCallbackList; 64 import android.os.RemoteException; 65 import android.os.SystemProperties; 66 import android.text.TextUtils; 67 import android.util.ArrayMap; 68 import android.util.ArraySet; 69 import android.util.Log; 70 import android.util.Pair; 71 import android.util.Slog; 72 73 import com.android.internal.annotations.GuardedBy; 74 import com.android.internal.annotations.VisibleForTesting; 75 import com.android.server.utils.EventLogger; 76 77 import com.google.android.collect.Sets; 78 79 import java.io.PrintWriter; 80 import java.util.ArrayList; 81 import java.util.Arrays; 82 import java.util.Collection; 83 import java.util.HashSet; 84 import java.util.Iterator; 85 import java.util.LinkedHashMap; 86 import java.util.List; 87 import java.util.Map.Entry; 88 import java.util.Objects; 89 import java.util.Set; 90 import java.util.concurrent.atomic.AtomicBoolean; 91 import java.util.stream.Stream; 92 93 /** 94 * Class to manage the inventory of all connected devices. 95 * This class is thread-safe. 96 * (non final for mocking/spying) 97 */ 98 public class AudioDeviceInventory { 99 100 private static final String TAG = "AS.AudioDeviceInventory"; 101 102 private static final String SETTING_DEVICE_SEPARATOR_CHAR = "|"; 103 private static final String SETTING_DEVICE_SEPARATOR = "\\|"; 104 105 /** Max String length that can be persisted within the Settings. */ 106 // LINT.IfChange(settings_max_length_per_string) 107 private static final int MAX_SETTINGS_LENGTH_PER_STRING = 32768; 108 // LINT.ThenChange(/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java) 109 110 private static final int MAX_DEVICE_INVENTORY_ENTRIES = 111 MAX_SETTINGS_LENGTH_PER_STRING / AdiDeviceState.getPeristedMaxSize(); 112 113 // lock to synchronize all access to mConnectedDevices and mApmConnectedDevices 114 private final Object mDevicesLock = new Object(); 115 116 //Audio Analytics ids. 117 private static final String mMetricsId = "audio.device."; 118 119 private final Object mDeviceInventoryLock = new Object(); 120 121 @GuardedBy("mDeviceInventoryLock") 122 private final LinkedHashMap<Pair<Integer, String>, AdiDeviceState> mDeviceInventory = 123 new LinkedHashMap<>(); 124 getImmutableDeviceInventory()125 Collection<AdiDeviceState> getImmutableDeviceInventory() { 126 final List<AdiDeviceState> newList; 127 synchronized (mDeviceInventoryLock) { 128 newList = new ArrayList<>(mDeviceInventory.values()); 129 } 130 return newList; 131 } 132 133 /** 134 * Adds a new AdiDeviceState or updates the spatial audio related properties of the matching 135 * AdiDeviceState in the {@link AudioDeviceInventory#mDeviceInventory} list. 136 * @param deviceState the device to update 137 */ addOrUpdateDeviceSAStateInInventory(AdiDeviceState deviceState, boolean syncInventory)138 void addOrUpdateDeviceSAStateInInventory(AdiDeviceState deviceState, boolean syncInventory) { 139 synchronized (mDeviceInventoryLock) { 140 mDeviceInventory.merge(deviceState.getDeviceId(), deviceState, 141 (oldState, newState) -> { 142 oldState.setHasHeadTracker(newState.hasHeadTracker()); 143 oldState.setHeadTrackerEnabled(newState.isHeadTrackerEnabled()); 144 oldState.setSAEnabled(newState.isSAEnabled()); 145 return oldState; 146 }); 147 checkDeviceInventorySize_l(); 148 } 149 if (syncInventory) { 150 mDeviceBroker.postSynchronizeAdiDevicesInInventory(deviceState); 151 } 152 } 153 154 /** 155 * Adds a new entry in mDeviceInventory if the attributes passed represent a sink 156 * Bluetooth device and no corresponding entry already exists. 157 * 158 * <p>This method will reconcile all BT devices connected with different profiles 159 * that share the same MAC address and will also synchronize the devices to their 160 * corresponding peers in case of BLE 161 */ addAudioDeviceInInventoryIfNeeded(int deviceType, String address, String peerAddress, @AudioDeviceCategory int category, boolean userDefined)162 void addAudioDeviceInInventoryIfNeeded(int deviceType, String address, String peerAddress, 163 @AudioDeviceCategory int category, boolean userDefined) { 164 if (!isBluetoothOutDevice(deviceType)) { 165 return; 166 } 167 synchronized (mDeviceInventoryLock) { 168 AdiDeviceState ads = findBtDeviceStateForAddress(address, deviceType); 169 if (ads == null && peerAddress != null) { 170 ads = findBtDeviceStateForAddress(peerAddress, deviceType); 171 } 172 if (ads != null) { 173 // if category is user defined allow to change back to unknown otherwise 174 // do not reset the category back to unknown since it might have been set 175 // before by the user 176 if (ads.getAudioDeviceCategory() != category && (userDefined 177 || category != AUDIO_DEVICE_CATEGORY_UNKNOWN)) { 178 ads.setAudioDeviceCategory(category); 179 mDeviceBroker.postUpdatedAdiDeviceState(ads, false /*initSA*/); 180 mDeviceBroker.postPersistAudioDeviceSettings(); 181 } 182 mDeviceBroker.postSynchronizeAdiDevicesInInventory(ads); 183 return; 184 } 185 ads = new AdiDeviceState(AudioDeviceInfo.convertInternalDeviceToDeviceType(deviceType), 186 deviceType, address); 187 ads.setAudioDeviceCategory(category); 188 189 mDeviceInventory.put(ads.getDeviceId(), ads); 190 checkDeviceInventorySize_l(); 191 192 mDeviceBroker.postUpdatedAdiDeviceState(ads, true /*initSA*/); 193 mDeviceBroker.postPersistAudioDeviceSettings(); 194 } 195 } 196 197 /** 198 * Adds a new AdiDeviceState or updates the audio device category of the matching 199 * AdiDeviceState in the {@link AudioDeviceInventory#mDeviceInventory} list. 200 * @param deviceState the device to update 201 */ addOrUpdateAudioDeviceCategoryInInventory( AdiDeviceState deviceState, boolean syncInventory)202 void addOrUpdateAudioDeviceCategoryInInventory( 203 AdiDeviceState deviceState, boolean syncInventory) { 204 AtomicBoolean updatedCategory = new AtomicBoolean(false); 205 synchronized (mDeviceInventoryLock) { 206 if (automaticBtDeviceType()) { 207 if (deviceState.updateAudioDeviceCategory()) { 208 updatedCategory.set(true); 209 } 210 } 211 deviceState = mDeviceInventory.merge(deviceState.getDeviceId(), 212 deviceState, (oldState, newState) -> { 213 if (oldState.getAudioDeviceCategory() 214 != newState.getAudioDeviceCategory()) { 215 oldState.setAudioDeviceCategory(newState.getAudioDeviceCategory()); 216 updatedCategory.set(true); 217 } 218 return oldState; 219 }); 220 checkDeviceInventorySize_l(); 221 } 222 if (updatedCategory.get()) { 223 mDeviceBroker.postUpdatedAdiDeviceState(deviceState, false /*initSA*/); 224 } 225 if (syncInventory) { 226 mDeviceBroker.postSynchronizeAdiDevicesInInventory(deviceState); 227 } 228 } 229 addAudioDeviceWithCategoryInInventoryIfNeeded(@onNull String address, @AudioDeviceCategory int btAudioDeviceCategory)230 void addAudioDeviceWithCategoryInInventoryIfNeeded(@NonNull String address, 231 @AudioDeviceCategory int btAudioDeviceCategory) { 232 addAudioDeviceInInventoryIfNeeded(DEVICE_OUT_BLE_HEADSET, 233 address, "", btAudioDeviceCategory, /*userDefined=*/true); 234 addAudioDeviceInInventoryIfNeeded(DEVICE_OUT_BLUETOOTH_A2DP, 235 address, "", btAudioDeviceCategory, /*userDefined=*/true); 236 237 } 238 @AudioDeviceCategory getAndUpdateBtAdiDeviceStateCategoryForAddress(@onNull String address)239 int getAndUpdateBtAdiDeviceStateCategoryForAddress(@NonNull String address) { 240 int btCategory = AUDIO_DEVICE_CATEGORY_UNKNOWN; 241 boolean bleCategoryFound = false; 242 AdiDeviceState deviceState = findBtDeviceStateForAddress(address, DEVICE_OUT_BLE_HEADSET); 243 if (deviceState != null) { 244 addOrUpdateAudioDeviceCategoryInInventory(deviceState, true /*syncInventory*/); 245 btCategory = deviceState.getAudioDeviceCategory(); 246 bleCategoryFound = true; 247 } 248 249 deviceState = findBtDeviceStateForAddress(address, DEVICE_OUT_BLUETOOTH_A2DP); 250 if (deviceState != null) { 251 addOrUpdateAudioDeviceCategoryInInventory(deviceState, true /*syncInventory*/); 252 int a2dpCategory = deviceState.getAudioDeviceCategory(); 253 if (bleCategoryFound && a2dpCategory != btCategory) { 254 Log.w(TAG, "Found different audio device category for A2DP and BLE profiles with " 255 + "address " + address); 256 } 257 btCategory = a2dpCategory; 258 } 259 260 return btCategory; 261 } 262 isBluetoothAudioDeviceCategoryFixed(@onNull String address)263 boolean isBluetoothAudioDeviceCategoryFixed(@NonNull String address) { 264 AdiDeviceState deviceState = findBtDeviceStateForAddress(address, DEVICE_OUT_BLE_HEADSET); 265 if (deviceState != null) { 266 return deviceState.isBtDeviceCategoryFixed(); 267 } 268 269 deviceState = findBtDeviceStateForAddress(address, DEVICE_OUT_BLUETOOTH_A2DP); 270 if (deviceState != null) { 271 return deviceState.isBtDeviceCategoryFixed(); 272 } 273 274 return false; 275 } 276 277 /** 278 * Synchronize AdiDeviceState for LE devices in the same group 279 * or BT classic devices with the same address. 280 * @param updatedDevice the device state to synchronize or null. 281 * Called with null once after the device inventory and spatializer helper 282 * have been initialized to resync all devices. 283 */ onSynchronizeAdiDevicesInInventory(AdiDeviceState updatedDevice)284 void onSynchronizeAdiDevicesInInventory(AdiDeviceState updatedDevice) { 285 synchronized (mDevicesLock) { 286 synchronized (mDeviceInventoryLock) { 287 if (updatedDevice != null) { 288 onSynchronizeAdiDeviceInInventory_l(updatedDevice); 289 } else { 290 for (AdiDeviceState ads : mDeviceInventory.values()) { 291 onSynchronizeAdiDeviceInInventory_l(ads); 292 } 293 } 294 } 295 } 296 } 297 298 /** 299 * Synchronize AdiDeviceState for LE devices in the same group 300 * or BT classic devices with the same address. 301 * @param updatedDevice the device state to synchronize. 302 */ 303 @GuardedBy({"mDevicesLock", "mDeviceInventoryLock"}) onSynchronizeAdiDeviceInInventory_l(AdiDeviceState updatedDevice)304 void onSynchronizeAdiDeviceInInventory_l(AdiDeviceState updatedDevice) { 305 boolean found = false; 306 found |= synchronizeBleDeviceInInventory(updatedDevice); 307 if (automaticBtDeviceType()) { 308 found |= synchronizeDeviceProfilesInInventory(updatedDevice); 309 } 310 if (found) { 311 mDeviceBroker.postPersistAudioDeviceSettings(); 312 } 313 } 314 315 @GuardedBy("mDeviceInventoryLock") checkDeviceInventorySize_l()316 private void checkDeviceInventorySize_l() { 317 if (mDeviceInventory.size() > MAX_DEVICE_INVENTORY_ENTRIES) { 318 // remove the first element 319 Iterator<Entry<Pair<Integer, String>, AdiDeviceState>> iterator = 320 mDeviceInventory.entrySet().iterator(); 321 if (iterator.hasNext()) { 322 iterator.next(); 323 iterator.remove(); 324 } 325 } 326 } 327 328 @GuardedBy({"mDevicesLock", "mDeviceInventoryLock"}) synchronizeBleDeviceInInventory(AdiDeviceState updatedDevice)329 private boolean synchronizeBleDeviceInInventory(AdiDeviceState updatedDevice) { 330 for (DeviceInfo di : mConnectedDevices.values()) { 331 if (di.mDeviceType != updatedDevice.getInternalDeviceType()) { 332 continue; 333 } 334 if (di.mDeviceAddress.equals(updatedDevice.getDeviceAddress())) { 335 for (AdiDeviceState ads2 : mDeviceInventory.values()) { 336 if (!(di.mDeviceType == ads2.getInternalDeviceType() 337 && di.mPeerDeviceAddress.equals(ads2.getDeviceAddress()))) { 338 continue; 339 } 340 if (mDeviceBroker.isSADevice(updatedDevice) 341 == mDeviceBroker.isSADevice(ads2)) { 342 ads2.setHasHeadTracker(updatedDevice.hasHeadTracker()); 343 ads2.setHeadTrackerEnabled(updatedDevice.isHeadTrackerEnabled()); 344 ads2.setSAEnabled(updatedDevice.isSAEnabled()); 345 } 346 ads2.setAudioDeviceCategory(updatedDevice.getAudioDeviceCategory()); 347 348 mDeviceBroker.postUpdatedAdiDeviceState(ads2, false /*initSA*/); 349 AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent( 350 "synchronizeBleDeviceInInventory synced device pair ads1=" 351 + updatedDevice + " ads2=" + ads2).printLog(TAG)); 352 return true; 353 } 354 } 355 if (di.mPeerDeviceAddress.equals(updatedDevice.getDeviceAddress())) { 356 for (AdiDeviceState ads2 : mDeviceInventory.values()) { 357 if (!(di.mDeviceType == ads2.getInternalDeviceType() 358 && di.mDeviceAddress.equals(ads2.getDeviceAddress()))) { 359 continue; 360 } 361 if (mDeviceBroker.isSADevice(updatedDevice) 362 == mDeviceBroker.isSADevice(ads2)) { 363 ads2.setHasHeadTracker(updatedDevice.hasHeadTracker()); 364 ads2.setHeadTrackerEnabled(updatedDevice.isHeadTrackerEnabled()); 365 ads2.setSAEnabled(updatedDevice.isSAEnabled()); 366 } 367 ads2.setAudioDeviceCategory(updatedDevice.getAudioDeviceCategory()); 368 369 mDeviceBroker.postUpdatedAdiDeviceState(ads2, false /*initSA*/); 370 AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent( 371 "synchronizeBleDeviceInInventory synced device pair ads1=" 372 + updatedDevice + " peer ads2=" + ads2).printLog(TAG)); 373 return true; 374 } 375 } 376 } 377 return false; 378 } 379 380 @GuardedBy("mDeviceInventoryLock") synchronizeDeviceProfilesInInventory(AdiDeviceState updatedDevice)381 private boolean synchronizeDeviceProfilesInInventory(AdiDeviceState updatedDevice) { 382 for (AdiDeviceState ads : mDeviceInventory.values()) { 383 if (updatedDevice.getInternalDeviceType() == ads.getInternalDeviceType() 384 || !updatedDevice.getDeviceAddress().equals(ads.getDeviceAddress())) { 385 continue; 386 } 387 if (mDeviceBroker.isSADevice(updatedDevice) == mDeviceBroker.isSADevice(ads)) { 388 ads.setHasHeadTracker(updatedDevice.hasHeadTracker()); 389 ads.setHeadTrackerEnabled(updatedDevice.isHeadTrackerEnabled()); 390 ads.setSAEnabled(updatedDevice.isSAEnabled()); 391 } 392 ads.setAudioDeviceCategory(updatedDevice.getAudioDeviceCategory()); 393 394 mDeviceBroker.postUpdatedAdiDeviceState(ads, false /*initSA*/); 395 AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent( 396 "synchronizeDeviceProfilesInInventory synced device pair ads1=" 397 + updatedDevice + " ads2=" + ads).printLog(TAG)); 398 return true; 399 } 400 return false; 401 } 402 403 /** 404 * Finds the BT device that matches the passed {@code address}. Currently, this method only 405 * returns a valid device for A2DP and BLE devices. 406 * 407 * @param address MAC address of BT device 408 * @param deviceType internal device type to identify the BT device 409 * @return the found {@link AdiDeviceState} or {@code null} otherwise. 410 */ 411 @Nullable 412 @VisibleForTesting(visibility = PACKAGE) findBtDeviceStateForAddress(String address, int deviceType)413 public AdiDeviceState findBtDeviceStateForAddress(String address, int deviceType) { 414 Set<Integer> deviceSet; 415 if (isBluetoothA2dpOutDevice(deviceType)) { 416 deviceSet = DEVICE_OUT_ALL_A2DP_SET; 417 } else if (isBluetoothLeOutDevice(deviceType)) { 418 deviceSet = DEVICE_OUT_ALL_BLE_SET; 419 } else if (isBluetoothScoOutDevice(deviceType)) { 420 deviceSet = DEVICE_OUT_ALL_SCO_SET; 421 } else if (deviceType == DEVICE_OUT_HEARING_AID) { 422 deviceSet = new HashSet<>(); 423 deviceSet.add(DEVICE_OUT_HEARING_AID); 424 } else { 425 return null; 426 } 427 synchronized (mDeviceInventoryLock) { 428 for (Integer internalType : deviceSet) { 429 AdiDeviceState deviceState = mDeviceInventory.get( 430 new Pair<>(internalType, address)); 431 if (deviceState != null) { 432 return deviceState; 433 } 434 } 435 } 436 return null; 437 } 438 439 /** 440 * Finds the device state that matches the passed {@link AudioDeviceAttributes} and device 441 * type. Note: currently this method only returns a valid device for A2DP and BLE devices. 442 * 443 * @param ada attributes of device to match 444 * @param canonicalDeviceType external device type to match 445 * @return the found {@link AdiDeviceState} matching a cached A2DP or BLE device or 446 * {@code null} otherwise. 447 */ 448 @Nullable findDeviceStateForAudioDeviceAttributes(AudioDeviceAttributes ada, int canonicalDeviceType)449 AdiDeviceState findDeviceStateForAudioDeviceAttributes(AudioDeviceAttributes ada, 450 int canonicalDeviceType) { 451 final boolean isWireless = isBluetoothDevice(ada.getInternalType()); 452 synchronized (mDeviceInventoryLock) { 453 for (AdiDeviceState deviceState : mDeviceInventory.values()) { 454 if (deviceState.getDeviceType() == canonicalDeviceType 455 && (!isWireless || ada.getAddress().equals( 456 deviceState.getDeviceAddress()))) { 457 return deviceState; 458 } 459 } 460 } 461 return null; 462 } 463 464 /** Clears all cached {@link AdiDeviceState}'s. */ clearDeviceInventory()465 void clearDeviceInventory() { 466 synchronized (mDeviceInventoryLock) { 467 mDeviceInventory.clear(); 468 } 469 } 470 471 // List of connected devices 472 // Key for map created from DeviceInfo.makeDeviceListKey() 473 @GuardedBy("mDevicesLock") 474 private final LinkedHashMap<String, DeviceInfo> mConnectedDevices = new LinkedHashMap<>() { 475 @Override 476 public DeviceInfo put(String key, DeviceInfo value) { 477 final DeviceInfo result = super.put(key, value); 478 record("put", true /* connected */, value); 479 return result; 480 } 481 482 @Override 483 public DeviceInfo putIfAbsent(String key, DeviceInfo value) { 484 final DeviceInfo result = super.putIfAbsent(key, value); 485 if (result == null) { 486 record("putIfAbsent", true /* connected */, value); 487 } 488 return result; 489 } 490 491 @Override 492 public DeviceInfo remove(Object key) { 493 final DeviceInfo result = super.remove(key); 494 if (result != null) { 495 record("remove", false /* connected */, result); 496 } 497 return result; 498 } 499 500 @Override 501 public boolean remove(Object key, Object value) { 502 final boolean result = super.remove(key, value); 503 if (result) { 504 record("remove", false /* connected */, (DeviceInfo) value); 505 } 506 return result; 507 } 508 509 // Not overridden 510 // clear 511 // compute 512 // computeIfAbsent 513 // computeIfPresent 514 // merge 515 // putAll 516 // replace 517 // replaceAll 518 private void record(String event, boolean connected, DeviceInfo value) { 519 // DeviceInfo - int mDeviceType; 520 // DeviceInfo - int mDeviceCodecFormat; 521 new MediaMetrics.Item(MediaMetrics.Name.AUDIO_DEVICE 522 + MediaMetrics.SEPARATOR + AudioSystem.getDeviceName(value.mDeviceType)) 523 .set(MediaMetrics.Property.ADDRESS, value.mDeviceAddress) 524 .set(MediaMetrics.Property.EVENT, event) 525 .set(MediaMetrics.Property.NAME, value.mDeviceName) 526 .set(MediaMetrics.Property.STATE, connected 527 ? MediaMetrics.Value.CONNECTED : MediaMetrics.Value.DISCONNECTED) 528 .record(); 529 } 530 }; 531 532 // List of devices actually connected to AudioPolicy (through AudioSystem), only one 533 // by device type, which is used as the key, value is the DeviceInfo generated key. 534 // For the moment only for A2DP sink devices. 535 // TODO: extend to all device types 536 @GuardedBy("mDevicesLock") 537 private final ArrayMap<Integer, String> mApmConnectedDevices = new ArrayMap<>(); 538 539 // List of preferred devices for strategies 540 private final ArrayMap<Integer, List<AudioDeviceAttributes>> mPreferredDevices = 541 new ArrayMap<>(); 542 543 // List of non-default devices for strategies 544 private final ArrayMap<Integer, List<AudioDeviceAttributes>> mNonDefaultDevices = 545 new ArrayMap<>(); 546 547 // List of preferred devices of capture preset 548 private final ArrayMap<Integer, List<AudioDeviceAttributes>> mPreferredDevicesForCapturePreset = 549 new ArrayMap<>(); 550 551 // the wrapper for AudioSystem static methods, allows us to spy AudioSystem 552 private final @NonNull AudioSystemAdapter mAudioSystem; 553 554 private @NonNull AudioDeviceBroker mDeviceBroker; 555 556 // Monitoring of audio routes. Protected by mAudioRoutes. 557 final AudioRoutesInfo mCurAudioRoutes = new AudioRoutesInfo(); 558 final RemoteCallbackList<IAudioRoutesObserver> mRoutesObservers = 559 new RemoteCallbackList<IAudioRoutesObserver>(); 560 561 // Monitoring of preferred device for strategies 562 final RemoteCallbackList<IStrategyPreferredDevicesDispatcher> mPrefDevDispatchers = 563 new RemoteCallbackList<IStrategyPreferredDevicesDispatcher>(); 564 565 // Monitoring of non-default device for strategies 566 final RemoteCallbackList<IStrategyNonDefaultDevicesDispatcher> mNonDefDevDispatchers = 567 new RemoteCallbackList<IStrategyNonDefaultDevicesDispatcher>(); 568 569 // Monitoring of devices for role and capture preset 570 final RemoteCallbackList<ICapturePresetDevicesRoleDispatcher> mDevRoleCapturePresetDispatchers = 571 new RemoteCallbackList<ICapturePresetDevicesRoleDispatcher>(); 572 573 final List<AudioProductStrategy> mStrategies; 574 AudioDeviceInventory(@onNull AudioDeviceBroker broker)575 /*package*/ AudioDeviceInventory(@NonNull AudioDeviceBroker broker) { 576 this(broker, AudioSystemAdapter.getDefaultAdapter()); 577 } 578 579 //----------------------------------------------------------- 580 /** for mocking only, allows to inject AudioSystem adapter */ AudioDeviceInventory(@onNull AudioSystemAdapter audioSystem)581 /*package*/ AudioDeviceInventory(@NonNull AudioSystemAdapter audioSystem) { 582 this(null, audioSystem); 583 } 584 AudioDeviceInventory(@ullable AudioDeviceBroker broker, @Nullable AudioSystemAdapter audioSystem)585 private AudioDeviceInventory(@Nullable AudioDeviceBroker broker, 586 @Nullable AudioSystemAdapter audioSystem) { 587 mDeviceBroker = broker; 588 mAudioSystem = audioSystem; 589 mStrategies = AudioProductStrategy.getAudioProductStrategies(); 590 mBluetoothDualModeEnabled = SystemProperties.getBoolean( 591 "persist.bluetooth.enable_dual_mode_audio", false); 592 } setDeviceBroker(@onNull AudioDeviceBroker broker)593 /*package*/ void setDeviceBroker(@NonNull AudioDeviceBroker broker) { 594 mDeviceBroker = broker; 595 } 596 597 //------------------------------------------------------------ 598 /** 599 * Class to store info about connected devices. 600 * Use makeDeviceListKey() to make a unique key for this list. 601 */ 602 private static class DeviceInfo { 603 final int mDeviceType; 604 final @NonNull String mDeviceName; 605 final @NonNull String mDeviceAddress; 606 @NonNull String mDeviceIdentityAddress; 607 int mDeviceCodecFormat; 608 final int mGroupId; 609 @NonNull String mPeerDeviceAddress; 610 @NonNull String mPeerIdentityDeviceAddress; 611 612 /** Disabled operating modes for this device. Use a negative logic so that by default 613 * an empty list means all modes are allowed. 614 * See BluetoothAdapter.AUDIO_MODE_DUPLEX and BluetoothAdapter.AUDIO_MODE_OUTPUT_ONLY */ 615 @NonNull ArraySet<String> mDisabledModes = new ArraySet(0); 616 DeviceInfo(int deviceType, String deviceName, String address, String identityAddress, int codecFormat, int groupId, String peerAddress, String peerIdentityAddress)617 DeviceInfo(int deviceType, String deviceName, String address, 618 String identityAddress, int codecFormat, 619 int groupId, String peerAddress, String peerIdentityAddress) { 620 mDeviceType = deviceType; 621 mDeviceName = TextUtils.emptyIfNull(deviceName); 622 mDeviceAddress = TextUtils.emptyIfNull(address); 623 mDeviceIdentityAddress = TextUtils.emptyIfNull(identityAddress); 624 if (mDeviceIdentityAddress.isEmpty()) { 625 mDeviceIdentityAddress = mDeviceAddress; 626 } 627 mDeviceCodecFormat = codecFormat; 628 mGroupId = groupId; 629 mPeerDeviceAddress = TextUtils.emptyIfNull(peerAddress); 630 mPeerIdentityDeviceAddress = TextUtils.emptyIfNull(peerIdentityAddress); 631 } 632 633 /** Constructor for all devices except A2DP sink and LE Audio */ DeviceInfo(int deviceType, String deviceName, String address)634 DeviceInfo(int deviceType, String deviceName, String address) { 635 this(deviceType, deviceName, address, null, AudioSystem.AUDIO_FORMAT_DEFAULT); 636 } 637 638 /** Constructor for A2DP sink devices */ DeviceInfo(int deviceType, String deviceName, String address, String identityAddress, int codecFormat)639 DeviceInfo(int deviceType, String deviceName, String address, 640 String identityAddress, int codecFormat) { 641 this(deviceType, deviceName, address, identityAddress, codecFormat, 642 BluetoothLeAudio.GROUP_ID_INVALID, null, null); 643 } 644 setModeDisabled(String mode)645 void setModeDisabled(String mode) { 646 mDisabledModes.add(mode); 647 } setModeEnabled(String mode)648 void setModeEnabled(String mode) { 649 mDisabledModes.remove(mode); 650 } isModeEnabled(String mode)651 boolean isModeEnabled(String mode) { 652 return !mDisabledModes.contains(mode); 653 } isOutputOnlyModeEnabled()654 boolean isOutputOnlyModeEnabled() { 655 return isModeEnabled(BluetoothAdapter.AUDIO_MODE_OUTPUT_ONLY); 656 } isDuplexModeEnabled()657 boolean isDuplexModeEnabled() { 658 return isModeEnabled(BluetoothAdapter.AUDIO_MODE_DUPLEX); 659 } 660 661 @Override toString()662 public String toString() { 663 return "[DeviceInfo: type:0x" + Integer.toHexString(mDeviceType) 664 + " (" + AudioSystem.getDeviceName(mDeviceType) 665 + ") name:" + mDeviceName 666 + " addr:" + Utils.anonymizeBluetoothAddress(mDeviceType, mDeviceAddress) 667 + " identity addr:" 668 + Utils.anonymizeBluetoothAddress(mDeviceType, mDeviceIdentityAddress) 669 + " codec: " + Integer.toHexString(mDeviceCodecFormat) 670 + " group:" + mGroupId 671 + " peer addr:" 672 + Utils.anonymizeBluetoothAddress(mDeviceType, mPeerDeviceAddress) 673 + " peer identity addr:" 674 + Utils.anonymizeBluetoothAddress(mDeviceType, mPeerIdentityDeviceAddress) 675 + " disabled modes: " + mDisabledModes + "]"; 676 } 677 getKey()678 @NonNull String getKey() { 679 return makeDeviceListKey(mDeviceType, mDeviceAddress); 680 } 681 682 /** 683 * Generate a unique key for the mConnectedDevices List by composing the device "type" 684 * and the "address" associated with a specific instance of that device type 685 */ makeDeviceListKey(int device, String deviceAddress)686 @NonNull private static String makeDeviceListKey(int device, String deviceAddress) { 687 return "0x" + Integer.toHexString(device) + ":" + deviceAddress; 688 } 689 } 690 691 /** 692 * A class just for packaging up a set of connection parameters. 693 */ 694 /*package*/ static class WiredDeviceConnectionState { 695 public final AudioDeviceAttributes mAttributes; 696 public final @AudioService.ConnectionState int mState; 697 public final String mCaller; 698 public boolean mForTest = false; 699 WiredDeviceConnectionState(AudioDeviceAttributes attributes, @AudioService.ConnectionState int state, String caller)700 /*package*/ WiredDeviceConnectionState(AudioDeviceAttributes attributes, 701 @AudioService.ConnectionState int state, String caller) { 702 mAttributes = attributes; 703 mState = state; 704 mCaller = caller; 705 } 706 } 707 708 //------------------------------------------------------------ dump(PrintWriter pw, String prefix)709 /*package*/ void dump(PrintWriter pw, String prefix) { 710 pw.println("\n" + prefix + "BECOMING_NOISY_INTENT_DEVICES_SET="); 711 BECOMING_NOISY_INTENT_DEVICES_SET.forEach(device -> { 712 pw.print(" 0x" + Integer.toHexString(device)); }); 713 pw.println("\n" + prefix + "Preferred devices for strategy:"); 714 mPreferredDevices.forEach((strategy, device) -> { 715 pw.println(" " + prefix + "strategy:" + strategy + " device:" + device); }); 716 pw.println("\n" + prefix + "Non-default devices for strategy:"); 717 mNonDefaultDevices.forEach((strategy, device) -> { 718 pw.println(" " + prefix + "strategy:" + strategy + " device:" + device); }); 719 pw.println("\n" + prefix + "Connected devices:"); 720 mConnectedDevices.forEach((key, deviceInfo) -> { 721 pw.println(" " + prefix + deviceInfo.toString()); }); 722 pw.println("\n" + prefix + "APM Connected device (A2DP sink only):"); 723 mApmConnectedDevices.forEach((keyType, valueAddress) -> { 724 pw.println(" " + prefix + " type:0x" + Integer.toHexString(keyType) 725 + " (" + AudioSystem.getDeviceName(keyType) 726 + ") addr:" + Utils.anonymizeBluetoothAddress(keyType, valueAddress)); }); 727 pw.println("\n" + prefix + "Preferred devices for capture preset:"); 728 mPreferredDevicesForCapturePreset.forEach((capturePreset, devices) -> { 729 pw.println(" " + prefix + "capturePreset:" + capturePreset 730 + " devices:" + devices); }); 731 pw.println("\n" + prefix + "Applied devices roles for strategies (from API):"); 732 mAppliedStrategyRoles.forEach((key, devices) -> { 733 pw.println(" " + prefix + "strategy: " + key.first 734 + " role:" + key.second + " devices:" + devices); }); 735 pw.println("\n" + prefix + "Applied devices roles for strategies (internal):"); 736 mAppliedStrategyRolesInt.forEach((key, devices) -> { 737 pw.println(" " + prefix + "strategy: " + key.first 738 + " role:" + key.second + " devices:" + devices); }); 739 pw.println("\n" + prefix + "Applied devices roles for presets (from API):"); 740 mAppliedPresetRoles.forEach((key, devices) -> { 741 pw.println(" " + prefix + "preset: " + key.first 742 + " role:" + key.second + " devices:" + devices); }); 743 pw.println("\n" + prefix + "Applied devices roles for presets (internal:"); 744 mAppliedPresetRolesInt.forEach((key, devices) -> { 745 pw.println(" " + prefix + "preset: " + key.first 746 + " role:" + key.second + " devices:" + devices); }); 747 pw.println("\ndevices:\n"); 748 synchronized (mDeviceInventoryLock) { 749 for (AdiDeviceState device : mDeviceInventory.values()) { 750 pw.println("\t" + device + "\n"); 751 } 752 } 753 } 754 755 //------------------------------------------------------------ 756 // Message handling from AudioDeviceBroker 757 758 /** 759 * Restore previously connected devices. Use in case of audio server crash 760 * (see AudioService.onAudioServerDied() method) 761 */ 762 // Always executed on AudioDeviceBroker message queue onRestoreDevices()763 /*package*/ void onRestoreDevices() { 764 synchronized (mDevicesLock) { 765 //TODO iterate on mApmConnectedDevices instead once it handles all device types 766 for (DeviceInfo di : mConnectedDevices.values()) { 767 mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(di.mDeviceType, 768 di.mDeviceAddress, 769 di.mDeviceName), 770 AudioSystem.DEVICE_STATE_AVAILABLE, 771 di.mDeviceCodecFormat); 772 } 773 mAppliedStrategyRolesInt.clear(); 774 mAppliedPresetRolesInt.clear(); 775 applyConnectedDevicesRoles_l(); 776 } 777 reapplyExternalDevicesRoles(); 778 } 779 reapplyExternalDevicesRoles()780 /*package*/ void reapplyExternalDevicesRoles() { 781 synchronized (mDevicesLock) { 782 mAppliedStrategyRoles.clear(); 783 mAppliedPresetRoles.clear(); 784 } 785 synchronized (mPreferredDevices) { 786 mPreferredDevices.forEach((strategy, devices) -> { 787 setPreferredDevicesForStrategy(strategy, devices); 788 }); 789 } 790 synchronized (mNonDefaultDevices) { 791 mNonDefaultDevices.forEach((strategy, devices) -> { 792 addDevicesRoleForStrategy(strategy, AudioSystem.DEVICE_ROLE_DISABLED, 793 devices, false /* internal */); 794 }); 795 } 796 synchronized (mPreferredDevicesForCapturePreset) { 797 mPreferredDevicesForCapturePreset.forEach((capturePreset, devices) -> { 798 setDevicesRoleForCapturePreset( 799 capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED, devices); 800 }); 801 } 802 } 803 804 /** only public for mocking/spying, do not call outside of AudioService */ 805 // @GuardedBy("mDeviceBroker.mSetModeLock") 806 @VisibleForTesting 807 //@GuardedBy("AudioDeviceBroker.this.mDeviceStateLock") onSetBtActiveDevice(@onNull AudioDeviceBroker.BtDeviceInfo btInfo, @AudioSystem.AudioFormatNativeEnumForBtCodec int codec, int streamType)808 public void onSetBtActiveDevice(@NonNull AudioDeviceBroker.BtDeviceInfo btInfo, 809 @AudioSystem.AudioFormatNativeEnumForBtCodec int codec, 810 int streamType) { 811 if (AudioService.DEBUG_DEVICES) { 812 Log.d(TAG, "onSetBtActiveDevice" 813 + " btDevice=" + btInfo.mDevice 814 + " profile=" + BluetoothProfile.getProfileName(btInfo.mProfile) 815 + " state=" + BluetoothProfile.getConnectionStateName(btInfo.mState)); 816 } 817 String address = btInfo.mDevice.getAddress(); 818 if (!BluetoothAdapter.checkBluetoothAddress(address)) { 819 address = ""; 820 } 821 822 AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent("BT connected:" 823 + btInfo + " codec=" + AudioSystem.audioFormatToString(codec))); 824 825 new MediaMetrics.Item(mMetricsId + "onSetBtActiveDevice") 826 .set(MediaMetrics.Property.STATUS, btInfo.mProfile) 827 .set(MediaMetrics.Property.DEVICE, 828 AudioSystem.getDeviceName(btInfo.mAudioSystemDevice)) 829 .set(MediaMetrics.Property.ADDRESS, address) 830 .set(MediaMetrics.Property.ENCODING, 831 AudioSystem.audioFormatToString(codec)) 832 .set(MediaMetrics.Property.EVENT, "onSetBtActiveDevice") 833 .set(MediaMetrics.Property.STREAM_TYPE, 834 AudioSystem.streamToString(streamType)) 835 .set(MediaMetrics.Property.STATE, 836 btInfo.mState == BluetoothProfile.STATE_CONNECTED 837 ? MediaMetrics.Value.CONNECTED : MediaMetrics.Value.DISCONNECTED) 838 .record(); 839 840 synchronized (mDevicesLock) { 841 final String key = DeviceInfo.makeDeviceListKey(btInfo.mAudioSystemDevice, address); 842 final DeviceInfo di = mConnectedDevices.get(key); 843 844 final boolean isConnected = di != null; 845 846 final boolean switchToUnavailable = isConnected 847 && btInfo.mState != BluetoothProfile.STATE_CONNECTED; 848 final boolean switchToAvailable = !isConnected 849 && btInfo.mState == BluetoothProfile.STATE_CONNECTED; 850 851 switch (btInfo.mProfile) { 852 case BluetoothProfile.A2DP_SINK: 853 if (switchToUnavailable) { 854 makeA2dpSrcUnavailable(address); 855 } else if (switchToAvailable) { 856 makeA2dpSrcAvailable(address); 857 } 858 break; 859 case BluetoothProfile.A2DP: 860 if (switchToUnavailable) { 861 makeA2dpDeviceUnavailableNow(address, di.mDeviceCodecFormat); 862 } else if (switchToAvailable) { 863 // device is not already connected 864 if (btInfo.mVolume != -1) { 865 mDeviceBroker.postSetVolumeIndexOnDevice(AudioSystem.STREAM_MUSIC, 866 // convert index to internal representation in VolumeStreamState 867 btInfo.mVolume * 10, btInfo.mAudioSystemDevice, 868 "onSetBtActiveDevice"); 869 } 870 makeA2dpDeviceAvailable(btInfo, codec, "onSetBtActiveDevice"); 871 } 872 break; 873 case BluetoothProfile.HEARING_AID: 874 if (switchToUnavailable) { 875 makeHearingAidDeviceUnavailable(address); 876 } else if (switchToAvailable) { 877 makeHearingAidDeviceAvailable(address, BtHelper.getName(btInfo.mDevice), 878 streamType, "onSetBtActiveDevice"); 879 } 880 break; 881 case BluetoothProfile.LE_AUDIO: 882 case BluetoothProfile.LE_AUDIO_BROADCAST: 883 if (switchToUnavailable) { 884 makeLeAudioDeviceUnavailableNow(address, 885 btInfo.mAudioSystemDevice, di.mDeviceCodecFormat); 886 } else if (switchToAvailable) { 887 makeLeAudioDeviceAvailable( 888 btInfo, streamType, codec, "onSetBtActiveDevice"); 889 } 890 break; 891 case BluetoothProfile.HEADSET: 892 if (mDeviceBroker.isScoManagedByAudio()) { 893 if (switchToUnavailable) { 894 mDeviceBroker.onSetBtScoActiveDevice(null); 895 } else if (switchToAvailable) { 896 mDeviceBroker.onSetBtScoActiveDevice(btInfo.mDevice); 897 } 898 } 899 break; 900 default: throw new IllegalArgumentException("Invalid profile " 901 + BluetoothProfile.getProfileName(btInfo.mProfile)); 902 } 903 } 904 } 905 906 // Additional delay added to the music mute duration when a codec config change is executed. 907 static final int BT_CONFIG_CHANGE_MUTE_DELAY_MS = 500; 908 909 /** 910 * Handles a Bluetooth link codec configuration change communicated by the Bluetooth stack. 911 * Called when either A2DP or LE Audio codec encoding or sampling rate changes: 912 * the change is communicated to native audio policy to eventually reconfigure the audio 913 * path. 914 * Also used to notify a change in preferred mode (duplex or output) for Bluetooth profiles. 915 * 916 * @param btInfo contains all information on the Bluetooth device and profile 917 * @param codec the requested audio encoding (e.g SBC) 918 * @param codecChanged true if a codec parameter changed, false for preferred mode change 919 * @param event currently only EVENT_DEVICE_CONFIG_CHANGE 920 * @return an optional additional delay in milliseconds to add to the music mute period in 921 * case of an actual codec reconfiguration. 922 */ 923 @GuardedBy("mDeviceBroker.mDeviceStateLock") onBluetoothDeviceConfigChange( @onNull AudioDeviceBroker.BtDeviceInfo btInfo, @AudioSystem.AudioFormatNativeEnumForBtCodec int codec, boolean codecChanged, int event)924 /*package*/ int onBluetoothDeviceConfigChange( 925 @NonNull AudioDeviceBroker.BtDeviceInfo btInfo, 926 @AudioSystem.AudioFormatNativeEnumForBtCodec int codec, 927 boolean codecChanged, int event) { 928 MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId 929 + "onBluetoothDeviceConfigChange") 930 .set(MediaMetrics.Property.EVENT, BtHelper.deviceEventToString(event)); 931 932 int delayMs = 0; 933 final BluetoothDevice btDevice = btInfo.mDevice; 934 if (btDevice == null) { 935 mmi.set(MediaMetrics.Property.EARLY_RETURN, "btDevice null").record(); 936 return delayMs; 937 } 938 if (AudioService.DEBUG_DEVICES) { 939 Log.d(TAG, "onBluetoothDeviceConfigChange btDevice=" + btDevice); 940 } 941 int volume = btInfo.mVolume; 942 943 String address = btDevice.getAddress(); 944 if (!BluetoothAdapter.checkBluetoothAddress(address)) { 945 address = ""; 946 } 947 AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent( 948 "onBluetoothDeviceConfigChange addr=" + address 949 + " event=" + BtHelper.deviceEventToString(event))); 950 951 synchronized (mDevicesLock) { 952 if (mDeviceBroker.hasScheduledA2dpConnection(btDevice)) { 953 AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent( 954 "A2dp config change ignored (scheduled connection change)") 955 .printSlog(EventLogger.Event.ALOGI, TAG)); 956 mmi.set(MediaMetrics.Property.EARLY_RETURN, "A2dp config change ignored") 957 .record(); 958 return delayMs; 959 } 960 final String key = DeviceInfo.makeDeviceListKey( 961 AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address); 962 final DeviceInfo di = mConnectedDevices.get(key); 963 if (di == null) { 964 Log.e(TAG, "invalid null DeviceInfo in onBluetoothDeviceConfigChange"); 965 mmi.set(MediaMetrics.Property.EARLY_RETURN, "null DeviceInfo").record(); 966 return delayMs; 967 } 968 969 mmi.set(MediaMetrics.Property.ADDRESS, address) 970 .set(MediaMetrics.Property.ENCODING, AudioSystem.audioFormatToString(codec)) 971 .set(MediaMetrics.Property.INDEX, volume) 972 .set(MediaMetrics.Property.NAME, di.mDeviceName); 973 974 if (event == BtHelper.EVENT_DEVICE_CONFIG_CHANGE) { 975 if (btInfo.mProfile == BluetoothProfile.A2DP 976 || btInfo.mProfile == BluetoothProfile.LE_AUDIO 977 || btInfo.mProfile == BluetoothProfile.LE_AUDIO_BROADCAST) { 978 if (codecChanged) { 979 di.mDeviceCodecFormat = codec; 980 mConnectedDevices.replace(key, di); 981 final int res = mAudioSystem.handleDeviceConfigChange( 982 btInfo.mAudioSystemDevice, address, 983 BtHelper.getName(btDevice), codec); 984 if (res != AudioSystem.AUDIO_STATUS_OK) { 985 AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent( 986 "APM handleDeviceConfigChange failed for A2DP device addr=" 987 + address + " codec=" 988 + AudioSystem.audioFormatToString(codec)) 989 .printSlog(EventLogger.Event.ALOGE, TAG)); 990 991 // force A2DP device disconnection in case of error so that AudioService 992 // state is consistent with audio policy manager state 993 setBluetoothActiveDevice(new AudioDeviceBroker.BtDeviceInfo(btInfo, 994 BluetoothProfile.STATE_DISCONNECTED)); 995 } else { 996 AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent( 997 "APM handleDeviceConfigChange success for A2DP device addr=" 998 + address 999 + " codec=" + AudioSystem.audioFormatToString(codec)) 1000 .printSlog(EventLogger.Event.ALOGI, TAG)); 1001 delayMs = BT_CONFIG_CHANGE_MUTE_DELAY_MS; 1002 } 1003 } 1004 } 1005 if (!codecChanged) { 1006 updateBluetoothPreferredModes_l(btDevice /*connectedDevice*/); 1007 } 1008 } 1009 } 1010 mmi.record(); 1011 return delayMs; 1012 } 1013 onMakeA2dpDeviceUnavailableNow(String address, int a2dpCodec)1014 /*package*/ void onMakeA2dpDeviceUnavailableNow(String address, int a2dpCodec) { 1015 synchronized (mDevicesLock) { 1016 makeA2dpDeviceUnavailableNow(address, a2dpCodec); 1017 } 1018 } 1019 onMakeLeAudioDeviceUnavailableNow(String address, int device, int codec)1020 /*package*/ void onMakeLeAudioDeviceUnavailableNow(String address, int device, int codec) { 1021 synchronized (mDevicesLock) { 1022 makeLeAudioDeviceUnavailableNow(address, device, codec); 1023 } 1024 } 1025 1026 1027 /** 1028 * Goes over all connected LE Audio devices in the provided group ID and 1029 * update: 1030 * - the peer address according to the addres of other device in the same 1031 * group (can also clear the peer address is not anymore in the group) 1032 * - The dentity address if not yet set. 1033 * LE Audio buds in a pair are in the same group. 1034 * @param groupId the LE Audio group to update 1035 */ onUpdateLeAudioGroupAddresses(int groupId)1036 /*package*/ void onUpdateLeAudioGroupAddresses(int groupId) { 1037 synchronized (mDevicesLock) { 1038 // <address, identy address> 1039 List<Pair<String, String>> addresses = new ArrayList<>(); 1040 for (DeviceInfo di : mConnectedDevices.values()) { 1041 if (di.mGroupId == groupId) { 1042 if (addresses.isEmpty()) { 1043 addresses = mDeviceBroker.getLeAudioGroupAddresses(groupId); 1044 } 1045 if (di.mPeerDeviceAddress.equals("")) { 1046 for (Pair<String, String> addr : addresses) { 1047 if (!di.mDeviceAddress.equals(addr.first)) { 1048 di.mPeerDeviceAddress = TextUtils.emptyIfNull(addr.first); 1049 di.mPeerIdentityDeviceAddress = TextUtils.emptyIfNull(addr.second); 1050 break; 1051 } 1052 } 1053 } else if (!addresses.contains( 1054 new Pair(di.mPeerDeviceAddress, di.mPeerIdentityDeviceAddress))) { 1055 di.mPeerDeviceAddress = ""; 1056 di.mPeerIdentityDeviceAddress = ""; 1057 } 1058 if (di.mDeviceIdentityAddress.equals("")) { 1059 for (Pair<String, String> addr : addresses) { 1060 if (di.mDeviceAddress.equals(addr.first)) { 1061 di.mDeviceIdentityAddress = TextUtils.emptyIfNull(addr.second); 1062 break; 1063 } 1064 } 1065 } 1066 } 1067 } 1068 } 1069 } 1070 onReportNewRoutes()1071 /*package*/ void onReportNewRoutes() { 1072 int n = mRoutesObservers.beginBroadcast(); 1073 if (n > 0) { 1074 new MediaMetrics.Item(mMetricsId + "onReportNewRoutes") 1075 .set(MediaMetrics.Property.OBSERVERS, n) 1076 .record(); 1077 AudioRoutesInfo routes; 1078 synchronized (mCurAudioRoutes) { 1079 routes = new AudioRoutesInfo(mCurAudioRoutes); 1080 } 1081 while (n > 0) { 1082 n--; 1083 IAudioRoutesObserver obs = mRoutesObservers.getBroadcastItem(n); 1084 try { 1085 obs.dispatchAudioRoutesChanged(routes); 1086 } catch (RemoteException e) { 1087 Log.e(TAG, "onReportNewRoutes", e); 1088 } 1089 } 1090 } 1091 mRoutesObservers.finishBroadcast(); 1092 mDeviceBroker.postObserveDevicesForAllStreams(); 1093 } 1094 1095 /* package */ static final Set<Integer> DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG_SET; 1096 static { 1097 DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG_SET = new HashSet<>(); 1098 DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG_SET.add(AudioSystem.DEVICE_OUT_WIRED_HEADSET); 1099 DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG_SET.add(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE); 1100 DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG_SET.add(AudioSystem.DEVICE_OUT_LINE); 1101 DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG_SET.addAll(AudioSystem.DEVICE_OUT_ALL_USB_SET); 1102 } 1103 onSetWiredDeviceConnectionState( AudioDeviceInventory.WiredDeviceConnectionState wdcs)1104 /*package*/ void onSetWiredDeviceConnectionState( 1105 AudioDeviceInventory.WiredDeviceConnectionState wdcs) { 1106 int type = wdcs.mAttributes.getInternalType(); 1107 1108 AudioService.sDeviceLogger.enqueue(new AudioServiceEvents.WiredDevConnectEvent(wdcs)); 1109 1110 MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId 1111 + "onSetWiredDeviceConnectionState") 1112 .set(MediaMetrics.Property.ADDRESS, wdcs.mAttributes.getAddress()) 1113 .set(MediaMetrics.Property.DEVICE, 1114 AudioSystem.getDeviceName(type)) 1115 .set(MediaMetrics.Property.STATE, 1116 wdcs.mState == AudioService.CONNECTION_STATE_DISCONNECTED 1117 ? MediaMetrics.Value.DISCONNECTED : MediaMetrics.Value.CONNECTED); 1118 AudioDeviceInfo info = null; 1119 if (wdcs.mState == AudioService.CONNECTION_STATE_DISCONNECTED 1120 && AudioSystem.DEVICE_OUT_ALL_USB_SET.contains( 1121 wdcs.mAttributes.getInternalType())) { 1122 for (AudioDeviceInfo deviceInfo : AudioManager.getDevicesStatic( 1123 AudioManager.GET_DEVICES_OUTPUTS)) { 1124 if (deviceInfo.getInternalType() == wdcs.mAttributes.getInternalType()) { 1125 info = deviceInfo; 1126 break; 1127 } 1128 } 1129 } 1130 synchronized (mDevicesLock) { 1131 if ((wdcs.mState == AudioService.CONNECTION_STATE_DISCONNECTED) 1132 && DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG_SET.contains(type)) { 1133 mDeviceBroker.setBluetoothA2dpOnInt(true, false /*fromA2dp*/, 1134 "onSetWiredDeviceConnectionState state DISCONNECTED"); 1135 } 1136 1137 if (!handleDeviceConnection(wdcs.mAttributes, 1138 wdcs.mState == AudioService.CONNECTION_STATE_CONNECTED, wdcs.mForTest, null)) { 1139 // change of connection state failed, bailout 1140 mmi.set(MediaMetrics.Property.EARLY_RETURN, "change of connection state failed") 1141 .record(); 1142 return; 1143 } 1144 if (wdcs.mState != AudioService.CONNECTION_STATE_DISCONNECTED) { 1145 if (DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG_SET.contains(type)) { 1146 mDeviceBroker.setBluetoothA2dpOnInt(false, false /*fromA2dp*/, 1147 "onSetWiredDeviceConnectionState state not DISCONNECTED"); 1148 } 1149 mDeviceBroker.checkMusicActive(type, wdcs.mCaller); 1150 } 1151 if (type == AudioSystem.DEVICE_OUT_HDMI) { 1152 mDeviceBroker.checkVolumeCecOnHdmiConnection(wdcs.mState, wdcs.mCaller); 1153 } 1154 if (wdcs.mState == AudioService.CONNECTION_STATE_DISCONNECTED 1155 && AudioSystem.DEVICE_OUT_ALL_USB_SET.contains( 1156 wdcs.mAttributes.getInternalType())) { 1157 if (info != null) { 1158 mDeviceBroker.dispatchPreferredMixerAttributesChangedCausedByDeviceRemoved( 1159 info); 1160 } else { 1161 Log.e(TAG, "Didn't find AudioDeviceInfo to notify preferred mixer " 1162 + "attributes change for type=" + wdcs.mAttributes.getType()); 1163 } 1164 } 1165 sendDeviceConnectionIntent(type, wdcs.mState, 1166 wdcs.mAttributes.getAddress(), wdcs.mAttributes.getName()); 1167 updateAudioRoutes(type, wdcs.mState); 1168 } 1169 mmi.record(); 1170 } 1171 onToggleHdmi()1172 /*package*/ void onToggleHdmi() { 1173 MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId + "onToggleHdmi") 1174 .set(MediaMetrics.Property.DEVICE, 1175 AudioSystem.getDeviceName(AudioSystem.DEVICE_OUT_HDMI)); 1176 synchronized (mDevicesLock) { 1177 // Is HDMI connected? 1178 final String key = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_HDMI, ""); 1179 final DeviceInfo di = mConnectedDevices.get(key); 1180 if (di == null) { 1181 Log.e(TAG, "invalid null DeviceInfo in onToggleHdmi"); 1182 mmi.set(MediaMetrics.Property.EARLY_RETURN, "invalid null DeviceInfo").record(); 1183 return; 1184 } 1185 // Toggle HDMI to retrigger broadcast with proper formats. 1186 setWiredDeviceConnectionState( 1187 new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_HDMI, ""), 1188 AudioSystem.DEVICE_STATE_UNAVAILABLE, "android"); // disconnect 1189 setWiredDeviceConnectionState( 1190 new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_HDMI, ""), 1191 AudioSystem.DEVICE_STATE_AVAILABLE, "android"); // reconnect 1192 } 1193 mmi.record(); 1194 } 1195 onSaveSetPreferredDevices(int strategy, @NonNull List<AudioDeviceAttributes> devices)1196 /*package*/ void onSaveSetPreferredDevices(int strategy, 1197 @NonNull List<AudioDeviceAttributes> devices) { 1198 mPreferredDevices.put(strategy, devices); 1199 List<AudioDeviceAttributes> nonDefaultDevices = mNonDefaultDevices.get(strategy); 1200 if (nonDefaultDevices != null) { 1201 nonDefaultDevices.removeAll(devices); 1202 1203 if (nonDefaultDevices.isEmpty()) { 1204 mNonDefaultDevices.remove(strategy); 1205 } else { 1206 mNonDefaultDevices.put(strategy, nonDefaultDevices); 1207 } 1208 dispatchNonDefaultDevice(strategy, nonDefaultDevices); 1209 } 1210 1211 dispatchPreferredDevice(strategy, devices); 1212 } 1213 onSaveRemovePreferredDevices(int strategy)1214 /*package*/ void onSaveRemovePreferredDevices(int strategy) { 1215 mPreferredDevices.remove(strategy); 1216 dispatchPreferredDevice(strategy, new ArrayList<AudioDeviceAttributes>()); 1217 } 1218 onSaveSetDeviceAsNonDefault(int strategy, @NonNull AudioDeviceAttributes device)1219 /*package*/ void onSaveSetDeviceAsNonDefault(int strategy, 1220 @NonNull AudioDeviceAttributes device) { 1221 List<AudioDeviceAttributes> nonDefaultDevices = mNonDefaultDevices.get(strategy); 1222 if (nonDefaultDevices == null) { 1223 nonDefaultDevices = new ArrayList<>(); 1224 } 1225 1226 if (!nonDefaultDevices.contains(device)) { 1227 nonDefaultDevices.add(device); 1228 } 1229 1230 mNonDefaultDevices.put(strategy, nonDefaultDevices); 1231 dispatchNonDefaultDevice(strategy, nonDefaultDevices); 1232 1233 List<AudioDeviceAttributes> preferredDevices = mPreferredDevices.get(strategy); 1234 1235 if (preferredDevices != null) { 1236 preferredDevices.remove(device); 1237 mPreferredDevices.put(strategy, preferredDevices); 1238 1239 dispatchPreferredDevice(strategy, preferredDevices); 1240 } 1241 } 1242 onSaveRemoveDeviceAsNonDefault(int strategy, @NonNull AudioDeviceAttributes device)1243 /*package*/ void onSaveRemoveDeviceAsNonDefault(int strategy, 1244 @NonNull AudioDeviceAttributes device) { 1245 List<AudioDeviceAttributes> nonDefaultDevices = mNonDefaultDevices.get(strategy); 1246 if (nonDefaultDevices != null) { 1247 nonDefaultDevices.remove(device); 1248 mNonDefaultDevices.put(strategy, nonDefaultDevices); 1249 dispatchNonDefaultDevice(strategy, nonDefaultDevices); 1250 } 1251 } 1252 onSaveSetPreferredDevicesForCapturePreset( int capturePreset, @NonNull List<AudioDeviceAttributes> devices)1253 /*package*/ void onSaveSetPreferredDevicesForCapturePreset( 1254 int capturePreset, @NonNull List<AudioDeviceAttributes> devices) { 1255 mPreferredDevicesForCapturePreset.put(capturePreset, devices); 1256 dispatchDevicesRoleForCapturePreset( 1257 capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED, devices); 1258 } 1259 onSaveClearPreferredDevicesForCapturePreset(int capturePreset)1260 /*package*/ void onSaveClearPreferredDevicesForCapturePreset(int capturePreset) { 1261 mPreferredDevicesForCapturePreset.remove(capturePreset); 1262 dispatchDevicesRoleForCapturePreset( 1263 capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED, 1264 new ArrayList<AudioDeviceAttributes>()); 1265 } 1266 1267 //------------------------------------------------------------ 1268 // preferred/non-default device(s) 1269 setPreferredDevicesForStrategyAndSave(int strategy, @NonNull List<AudioDeviceAttributes> devices)1270 /*package*/ int setPreferredDevicesForStrategyAndSave(int strategy, 1271 @NonNull List<AudioDeviceAttributes> devices) { 1272 final int status = setPreferredDevicesForStrategy(strategy, devices); 1273 if (status == AudioSystem.SUCCESS) { 1274 mDeviceBroker.postSaveSetPreferredDevicesForStrategy(strategy, devices); 1275 } 1276 return status; 1277 } 1278 // Only used for external requests coming from an API setPreferredDevicesForStrategy(int strategy, @NonNull List<AudioDeviceAttributes> devices)1279 /*package*/ int setPreferredDevicesForStrategy(int strategy, 1280 @NonNull List<AudioDeviceAttributes> devices) { 1281 int status = AudioSystem.ERROR; 1282 try (SafeCloseable ignored = ClearCallingIdentityContext.create()) { 1283 status = setDevicesRoleForStrategy( 1284 strategy, AudioSystem.DEVICE_ROLE_PREFERRED, devices, false /* internal */); 1285 } 1286 return status; 1287 } 1288 // Only used for internal requests setPreferredDevicesForStrategyInt(int strategy, @NonNull List<AudioDeviceAttributes> devices)1289 /*package*/ int setPreferredDevicesForStrategyInt(int strategy, 1290 @NonNull List<AudioDeviceAttributes> devices) { 1291 1292 return setDevicesRoleForStrategy( 1293 strategy, AudioSystem.DEVICE_ROLE_PREFERRED, devices, true /* internal */); 1294 } 1295 removePreferredDevicesForStrategyAndSave(int strategy)1296 /*package*/ int removePreferredDevicesForStrategyAndSave(int strategy) { 1297 final int status = removePreferredDevicesForStrategy(strategy); 1298 if (status == AudioSystem.SUCCESS) { 1299 mDeviceBroker.postSaveRemovePreferredDevicesForStrategy(strategy); 1300 } 1301 return status; 1302 } 1303 // Only used for external requests coming from an API removePreferredDevicesForStrategy(int strategy)1304 /*package*/ int removePreferredDevicesForStrategy(int strategy) { 1305 int status = AudioSystem.ERROR; 1306 1307 try (SafeCloseable ignored = ClearCallingIdentityContext.create()) { 1308 status = clearDevicesRoleForStrategy( 1309 strategy, AudioSystem.DEVICE_ROLE_PREFERRED, false /*internal */); 1310 } 1311 return status; 1312 } 1313 // Only used for internal requests removePreferredDevicesForStrategyInt(int strategy)1314 /*package*/ int removePreferredDevicesForStrategyInt(int strategy) { 1315 return clearDevicesRoleForStrategy( 1316 strategy, AudioSystem.DEVICE_ROLE_PREFERRED, true /*internal */); 1317 } 1318 setDeviceAsNonDefaultForStrategyAndSave(int strategy, @NonNull AudioDeviceAttributes device)1319 /*package*/ int setDeviceAsNonDefaultForStrategyAndSave(int strategy, 1320 @NonNull AudioDeviceAttributes device) { 1321 int status = AudioSystem.ERROR; 1322 1323 try (SafeCloseable ignored = ClearCallingIdentityContext.create()) { 1324 List<AudioDeviceAttributes> devices = new ArrayList<>(); 1325 devices.add(device); 1326 status = addDevicesRoleForStrategy( 1327 strategy, AudioSystem.DEVICE_ROLE_DISABLED, devices, false /* internal */); 1328 } 1329 1330 if (status == AudioSystem.SUCCESS) { 1331 mDeviceBroker.postSaveSetDeviceAsNonDefaultForStrategy(strategy, device); 1332 } 1333 return status; 1334 } 1335 removeDeviceAsNonDefaultForStrategyAndSave(int strategy, @NonNull AudioDeviceAttributes device)1336 /*package*/ int removeDeviceAsNonDefaultForStrategyAndSave(int strategy, 1337 @NonNull AudioDeviceAttributes device) { 1338 int status = AudioSystem.ERROR; 1339 1340 try (SafeCloseable ignored = ClearCallingIdentityContext.create()) { 1341 List<AudioDeviceAttributes> devices = new ArrayList<>(); 1342 devices.add(device); 1343 status = removeDevicesRoleForStrategy( 1344 strategy, AudioSystem.DEVICE_ROLE_DISABLED, devices, false /* internal */); 1345 } 1346 1347 if (status == AudioSystem.SUCCESS) { 1348 mDeviceBroker.postSaveRemoveDeviceAsNonDefaultForStrategy(strategy, device); 1349 } 1350 return status; 1351 } 1352 1353 registerStrategyPreferredDevicesDispatcher( @onNull IStrategyPreferredDevicesDispatcher dispatcher, boolean isPrivileged)1354 /*package*/ void registerStrategyPreferredDevicesDispatcher( 1355 @NonNull IStrategyPreferredDevicesDispatcher dispatcher, boolean isPrivileged) { 1356 mPrefDevDispatchers.register(dispatcher, isPrivileged); 1357 } 1358 unregisterStrategyPreferredDevicesDispatcher( @onNull IStrategyPreferredDevicesDispatcher dispatcher)1359 /*package*/ void unregisterStrategyPreferredDevicesDispatcher( 1360 @NonNull IStrategyPreferredDevicesDispatcher dispatcher) { 1361 mPrefDevDispatchers.unregister(dispatcher); 1362 } 1363 registerStrategyNonDefaultDevicesDispatcher( @onNull IStrategyNonDefaultDevicesDispatcher dispatcher, boolean isPrivileged)1364 /*package*/ void registerStrategyNonDefaultDevicesDispatcher( 1365 @NonNull IStrategyNonDefaultDevicesDispatcher dispatcher, boolean isPrivileged) { 1366 mNonDefDevDispatchers.register(dispatcher, isPrivileged); 1367 } 1368 unregisterStrategyNonDefaultDevicesDispatcher( @onNull IStrategyNonDefaultDevicesDispatcher dispatcher)1369 /*package*/ void unregisterStrategyNonDefaultDevicesDispatcher( 1370 @NonNull IStrategyNonDefaultDevicesDispatcher dispatcher) { 1371 mNonDefDevDispatchers.unregister(dispatcher); 1372 } 1373 setPreferredDevicesForCapturePresetAndSave( int capturePreset, @NonNull List<AudioDeviceAttributes> devices)1374 /*package*/ int setPreferredDevicesForCapturePresetAndSave( 1375 int capturePreset, @NonNull List<AudioDeviceAttributes> devices) { 1376 final int status = setPreferredDevicesForCapturePreset(capturePreset, devices); 1377 if (status == AudioSystem.SUCCESS) { 1378 mDeviceBroker.postSaveSetPreferredDevicesForCapturePreset(capturePreset, devices); 1379 } 1380 return status; 1381 } 1382 1383 // Only used for external requests coming from an API setPreferredDevicesForCapturePreset( int capturePreset, @NonNull List<AudioDeviceAttributes> devices)1384 private int setPreferredDevicesForCapturePreset( 1385 int capturePreset, @NonNull List<AudioDeviceAttributes> devices) { 1386 int status = AudioSystem.ERROR; 1387 try (SafeCloseable ignored = ClearCallingIdentityContext.create()) { 1388 status = setDevicesRoleForCapturePreset( 1389 capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED, devices); 1390 } 1391 return status; 1392 } 1393 clearPreferredDevicesForCapturePresetAndSave(int capturePreset)1394 /*package*/ int clearPreferredDevicesForCapturePresetAndSave(int capturePreset) { 1395 final int status = clearPreferredDevicesForCapturePreset(capturePreset); 1396 if (status == AudioSystem.SUCCESS) { 1397 mDeviceBroker.postSaveClearPreferredDevicesForCapturePreset(capturePreset); 1398 } 1399 return status; 1400 } 1401 1402 // Only used for external requests coming from an API clearPreferredDevicesForCapturePreset(int capturePreset)1403 private int clearPreferredDevicesForCapturePreset(int capturePreset) { 1404 int status = AudioSystem.ERROR; 1405 1406 try (SafeCloseable ignored = ClearCallingIdentityContext.create()) { 1407 status = clearDevicesRoleForCapturePreset( 1408 capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED); 1409 } 1410 return status; 1411 } 1412 1413 // Only used for internal requests addDevicesRoleForCapturePresetInt(int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devices)1414 private int addDevicesRoleForCapturePresetInt(int capturePreset, int role, 1415 @NonNull List<AudioDeviceAttributes> devices) { 1416 return addDevicesRole(mAppliedPresetRolesInt, (p, r, d) -> { 1417 return mAudioSystem.addDevicesRoleForCapturePreset(p, r, d); 1418 }, capturePreset, role, devices); 1419 } 1420 1421 // Only used for internal requests removeDevicesRoleForCapturePresetInt(int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devices)1422 private int removeDevicesRoleForCapturePresetInt(int capturePreset, int role, 1423 @NonNull List<AudioDeviceAttributes> devices) { 1424 return removeDevicesRole(mAppliedPresetRolesInt, (p, r, d) -> { 1425 return mAudioSystem.removeDevicesRoleForCapturePreset(p, r, d); 1426 }, capturePreset, role, devices); 1427 } 1428 1429 // Only used for external requests coming from an API 1430 private int setDevicesRoleForCapturePreset(int capturePreset, int role, 1431 @NonNull List<AudioDeviceAttributes> devices) { 1432 return setDevicesRole(mAppliedPresetRoles, (p, r, d) -> { 1433 return mAudioSystem.addDevicesRoleForCapturePreset(p, r, d); 1434 }, (p, r, d) -> { 1435 return mAudioSystem.clearDevicesRoleForCapturePreset(p, r); 1436 }, capturePreset, role, devices); 1437 } 1438 1439 // Only used for external requests coming from an API 1440 private int clearDevicesRoleForCapturePreset(int capturePreset, int role) { 1441 return clearDevicesRole(mAppliedPresetRoles, (p, r, d) -> { 1442 return mAudioSystem.clearDevicesRoleForCapturePreset(p, r); 1443 }, capturePreset, role); 1444 } 1445 1446 /*package*/ void registerCapturePresetDevicesRoleDispatcher( 1447 @NonNull ICapturePresetDevicesRoleDispatcher dispatcher, boolean isPrivileged) { 1448 mDevRoleCapturePresetDispatchers.register(dispatcher, isPrivileged); 1449 } 1450 1451 /*package*/ void unregisterCapturePresetDevicesRoleDispatcher( 1452 @NonNull ICapturePresetDevicesRoleDispatcher dispatcher) { 1453 mDevRoleCapturePresetDispatchers.unregister(dispatcher); 1454 } 1455 1456 private int addDevicesRoleForStrategy(int strategy, int role, 1457 @NonNull List<AudioDeviceAttributes> devices, 1458 boolean internal) { 1459 return addDevicesRole(internal ? mAppliedStrategyRolesInt : mAppliedStrategyRoles, 1460 (s, r, d) -> { 1461 return mAudioSystem.setDevicesRoleForStrategy(s, r, d); 1462 }, strategy, role, devices); 1463 } 1464 1465 private int removeDevicesRoleForStrategy(int strategy, int role, 1466 @NonNull List<AudioDeviceAttributes> devices, 1467 boolean internal) { 1468 return removeDevicesRole(internal ? mAppliedStrategyRolesInt : mAppliedStrategyRoles, 1469 (s, r, d) -> { 1470 return mAudioSystem.removeDevicesRoleForStrategy(s, r, d); 1471 }, strategy, role, devices); 1472 } 1473 1474 private int setDevicesRoleForStrategy(int strategy, int role, 1475 @NonNull List<AudioDeviceAttributes> devices, 1476 boolean internal) { 1477 return setDevicesRole(internal ? mAppliedStrategyRolesInt : mAppliedStrategyRoles, 1478 (s, r, d) -> { 1479 return mAudioSystem.setDevicesRoleForStrategy(s, r, d); 1480 }, (s, r, d) -> { 1481 return mAudioSystem.clearDevicesRoleForStrategy(s, r); 1482 }, strategy, role, devices); 1483 } 1484 1485 private int clearDevicesRoleForStrategy(int strategy, int role, boolean internal) { 1486 return clearDevicesRole(internal ? mAppliedStrategyRolesInt : mAppliedStrategyRoles, 1487 (s, r, d) -> { 1488 return mAudioSystem.clearDevicesRoleForStrategy(s, r); 1489 }, strategy, role); 1490 } 1491 1492 //------------------------------------------------------------ 1493 // Cache for applied roles for strategies and devices. The cache avoids reapplying the 1494 // same list of devices for a given role and strategy and the corresponding systematic 1495 // redundant work in audio policy manager and audio flinger. 1496 // The key is the pair <Strategy , Role> and the value is the current list of devices. 1497 // mAppliedStrategyRoles is for requests coming from an API. 1498 // mAppliedStrategyRolesInt is for internal requests. Entries are removed when the requested 1499 // device is disconnected. 1500 private final ArrayMap<Pair<Integer, Integer>, List<AudioDeviceAttributes>> 1501 mAppliedStrategyRoles = new ArrayMap<>(); 1502 private final ArrayMap<Pair<Integer, Integer>, List<AudioDeviceAttributes>> 1503 mAppliedStrategyRolesInt = new ArrayMap<>(); 1504 1505 // Cache for applied roles for capture presets and devices. The cache avoids reapplying the 1506 // same list of devices for a given role and capture preset and the corresponding systematic 1507 // redundant work in audio policy manager and audio flinger. 1508 // The key is the pair <Preset , Role> and the value is the current list of devices. 1509 // mAppliedPresetRoles is for requests coming from an API. 1510 // mAppliedPresetRolesInt is for internal requests. Entries are removed when the requested 1511 // device is disconnected. 1512 private final ArrayMap<Pair<Integer, Integer>, List<AudioDeviceAttributes>> 1513 mAppliedPresetRoles = new ArrayMap<>(); 1514 private final ArrayMap<Pair<Integer, Integer>, List<AudioDeviceAttributes>> 1515 mAppliedPresetRolesInt = new ArrayMap<>(); 1516 1517 interface AudioSystemInterface { 1518 int deviceRoleAction(int usecase, int role, @Nullable List<AudioDeviceAttributes> devices); 1519 } 1520 1521 private int addDevicesRole( 1522 ArrayMap<Pair<Integer, Integer>, List<AudioDeviceAttributes>> rolesMap, 1523 AudioSystemInterface asi, 1524 int useCase, int role, @NonNull List<AudioDeviceAttributes> devices) { 1525 synchronized (rolesMap) { 1526 Pair<Integer, Integer> key = new Pair<>(useCase, role); 1527 List<AudioDeviceAttributes> roleDevices = new ArrayList<>(); 1528 List<AudioDeviceAttributes> appliedDevices = new ArrayList<>(); 1529 1530 if (rolesMap.containsKey(key)) { 1531 roleDevices = rolesMap.get(key); 1532 for (AudioDeviceAttributes device : devices) { 1533 if (!roleDevices.contains(device)) { 1534 appliedDevices.add(device); 1535 } 1536 } 1537 } else { 1538 appliedDevices.addAll(devices); 1539 } 1540 if (appliedDevices.isEmpty()) { 1541 return AudioSystem.SUCCESS; 1542 } 1543 final int status = asi.deviceRoleAction(useCase, role, appliedDevices); 1544 if (status == AudioSystem.SUCCESS) { 1545 roleDevices.addAll(appliedDevices); 1546 rolesMap.put(key, roleDevices); 1547 } 1548 return status; 1549 } 1550 } 1551 1552 private int removeDevicesRole( 1553 ArrayMap<Pair<Integer, Integer>, List<AudioDeviceAttributes>> rolesMap, 1554 AudioSystemInterface asi, 1555 int useCase, int role, @NonNull List<AudioDeviceAttributes> devices) { 1556 synchronized (rolesMap) { 1557 Pair<Integer, Integer> key = new Pair<>(useCase, role); 1558 if (!rolesMap.containsKey(key)) { 1559 // trying to remove a role for a device that wasn't set 1560 return AudioSystem.BAD_VALUE; 1561 } 1562 List<AudioDeviceAttributes> roleDevices = rolesMap.get(key); 1563 List<AudioDeviceAttributes> appliedDevices = new ArrayList<>(); 1564 for (AudioDeviceAttributes device : devices) { 1565 if (roleDevices.contains(device)) { 1566 appliedDevices.add(device); 1567 } 1568 } 1569 if (appliedDevices.isEmpty()) { 1570 return AudioSystem.SUCCESS; 1571 } 1572 final int status = asi.deviceRoleAction(useCase, role, appliedDevices); 1573 if (status == AudioSystem.SUCCESS) { 1574 roleDevices.removeAll(appliedDevices); 1575 if (roleDevices.isEmpty()) { 1576 rolesMap.remove(key); 1577 } else { 1578 rolesMap.put(key, roleDevices); 1579 } 1580 } 1581 return status; 1582 } 1583 } 1584 1585 private static boolean devicesListEqual(@NonNull List<AudioDeviceAttributes> list1, 1586 @NonNull List<AudioDeviceAttributes> list2) { 1587 if (list1.size() != list2.size()) { 1588 return false; 1589 } 1590 // This assumes a given device is only present once in a list 1591 for (AudioDeviceAttributes d1 : list1) { 1592 boolean found = false; 1593 for (AudioDeviceAttributes d2 : list2) { 1594 if (d1.equalTypeAddress(d2)) { 1595 found = true; 1596 break; 1597 } 1598 } 1599 if (!found) { 1600 return false; 1601 } 1602 } 1603 return true; 1604 } 1605 1606 private int setDevicesRole( 1607 ArrayMap<Pair<Integer, Integer>, List<AudioDeviceAttributes>> rolesMap, 1608 AudioSystemInterface addOp, 1609 AudioSystemInterface clearOp, 1610 int useCase, int role, @NonNull List<AudioDeviceAttributes> devices) { 1611 synchronized (rolesMap) { 1612 Pair<Integer, Integer> key = new Pair<>(useCase, role); 1613 if (rolesMap.containsKey(key)) { 1614 if (devicesListEqual(devices, rolesMap.get(key))) { 1615 // NO OP: no change in preference 1616 return AudioSystem.SUCCESS; 1617 } 1618 } else if (devices.isEmpty()) { 1619 // NO OP: no preference to no preference 1620 return AudioSystem.SUCCESS; 1621 } 1622 int status; 1623 if (devices.isEmpty()) { 1624 status = clearOp.deviceRoleAction(useCase, role, null); 1625 if (status == AudioSystem.SUCCESS) { 1626 rolesMap.remove(key); 1627 } 1628 } else { 1629 status = addOp.deviceRoleAction(useCase, role, devices); 1630 if (status == AudioSystem.SUCCESS) { 1631 rolesMap.put(key, new ArrayList(devices)); 1632 } 1633 } 1634 return status; 1635 } 1636 } 1637 1638 private int clearDevicesRole( 1639 ArrayMap<Pair<Integer, Integer>, List<AudioDeviceAttributes>> rolesMap, 1640 AudioSystemInterface asi, int useCase, int role) { 1641 synchronized (rolesMap) { 1642 Pair<Integer, Integer> key = new Pair<>(useCase, role); 1643 if (!rolesMap.containsKey(key)) { 1644 // trying to clear a role for a device that wasn't set 1645 return AudioSystem.BAD_VALUE; 1646 } 1647 final int status = asi.deviceRoleAction(useCase, role, null); 1648 if (status == AudioSystem.SUCCESS) { 1649 rolesMap.remove(key); 1650 } 1651 return status; 1652 } 1653 } 1654 1655 @GuardedBy("mDevicesLock") 1656 private void purgeDevicesRoles_l() { 1657 purgeRoles(mAppliedStrategyRolesInt, (s, r, d) -> { 1658 return mAudioSystem.removeDevicesRoleForStrategy(s, r, d); }); 1659 purgeRoles(mAppliedPresetRolesInt, (p, r, d) -> { 1660 return mAudioSystem.removeDevicesRoleForCapturePreset(p, r, d); }); 1661 reapplyExternalDevicesRoles(); 1662 } 1663 1664 @GuardedBy("mDevicesLock") 1665 private void purgeRoles( 1666 ArrayMap<Pair<Integer, Integer>, List<AudioDeviceAttributes>> rolesMap, 1667 AudioSystemInterface asi) { 1668 synchronized (rolesMap) { 1669 AudioDeviceInfo[] connectedDevices = AudioManager.getDevicesStatic( 1670 AudioManager.GET_DEVICES_ALL); 1671 1672 Iterator<Entry<Pair<Integer, Integer>, List<AudioDeviceAttributes>>> itRole = 1673 rolesMap.entrySet().iterator(); 1674 1675 while (itRole.hasNext()) { 1676 Entry<Pair<Integer, Integer>, List<AudioDeviceAttributes>> entry = 1677 itRole.next(); 1678 Pair<Integer, Integer> keyRole = entry.getKey(); 1679 Iterator<AudioDeviceAttributes> itDev = rolesMap.get(keyRole).iterator(); 1680 while (itDev.hasNext()) { 1681 AudioDeviceAttributes ada = itDev.next(); 1682 1683 AudioDeviceInfo device = Stream.of(connectedDevices) 1684 .filter(d -> d.getInternalType() == ada.getInternalType()) 1685 .filter(d -> (!isBluetoothDevice(d.getInternalType()) 1686 || (d.getAddress().equals(ada.getAddress())))) 1687 .findFirst() 1688 .orElse(null); 1689 1690 if (device == null) { 1691 if (AudioService.DEBUG_DEVICES) { 1692 Slog.i(TAG, "purgeRoles() removing device: " + ada.toString() 1693 + ", for strategy: " + keyRole.first 1694 + " and role: " + keyRole.second); 1695 } 1696 asi.deviceRoleAction(keyRole.first, keyRole.second, Arrays.asList(ada)); 1697 itDev.remove(); 1698 } 1699 } 1700 if (rolesMap.get(keyRole).isEmpty()) { 1701 itRole.remove(); 1702 } 1703 } 1704 } 1705 } 1706 1707 //----------------------------------------------------------------------- 1708 1709 /** 1710 * Check if a device is in the list of connected devices 1711 * @param device the device whose connection state is queried 1712 * @return true if connected 1713 */ 1714 @GuardedBy("mDeviceBroker.mDeviceStateLock") 1715 public boolean isDeviceConnected(@NonNull AudioDeviceAttributes device) { 1716 final String key = DeviceInfo.makeDeviceListKey(device.getInternalType(), 1717 device.getAddress()); 1718 synchronized (mDevicesLock) { 1719 return (mConnectedDevices.get(key) != null); 1720 } 1721 } 1722 1723 /** 1724 * Implements the communication with AudioSystem to (dis)connect a device in the native layers 1725 * @param attributes the attributes of the device 1726 * @param connect true if connection 1727 * @param isForTesting if true, not calling AudioSystem for the connection as this is 1728 * just for testing 1729 * @param btDevice the corresponding Bluetooth device when relevant. 1730 * @return false if an error was reported by AudioSystem 1731 */ 1732 /*package*/ boolean handleDeviceConnection(@NonNull AudioDeviceAttributes attributes, 1733 boolean connect, boolean isForTesting, 1734 @Nullable BluetoothDevice btDevice) { 1735 int device = attributes.getInternalType(); 1736 String address = attributes.getAddress(); 1737 String deviceName = attributes.getName(); 1738 if (AudioService.DEBUG_DEVICES) { 1739 Slog.i(TAG, "handleDeviceConnection(" + connect + " dev:" 1740 + Integer.toHexString(device) + " address:" + address 1741 + " name:" + deviceName + ")"); 1742 } 1743 MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId + "handleDeviceConnection") 1744 .set(MediaMetrics.Property.ADDRESS, address) 1745 .set(MediaMetrics.Property.DEVICE, AudioSystem.getDeviceName(device)) 1746 .set(MediaMetrics.Property.MODE, connect 1747 ? MediaMetrics.Value.CONNECT : MediaMetrics.Value.DISCONNECT) 1748 .set(MediaMetrics.Property.NAME, deviceName); 1749 boolean status = false; 1750 synchronized (mDevicesLock) { 1751 final String deviceKey = DeviceInfo.makeDeviceListKey(device, address); 1752 if (AudioService.DEBUG_DEVICES) { 1753 Slog.i(TAG, "deviceKey:" + deviceKey); 1754 } 1755 DeviceInfo di = mConnectedDevices.get(deviceKey); 1756 boolean isConnected = di != null; 1757 if (AudioService.DEBUG_DEVICES) { 1758 Slog.i(TAG, "deviceInfo:" + di + " is(already)Connected:" + isConnected); 1759 } 1760 if (connect && !isConnected) { 1761 final int res; 1762 if (isForTesting) { 1763 res = AudioSystem.AUDIO_STATUS_OK; 1764 } else { 1765 res = mAudioSystem.setDeviceConnectionState(attributes, 1766 AudioSystem.DEVICE_STATE_AVAILABLE, AudioSystem.AUDIO_FORMAT_DEFAULT); 1767 } 1768 if (res != AudioSystem.AUDIO_STATUS_OK) { 1769 final String reason = "not connecting device 0x" + Integer.toHexString(device) 1770 + " due to command error " + res; 1771 mmi.set(MediaMetrics.Property.EARLY_RETURN, reason) 1772 .set(MediaMetrics.Property.STATE, MediaMetrics.Value.DISCONNECTED) 1773 .record(); 1774 AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent( 1775 "APM failed to make available device 0x" + Integer.toHexString(device) 1776 + "addr=" + address + " error=" + res) 1777 .printSlog(EventLogger.Event.ALOGE, TAG)); 1778 return false; 1779 } 1780 mConnectedDevices.put(deviceKey, new DeviceInfo(device, deviceName, address)); 1781 mDeviceBroker.postAccessoryPlugMediaUnmute(device); 1782 status = true; 1783 } else if (!connect && isConnected) { 1784 mAudioSystem.setDeviceConnectionState(attributes, 1785 AudioSystem.DEVICE_STATE_UNAVAILABLE, AudioSystem.AUDIO_FORMAT_DEFAULT); 1786 // always remove even if disconnection failed 1787 mConnectedDevices.remove(deviceKey); 1788 mDeviceBroker.postCheckCommunicationDeviceRemoval(attributes); 1789 status = true; 1790 } 1791 if (status) { 1792 if (AudioSystem.isBluetoothScoDevice(device)) { 1793 updateBluetoothPreferredModes_l(connect ? btDevice : null /*connectedDevice*/); 1794 if (!connect) { 1795 purgeDevicesRoles_l(); 1796 } else { 1797 addAudioDeviceInInventoryIfNeeded(device, address, "", 1798 BtHelper.getBtDeviceCategory(address), /*userDefined=*/false); 1799 } 1800 AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent( 1801 "SCO " + (AudioSystem.isInputDevice(device) ? "source" : "sink") 1802 + " device addr=" + address 1803 + (connect ? " now available" : " made unavailable")) 1804 .printSlog(EventLogger.Event.ALOGI, TAG)); 1805 } 1806 mmi.set(MediaMetrics.Property.STATE, MediaMetrics.Value.CONNECTED).record(); 1807 } else { 1808 Log.w(TAG, "handleDeviceConnection() failed, deviceKey=" + deviceKey 1809 + ", deviceSpec=" + di + ", connect=" + connect); 1810 mmi.set(MediaMetrics.Property.STATE, MediaMetrics.Value.DISCONNECTED).record(); 1811 } 1812 } 1813 return status; 1814 } 1815 1816 1817 private void disconnectA2dp() { 1818 synchronized (mDevicesLock) { 1819 final ArraySet<String> toRemove = new ArraySet<>(); 1820 // Disconnect ALL DEVICE_OUT_BLUETOOTH_A2DP devices 1821 mConnectedDevices.values().forEach(deviceInfo -> { 1822 if (deviceInfo.mDeviceType == AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) { 1823 toRemove.add(deviceInfo.mDeviceAddress); 1824 } 1825 }); 1826 new MediaMetrics.Item(mMetricsId + "disconnectA2dp") 1827 .set(MediaMetrics.Property.EVENT, "disconnectA2dp") 1828 .record(); 1829 if (toRemove.size() > 0) { 1830 final int delay = checkSendBecomingNoisyIntentInt( 1831 AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, 1832 AudioService.CONNECTION_STATE_DISCONNECTED, AudioSystem.DEVICE_NONE); 1833 toRemove.stream().forEach(deviceAddress -> 1834 makeA2dpDeviceUnavailableLater(deviceAddress, delay) 1835 ); 1836 } 1837 } 1838 } 1839 1840 private void disconnectA2dpSink() { 1841 synchronized (mDevicesLock) { 1842 final ArraySet<String> toRemove = new ArraySet<>(); 1843 // Disconnect ALL DEVICE_IN_BLUETOOTH_A2DP devices 1844 mConnectedDevices.values().forEach(deviceInfo -> { 1845 if (deviceInfo.mDeviceType == AudioSystem.DEVICE_IN_BLUETOOTH_A2DP) { 1846 toRemove.add(deviceInfo.mDeviceAddress); 1847 } 1848 }); 1849 new MediaMetrics.Item(mMetricsId + "disconnectA2dpSink") 1850 .set(MediaMetrics.Property.EVENT, "disconnectA2dpSink") 1851 .record(); 1852 toRemove.stream().forEach(deviceAddress -> makeA2dpSrcUnavailable(deviceAddress)); 1853 } 1854 } 1855 1856 private void disconnectHearingAid() { 1857 synchronized (mDevicesLock) { 1858 final ArraySet<String> toRemove = new ArraySet<>(); 1859 // Disconnect ALL DEVICE_OUT_HEARING_AID devices 1860 mConnectedDevices.values().forEach(deviceInfo -> { 1861 if (deviceInfo.mDeviceType == DEVICE_OUT_HEARING_AID) { 1862 toRemove.add(deviceInfo.mDeviceAddress); 1863 } 1864 }); 1865 new MediaMetrics.Item(mMetricsId + "disconnectHearingAid") 1866 .set(MediaMetrics.Property.EVENT, "disconnectHearingAid") 1867 .record(); 1868 if (toRemove.size() > 0) { 1869 /*final int delay = */ 1870 checkSendBecomingNoisyIntentInt(DEVICE_OUT_HEARING_AID, 1871 AudioService.CONNECTION_STATE_DISCONNECTED, AudioSystem.DEVICE_NONE); 1872 toRemove.stream().forEach(deviceAddress -> 1873 // TODO delay not used? 1874 makeHearingAidDeviceUnavailable(deviceAddress /*, delay*/) 1875 ); 1876 } 1877 } 1878 } 1879 1880 @GuardedBy("mDeviceBroker.mDeviceStateLock") 1881 /*package*/ void onBtProfileDisconnected(int profile) { 1882 switch (profile) { 1883 case BluetoothProfile.HEADSET: 1884 disconnectHeadset(); 1885 break; 1886 case BluetoothProfile.A2DP: 1887 disconnectA2dp(); 1888 break; 1889 case BluetoothProfile.A2DP_SINK: 1890 disconnectA2dpSink(); 1891 break; 1892 case BluetoothProfile.HEARING_AID: 1893 disconnectHearingAid(); 1894 break; 1895 case BluetoothProfile.LE_AUDIO: 1896 disconnectLeAudioUnicast(); 1897 break; 1898 case BluetoothProfile.LE_AUDIO_BROADCAST: 1899 disconnectLeAudioBroadcast(); 1900 break; 1901 default: 1902 // Not a valid profile to disconnect 1903 Log.e(TAG, "onBtProfileDisconnected: Not a valid profile to disconnect " 1904 + BluetoothProfile.getProfileName(profile)); 1905 break; 1906 } 1907 } 1908 1909 /*package*/ void disconnectLeAudio(int device) { 1910 if (device != AudioSystem.DEVICE_OUT_BLE_HEADSET 1911 && device != AudioSystem.DEVICE_OUT_BLE_BROADCAST) { 1912 Log.e(TAG, "disconnectLeAudio: Can't disconnect not LE Audio device " + device); 1913 return; 1914 } 1915 1916 synchronized (mDevicesLock) { 1917 final ArraySet<Pair<String, Integer>> toRemove = new ArraySet<>(); 1918 // Disconnect ALL DEVICE_OUT_BLE_HEADSET or DEVICE_OUT_BLE_BROADCAST devices 1919 mConnectedDevices.values().forEach(deviceInfo -> { 1920 if (deviceInfo.mDeviceType == device) { 1921 toRemove.add( 1922 new Pair<>(deviceInfo.mDeviceAddress, deviceInfo.mDeviceCodecFormat)); 1923 } 1924 }); 1925 new MediaMetrics.Item(mMetricsId + "disconnectLeAudio") 1926 .set(MediaMetrics.Property.EVENT, "disconnectLeAudio") 1927 .record(); 1928 if (toRemove.size() > 0) { 1929 final int delay = checkSendBecomingNoisyIntentInt(device, 1930 AudioService.CONNECTION_STATE_DISCONNECTED, 1931 AudioSystem.DEVICE_NONE); 1932 toRemove.stream().forEach(entry -> 1933 makeLeAudioDeviceUnavailableLater(entry.first, device, entry.second, delay) 1934 ); 1935 } 1936 } 1937 } 1938 1939 /*package*/ void disconnectLeAudioUnicast() { 1940 disconnectLeAudio(AudioSystem.DEVICE_OUT_BLE_HEADSET); 1941 } 1942 1943 /*package*/ void disconnectLeAudioBroadcast() { 1944 disconnectLeAudio(AudioSystem.DEVICE_OUT_BLE_BROADCAST); 1945 } 1946 1947 @GuardedBy("mDeviceBroker.mDeviceStateLock") 1948 private void disconnectHeadset() { 1949 boolean disconnect = false; 1950 synchronized (mDevicesLock) { 1951 for (DeviceInfo di : mConnectedDevices.values()) { 1952 if (AudioSystem.isBluetoothScoDevice(di.mDeviceType)) { 1953 // There is only one HFP active device and setting the active 1954 // device to null will disconnect both in and out devices 1955 disconnect = true; 1956 break; 1957 } 1958 } 1959 } 1960 if (disconnect) { 1961 mDeviceBroker.onSetBtScoActiveDevice(null); 1962 } 1963 } 1964 1965 // must be called before removing the device from mConnectedDevices 1966 // musicDevice argument is used when not AudioSystem.DEVICE_NONE instead of querying 1967 // from AudioSystem 1968 /*package*/ int checkSendBecomingNoisyIntent(int device, 1969 @AudioService.ConnectionState int state, int musicDevice) { 1970 synchronized (mDevicesLock) { 1971 return checkSendBecomingNoisyIntentInt(device, state, musicDevice); 1972 } 1973 } 1974 1975 /*package*/ AudioRoutesInfo startWatchingRoutes(IAudioRoutesObserver observer) { 1976 synchronized (mCurAudioRoutes) { 1977 AudioRoutesInfo routes = new AudioRoutesInfo(mCurAudioRoutes); 1978 mRoutesObservers.register(observer); 1979 return routes; 1980 } 1981 } 1982 1983 /*package*/ AudioRoutesInfo getCurAudioRoutes() { 1984 return mCurAudioRoutes; 1985 } 1986 1987 /** 1988 * Set a Bluetooth device to active. 1989 */ 1990 @GuardedBy("mDeviceBroker.mDeviceStateLock") 1991 public int setBluetoothActiveDevice(@NonNull AudioDeviceBroker.BtDeviceInfo info) { 1992 int delay; 1993 synchronized (mDevicesLock) { 1994 if (!info.mSupprNoisy 1995 && (((info.mProfile == BluetoothProfile.LE_AUDIO 1996 || info.mProfile == BluetoothProfile.LE_AUDIO_BROADCAST) 1997 && info.mIsLeOutput) 1998 || info.mProfile == BluetoothProfile.HEARING_AID 1999 || info.mProfile == BluetoothProfile.A2DP)) { 2000 @AudioService.ConnectionState int asState = 2001 (info.mState == BluetoothProfile.STATE_CONNECTED) 2002 ? AudioService.CONNECTION_STATE_CONNECTED 2003 : AudioService.CONNECTION_STATE_DISCONNECTED; 2004 delay = checkSendBecomingNoisyIntentInt(info.mAudioSystemDevice, asState, 2005 info.mMusicDevice); 2006 } else { 2007 delay = 0; 2008 } 2009 2010 if (AudioService.DEBUG_DEVICES) { 2011 Log.i(TAG, "setBluetoothActiveDevice " + info.toString() + " delay(ms): " + delay); 2012 } 2013 mDeviceBroker.postBluetoothActiveDevice(info, delay); 2014 } 2015 return delay; 2016 } 2017 2018 /*package*/ int setWiredDeviceConnectionState(AudioDeviceAttributes attributes, 2019 @AudioService.ConnectionState int state, String caller) { 2020 synchronized (mDevicesLock) { 2021 int delay = checkSendBecomingNoisyIntentInt( 2022 attributes.getInternalType(), state, AudioSystem.DEVICE_NONE); 2023 mDeviceBroker.postSetWiredDeviceConnectionState( 2024 new WiredDeviceConnectionState(attributes, state, caller), delay); 2025 return delay; 2026 } 2027 } 2028 2029 /*package*/ void setTestDeviceConnectionState(@NonNull AudioDeviceAttributes device, 2030 @AudioService.ConnectionState int state) { 2031 final WiredDeviceConnectionState connection = new WiredDeviceConnectionState( 2032 device, state, "com.android.server.audio"); 2033 connection.mForTest = true; 2034 onSetWiredDeviceConnectionState(connection); 2035 } 2036 2037 //------------------------------------------------------------------- 2038 // Internal utilities 2039 2040 @GuardedBy("mDevicesLock") 2041 private void makeA2dpDeviceAvailable(AudioDeviceBroker.BtDeviceInfo btInfo, 2042 @AudioSystem.AudioFormatNativeEnumForBtCodec int codec, 2043 String eventSource) { 2044 final String address = btInfo.mDevice.getAddress(); 2045 final String name = BtHelper.getName(btInfo.mDevice); 2046 2047 // enable A2DP before notifying A2DP connection to avoid unnecessary processing in 2048 // audio policy manager 2049 mDeviceBroker.setBluetoothA2dpOnInt(true, true /*fromA2dp*/, eventSource); 2050 // at this point there could be another A2DP device already connected in APM, but it 2051 // doesn't matter as this new one will overwrite the previous one 2052 AudioDeviceAttributes ada = new AudioDeviceAttributes( 2053 AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address, name); 2054 final int res = mAudioSystem.setDeviceConnectionState(ada, 2055 AudioSystem.DEVICE_STATE_AVAILABLE, codec); 2056 2057 // TODO: log in MediaMetrics once distinction between connection failure and 2058 // double connection is made. 2059 if (res != AudioSystem.AUDIO_STATUS_OK) { 2060 AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent( 2061 "APM failed to make available A2DP device addr=" 2062 + Utils.anonymizeBluetoothAddress(address) 2063 + " error=" + res).printSlog(EventLogger.Event.ALOGE, TAG)); 2064 // TODO: connection failed, stop here 2065 // TODO: return; 2066 } else { 2067 AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent( 2068 "A2DP sink device addr=" + Utils.anonymizeBluetoothAddress(address) 2069 + " now available").printSlog(EventLogger.Event.ALOGI, TAG)); 2070 } 2071 2072 // Reset A2DP suspend state each time a new sink is connected 2073 mDeviceBroker.clearA2dpSuspended(true /* internalOnly */); 2074 2075 final DeviceInfo di = new DeviceInfo(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, name, 2076 address, btInfo.mDevice.getIdentityAddress(), codec); 2077 final String diKey = di.getKey(); 2078 mConnectedDevices.put(diKey, di); 2079 // on a connection always overwrite the device seen by AudioPolicy, see comment above when 2080 // calling AudioSystem 2081 mApmConnectedDevices.put(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, diKey); 2082 2083 mDeviceBroker.postAccessoryPlugMediaUnmute(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP); 2084 setCurrentAudioRouteNameIfPossible(name, true /*fromA2dp*/); 2085 2086 updateBluetoothPreferredModes_l(btInfo.mDevice /*connectedDevice*/); 2087 2088 addAudioDeviceInInventoryIfNeeded(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address, "", 2089 BtHelper.getBtDeviceCategory(address), /*userDefined=*/false); 2090 } 2091 2092 static final int[] CAPTURE_PRESETS = new int[] {AudioSource.MIC, AudioSource.CAMCORDER, 2093 AudioSource.VOICE_RECOGNITION, AudioSource.VOICE_COMMUNICATION, 2094 AudioSource.UNPROCESSED, AudioSource.VOICE_PERFORMANCE, AudioSource.HOTWORD}; 2095 2096 // reflects system property persist.bluetooth.enable_dual_mode_audio 2097 final boolean mBluetoothDualModeEnabled; 2098 /** 2099 * Goes over all connected Bluetooth devices and set the audio policy device role to DISABLED 2100 * or not according to their own and other devices modes. 2101 * The top priority is given to LE devices, then SCO ,then A2DP. 2102 */ 2103 @GuardedBy("mDevicesLock") 2104 private void applyConnectedDevicesRoles_l() { 2105 if (!mBluetoothDualModeEnabled) { 2106 return; 2107 } 2108 DeviceInfo leOutDevice = 2109 getFirstConnectedDeviceOfTypes(DEVICE_OUT_ALL_BLE_SET); 2110 DeviceInfo leInDevice = 2111 getFirstConnectedDeviceOfTypes(AudioSystem.DEVICE_IN_ALL_BLE_SET); 2112 DeviceInfo a2dpDevice = 2113 getFirstConnectedDeviceOfTypes(DEVICE_OUT_ALL_A2DP_SET); 2114 DeviceInfo scoOutDevice = 2115 getFirstConnectedDeviceOfTypes(DEVICE_OUT_ALL_SCO_SET); 2116 DeviceInfo scoInDevice = 2117 getFirstConnectedDeviceOfTypes(DEVICE_IN_ALL_SCO_SET); 2118 boolean disableA2dp = (leOutDevice != null && leOutDevice.isOutputOnlyModeEnabled()); 2119 boolean disableSco = (leOutDevice != null && leOutDevice.isDuplexModeEnabled()) 2120 || (leInDevice != null && leInDevice.isDuplexModeEnabled()); 2121 AudioDeviceAttributes communicationDevice = 2122 mDeviceBroker.mActiveCommunicationDevice == null 2123 ? null : ((mDeviceBroker.isInCommunication() 2124 && mDeviceBroker.mActiveCommunicationDevice != null) 2125 ? new AudioDeviceAttributes(mDeviceBroker.mActiveCommunicationDevice) 2126 : null); 2127 2128 if (AudioService.DEBUG_DEVICES) { 2129 Log.i(TAG, "applyConnectedDevicesRoles_l\n - leOutDevice: " + leOutDevice 2130 + "\n - leInDevice: " + leInDevice 2131 + "\n - a2dpDevice: " + a2dpDevice 2132 + "\n - scoOutDevice: " + scoOutDevice 2133 + "\n - scoInDevice: " + scoInDevice 2134 + "\n - disableA2dp: " + disableA2dp 2135 + ", disableSco: " + disableSco); 2136 } 2137 2138 for (DeviceInfo di : mConnectedDevices.values()) { 2139 if (!isBluetoothDevice(di.mDeviceType)) { 2140 continue; 2141 } 2142 AudioDeviceAttributes ada = 2143 new AudioDeviceAttributes(di.mDeviceType, di.mDeviceAddress, di.mDeviceName); 2144 if (AudioService.DEBUG_DEVICES) { 2145 Log.i(TAG, " + checking Device: " + ada); 2146 } 2147 if (ada.equalTypeAddress(communicationDevice)) { 2148 continue; 2149 } 2150 2151 if (isBluetoothOutDevice(di.mDeviceType)) { 2152 for (AudioProductStrategy strategy : mStrategies) { 2153 boolean disable = false; 2154 if (strategy.getId() == mDeviceBroker.mCommunicationStrategyId) { 2155 if (AudioSystem.isBluetoothScoDevice(di.mDeviceType)) { 2156 disable = disableSco || !di.isDuplexModeEnabled(); 2157 } else if (AudioSystem.isBluetoothLeDevice(di.mDeviceType)) { 2158 disable = !di.isDuplexModeEnabled(); 2159 } 2160 } else { 2161 if (AudioSystem.isBluetoothA2dpOutDevice(di.mDeviceType)) { 2162 disable = disableA2dp || !di.isOutputOnlyModeEnabled(); 2163 } else if (AudioSystem.isBluetoothScoDevice(di.mDeviceType)) { 2164 disable = disableSco || !di.isOutputOnlyModeEnabled(); 2165 } else if (AudioSystem.isBluetoothLeDevice(di.mDeviceType)) { 2166 disable = !di.isOutputOnlyModeEnabled(); 2167 } 2168 } 2169 if (AudioService.DEBUG_DEVICES) { 2170 Log.i(TAG, " - strategy: " + strategy.getId() 2171 + ", disable: " + disable); 2172 } 2173 if (disable) { 2174 addDevicesRoleForStrategy(strategy.getId(), 2175 AudioSystem.DEVICE_ROLE_DISABLED, 2176 Arrays.asList(ada), true /* internal */); 2177 } else { 2178 removeDevicesRoleForStrategy(strategy.getId(), 2179 AudioSystem.DEVICE_ROLE_DISABLED, 2180 Arrays.asList(ada), true /* internal */); 2181 } 2182 } 2183 } 2184 if (AudioSystem.isBluetoothInDevice(di.mDeviceType)) { 2185 for (int capturePreset : CAPTURE_PRESETS) { 2186 boolean disable = false; 2187 if (AudioSystem.isBluetoothScoDevice(di.mDeviceType)) { 2188 disable = disableSco || !di.isDuplexModeEnabled(); 2189 } else if (AudioSystem.isBluetoothLeDevice(di.mDeviceType)) { 2190 disable = !di.isDuplexModeEnabled(); 2191 } 2192 if (AudioService.DEBUG_DEVICES) { 2193 Log.i(TAG, " - capturePreset: " + capturePreset 2194 + ", disable: " + disable); 2195 } 2196 if (disable) { 2197 addDevicesRoleForCapturePresetInt(capturePreset, 2198 AudioSystem.DEVICE_ROLE_DISABLED, Arrays.asList(ada)); 2199 } else { 2200 removeDevicesRoleForCapturePresetInt(capturePreset, 2201 AudioSystem.DEVICE_ROLE_DISABLED, Arrays.asList(ada)); 2202 } 2203 } 2204 } 2205 } 2206 } 2207 2208 /* package */ void applyConnectedDevicesRoles() { 2209 synchronized (mDevicesLock) { 2210 applyConnectedDevicesRoles_l(); 2211 } 2212 } 2213 2214 @GuardedBy("mDevicesLock") 2215 int checkProfileIsConnected(int profile) { 2216 switch (profile) { 2217 case BluetoothProfile.HEADSET: 2218 if (getFirstConnectedDeviceOfTypes(DEVICE_OUT_ALL_SCO_SET) != null 2219 || getFirstConnectedDeviceOfTypes(DEVICE_IN_ALL_SCO_SET) != null) { 2220 return profile; 2221 } 2222 break; 2223 case BluetoothProfile.A2DP: 2224 if (getFirstConnectedDeviceOfTypes(DEVICE_OUT_ALL_A2DP_SET) != null) { 2225 return profile; 2226 } 2227 break; 2228 case BluetoothProfile.LE_AUDIO: 2229 case BluetoothProfile.LE_AUDIO_BROADCAST: 2230 if (getFirstConnectedDeviceOfTypes( 2231 DEVICE_OUT_ALL_BLE_SET) != null 2232 || getFirstConnectedDeviceOfTypes( 2233 AudioSystem.DEVICE_IN_ALL_BLE_SET) != null) { 2234 return profile; 2235 } 2236 break; 2237 default: 2238 break; 2239 } 2240 return 0; 2241 } 2242 2243 @GuardedBy("mDevicesLock") 2244 private void updateBluetoothPreferredModes_l(BluetoothDevice connectedDevice) { 2245 if (!mBluetoothDualModeEnabled) { 2246 return; 2247 } 2248 HashSet<String> processedAddresses = new HashSet<>(0); 2249 for (DeviceInfo di : mConnectedDevices.values()) { 2250 if (!isBluetoothDevice(di.mDeviceType) 2251 || processedAddresses.contains(di.mDeviceAddress)) { 2252 continue; 2253 } 2254 Bundle preferredProfiles = BtHelper.getPreferredAudioProfiles(di.mDeviceAddress); 2255 if (AudioService.DEBUG_DEVICES) { 2256 Log.i(TAG, "updateBluetoothPreferredModes_l processing device address: " 2257 + di.mDeviceAddress + ", preferredProfiles: " + preferredProfiles); 2258 } 2259 for (DeviceInfo di2 : mConnectedDevices.values()) { 2260 if (!isBluetoothDevice(di2.mDeviceType) 2261 || !di.mDeviceAddress.equals(di2.mDeviceAddress)) { 2262 continue; 2263 } 2264 int profile = BtHelper.getProfileFromType(di2.mDeviceType); 2265 if (profile == 0) { 2266 continue; 2267 } 2268 int preferredProfile = checkProfileIsConnected( 2269 preferredProfiles.getInt(BluetoothAdapter.AUDIO_MODE_DUPLEX)); 2270 if (preferredProfile == profile || preferredProfile == 0) { 2271 di2.setModeEnabled(BluetoothAdapter.AUDIO_MODE_DUPLEX); 2272 } else { 2273 di2.setModeDisabled(BluetoothAdapter.AUDIO_MODE_DUPLEX); 2274 } 2275 preferredProfile = checkProfileIsConnected( 2276 preferredProfiles.getInt(BluetoothAdapter.AUDIO_MODE_OUTPUT_ONLY)); 2277 if (preferredProfile == profile || preferredProfile == 0) { 2278 di2.setModeEnabled(BluetoothAdapter.AUDIO_MODE_OUTPUT_ONLY); 2279 } else { 2280 di2.setModeDisabled(BluetoothAdapter.AUDIO_MODE_OUTPUT_ONLY); 2281 } 2282 } 2283 processedAddresses.add(di.mDeviceAddress); 2284 } 2285 applyConnectedDevicesRoles_l(); 2286 if (connectedDevice != null) { 2287 mDeviceBroker.postNotifyPreferredAudioProfileApplied(connectedDevice); 2288 } 2289 } 2290 2291 @GuardedBy("mDevicesLock") 2292 private void makeA2dpDeviceUnavailableNow(String address, int codec) { 2293 MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId + "a2dp." + address) 2294 .set(MediaMetrics.Property.ENCODING, AudioSystem.audioFormatToString(codec)) 2295 .set(MediaMetrics.Property.EVENT, "makeA2dpDeviceUnavailableNow"); 2296 2297 if (address == null) { 2298 mmi.set(MediaMetrics.Property.EARLY_RETURN, "address null").record(); 2299 return; 2300 } 2301 final String deviceToRemoveKey = 2302 DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address); 2303 2304 mConnectedDevices.remove(deviceToRemoveKey); 2305 if (!deviceToRemoveKey 2306 .equals(mApmConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP))) { 2307 // removing A2DP device not currently used by AudioPolicy, log but don't act on it 2308 AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent( 2309 "A2DP device " + Utils.anonymizeBluetoothAddress(address) 2310 + " made unavailable, was not used")) 2311 .printSlog(EventLogger.Event.ALOGI, TAG)); 2312 mmi.set(MediaMetrics.Property.EARLY_RETURN, 2313 "A2DP device made unavailable, was not used") 2314 .record(); 2315 return; 2316 } 2317 2318 // device to remove was visible by APM, update APM 2319 mDeviceBroker.clearAvrcpAbsoluteVolumeSupported(); 2320 AudioDeviceAttributes ada = new AudioDeviceAttributes( 2321 AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address); 2322 final int res = mAudioSystem.setDeviceConnectionState(ada, 2323 AudioSystem.DEVICE_STATE_UNAVAILABLE, codec); 2324 2325 if (res != AudioSystem.AUDIO_STATUS_OK) { 2326 AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent( 2327 "APM failed to make unavailable A2DP device addr=" 2328 + Utils.anonymizeBluetoothAddress(address) 2329 + " error=" + res).printSlog(EventLogger.Event.ALOGE, TAG)); 2330 // TODO: failed to disconnect, stop here 2331 // TODO: return; 2332 } else { 2333 AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent( 2334 "A2DP device addr=" + Utils.anonymizeBluetoothAddress(address) 2335 + " made unavailable")).printSlog(EventLogger.Event.ALOGI, TAG)); 2336 } 2337 mApmConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP); 2338 2339 // Remove A2DP routes as well 2340 setCurrentAudioRouteNameIfPossible(null, true /*fromA2dp*/); 2341 mmi.record(); 2342 updateBluetoothPreferredModes_l(null /*connectedDevice*/); 2343 purgeDevicesRoles_l(); 2344 mDeviceBroker.postCheckCommunicationDeviceRemoval(ada); 2345 } 2346 2347 @GuardedBy("mDevicesLock") 2348 private void makeA2dpDeviceUnavailableLater(String address, int delayMs) { 2349 // prevent any activity on the A2DP audio output to avoid unwanted 2350 // reconnection of the sink. 2351 mDeviceBroker.setA2dpSuspended( 2352 true /*enable*/, true /*internal*/, "makeA2dpDeviceUnavailableLater"); 2353 // retrieve DeviceInfo before removing device 2354 final String deviceKey = 2355 DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address); 2356 final DeviceInfo deviceInfo = mConnectedDevices.get(deviceKey); 2357 final int a2dpCodec = deviceInfo != null ? deviceInfo.mDeviceCodecFormat : 2358 AudioSystem.AUDIO_FORMAT_DEFAULT; 2359 // the device will be made unavailable later, so consider it disconnected right away 2360 mConnectedDevices.remove(deviceKey); 2361 // send the delayed message to make the device unavailable later 2362 mDeviceBroker.setA2dpTimeout(address, a2dpCodec, delayMs); 2363 } 2364 2365 2366 @GuardedBy("mDevicesLock") 2367 private void makeA2dpSrcAvailable(String address) { 2368 final int res = mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes( 2369 AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address), 2370 AudioSystem.DEVICE_STATE_AVAILABLE, 2371 AudioSystem.AUDIO_FORMAT_DEFAULT); 2372 if (res != AudioSystem.AUDIO_STATUS_OK) { 2373 AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent( 2374 "APM failed to make available A2DP source device addr=" 2375 + Utils.anonymizeBluetoothAddress(address) 2376 + " error=" + res).printSlog(EventLogger.Event.ALOGE, TAG)); 2377 // TODO: connection failed, stop here 2378 // TODO: return 2379 } else { 2380 AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent( 2381 "A2DP source device addr=" + Utils.anonymizeBluetoothAddress(address) 2382 + " now available").printSlog(EventLogger.Event.ALOGI, TAG)); 2383 } 2384 mConnectedDevices.put( 2385 DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address), 2386 new DeviceInfo(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, "", address)); 2387 } 2388 2389 @GuardedBy("mDevicesLock") 2390 private void makeA2dpSrcUnavailable(String address) { 2391 AudioDeviceAttributes ada = new AudioDeviceAttributes( 2392 AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address); 2393 mAudioSystem.setDeviceConnectionState(ada, 2394 AudioSystem.DEVICE_STATE_UNAVAILABLE, 2395 AudioSystem.AUDIO_FORMAT_DEFAULT); 2396 mConnectedDevices.remove( 2397 DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address)); 2398 mDeviceBroker.postCheckCommunicationDeviceRemoval(ada); 2399 } 2400 2401 @GuardedBy("mDevicesLock") 2402 private void makeHearingAidDeviceAvailable( 2403 String address, String name, int streamType, String eventSource) { 2404 final int hearingAidVolIndex = mDeviceBroker.getVssVolumeForDevice(streamType, 2405 DEVICE_OUT_HEARING_AID); 2406 mDeviceBroker.postSetHearingAidVolumeIndex(hearingAidVolIndex, streamType); 2407 2408 mDeviceBroker.setBluetoothA2dpOnInt(true, false /*fromA2dp*/, eventSource); 2409 2410 AudioDeviceAttributes ada = new AudioDeviceAttributes( 2411 DEVICE_OUT_HEARING_AID, address, name); 2412 mAudioSystem.setDeviceConnectionState(ada, 2413 AudioSystem.DEVICE_STATE_AVAILABLE, 2414 AudioSystem.AUDIO_FORMAT_DEFAULT); 2415 mConnectedDevices.put( 2416 DeviceInfo.makeDeviceListKey(DEVICE_OUT_HEARING_AID, address), 2417 new DeviceInfo(DEVICE_OUT_HEARING_AID, name, address)); 2418 mDeviceBroker.postAccessoryPlugMediaUnmute(DEVICE_OUT_HEARING_AID); 2419 mDeviceBroker.postApplyVolumeOnDevice(streamType, 2420 DEVICE_OUT_HEARING_AID, "makeHearingAidDeviceAvailable"); 2421 setCurrentAudioRouteNameIfPossible(name, false /*fromA2dp*/); 2422 addAudioDeviceInInventoryIfNeeded(DEVICE_OUT_HEARING_AID, address, "", 2423 BtHelper.getBtDeviceCategory(address), /*userDefined=*/false); 2424 new MediaMetrics.Item(mMetricsId + "makeHearingAidDeviceAvailable") 2425 .set(MediaMetrics.Property.ADDRESS, address != null ? address : "") 2426 .set(MediaMetrics.Property.DEVICE, 2427 AudioSystem.getDeviceName(DEVICE_OUT_HEARING_AID)) 2428 .set(MediaMetrics.Property.NAME, name) 2429 .set(MediaMetrics.Property.STREAM_TYPE, 2430 AudioSystem.streamToString(streamType)) 2431 .record(); 2432 } 2433 2434 @GuardedBy("mDevicesLock") 2435 private void makeHearingAidDeviceUnavailable(String address) { 2436 AudioDeviceAttributes ada = new AudioDeviceAttributes( 2437 DEVICE_OUT_HEARING_AID, address); 2438 mAudioSystem.setDeviceConnectionState(ada, 2439 AudioSystem.DEVICE_STATE_UNAVAILABLE, 2440 AudioSystem.AUDIO_FORMAT_DEFAULT); 2441 mConnectedDevices.remove( 2442 DeviceInfo.makeDeviceListKey(DEVICE_OUT_HEARING_AID, address)); 2443 // Remove Hearing Aid routes as well 2444 setCurrentAudioRouteNameIfPossible(null, false /*fromA2dp*/); 2445 new MediaMetrics.Item(mMetricsId + "makeHearingAidDeviceUnavailable") 2446 .set(MediaMetrics.Property.ADDRESS, address != null ? address : "") 2447 .set(MediaMetrics.Property.DEVICE, 2448 AudioSystem.getDeviceName(DEVICE_OUT_HEARING_AID)) 2449 .record(); 2450 mDeviceBroker.postCheckCommunicationDeviceRemoval(ada); 2451 } 2452 2453 /** 2454 * Returns whether a device of type DEVICE_OUT_HEARING_AID is connected. 2455 * Visibility by APM plays no role 2456 * @return true if a DEVICE_OUT_HEARING_AID is connected, false otherwise. 2457 */ 2458 boolean isHearingAidConnected() { 2459 return getFirstConnectedDeviceOfTypes( 2460 Sets.newHashSet(DEVICE_OUT_HEARING_AID)) != null; 2461 } 2462 2463 /** 2464 * Returns a DeviceInfo for the first connected device matching one of the supplied types 2465 */ 2466 private DeviceInfo getFirstConnectedDeviceOfTypes(Set<Integer> internalTypes) { 2467 List<DeviceInfo> devices = getConnectedDevicesOfTypes(internalTypes); 2468 return devices.isEmpty() ? null : devices.get(0); 2469 } 2470 2471 /** 2472 * Returns a list of connected devices matching one of the supplied types 2473 */ 2474 private List<DeviceInfo> getConnectedDevicesOfTypes(Set<Integer> internalTypes) { 2475 ArrayList<DeviceInfo> devices = new ArrayList<>(); 2476 synchronized (mDevicesLock) { 2477 for (DeviceInfo di : mConnectedDevices.values()) { 2478 if (internalTypes.contains(di.mDeviceType)) { 2479 devices.add(di); 2480 } 2481 } 2482 } 2483 return devices; 2484 } 2485 2486 /* package */ AudioDeviceAttributes getDeviceOfType(int type) { 2487 DeviceInfo di = getFirstConnectedDeviceOfTypes(Sets.newHashSet(type)); 2488 return di == null ? null : new AudioDeviceAttributes( 2489 di.mDeviceType, di.mDeviceAddress, di.mDeviceName); 2490 } 2491 2492 @GuardedBy("mDevicesLock") 2493 private void makeLeAudioDeviceAvailable( 2494 AudioDeviceBroker.BtDeviceInfo btInfo, int streamType, 2495 @AudioSystem.AudioFormatNativeEnumForBtCodec int codec, String eventSource) { 2496 final int volumeIndex = btInfo.mVolume == -1 ? -1 : btInfo.mVolume * 10; 2497 final int device = btInfo.mAudioSystemDevice; 2498 2499 if (device != AudioSystem.DEVICE_NONE) { 2500 final String address = btInfo.mDevice.getAddress(); 2501 String name = BtHelper.getName(btInfo.mDevice); 2502 2503 // Find LE Group ID and peer headset address if available 2504 final int groupId = mDeviceBroker.getLeAudioDeviceGroupId(btInfo.mDevice); 2505 String peerAddress = ""; 2506 String peerIdentityAddress = ""; 2507 if (groupId != BluetoothLeAudio.GROUP_ID_INVALID) { 2508 List<Pair<String, String>> addresses = 2509 mDeviceBroker.getLeAudioGroupAddresses(groupId); 2510 if (addresses.size() > 1) { 2511 for (Pair<String, String> addr : addresses) { 2512 if (!addr.first.equals(address)) { 2513 peerAddress = addr.first; 2514 peerIdentityAddress = addr.second; 2515 break; 2516 } 2517 } 2518 } 2519 } 2520 // The BT Stack does not provide a name for LE Broadcast devices 2521 if (device == AudioSystem.DEVICE_OUT_BLE_BROADCAST && name.equals("")) { 2522 name = "Broadcast"; 2523 } 2524 2525 /* Audio Policy sees Le Audio similar to A2DP. Let's make sure 2526 * AUDIO_POLICY_FORCE_NO_BT_A2DP is not set 2527 */ 2528 mDeviceBroker.setBluetoothA2dpOnInt(true, false /*fromA2dp*/, eventSource); 2529 2530 AudioDeviceAttributes ada = new AudioDeviceAttributes(device, address, name); 2531 final int res = AudioSystem.setDeviceConnectionState(ada, 2532 AudioSystem.DEVICE_STATE_AVAILABLE, codec); 2533 if (res != AudioSystem.AUDIO_STATUS_OK) { 2534 AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent( 2535 "APM failed to make available LE Audio device addr=" + address 2536 + " error=" + res).printSlog(EventLogger.Event.ALOGE, TAG)); 2537 // TODO: connection failed, stop here 2538 // TODO: return; 2539 } else { 2540 AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent( 2541 "LE Audio " + (AudioSystem.isInputDevice(device) ? "source" : "sink") 2542 + " device addr=" + Utils.anonymizeBluetoothAddress(address) 2543 + " now available").printSlog(EventLogger.Event.ALOGI, TAG)); 2544 } 2545 // Reset LEA suspend state each time a new sink is connected 2546 mDeviceBroker.clearLeAudioSuspended(true /* internalOnly */); 2547 mConnectedDevices.put(DeviceInfo.makeDeviceListKey(device, address), 2548 new DeviceInfo(device, name, address, 2549 btInfo.mDevice.getIdentityAddress(), codec, 2550 groupId, peerAddress, peerIdentityAddress)); 2551 mDeviceBroker.postAccessoryPlugMediaUnmute(device); 2552 setCurrentAudioRouteNameIfPossible(name, /*fromA2dp=*/false); 2553 addAudioDeviceInInventoryIfNeeded(device, address, peerAddress, 2554 BtHelper.getBtDeviceCategory(address), /*userDefined=*/false); 2555 } 2556 2557 if (streamType == AudioSystem.STREAM_DEFAULT) { 2558 // No need to update volume for input devices 2559 return; 2560 } 2561 2562 final int leAudioVolIndex = (volumeIndex == -1) 2563 ? mDeviceBroker.getVssVolumeForDevice(streamType, device) 2564 : volumeIndex; 2565 final int maxIndex = mDeviceBroker.getMaxVssVolumeForStream(streamType); 2566 mDeviceBroker.postSetLeAudioVolumeIndex(leAudioVolIndex, maxIndex, streamType); 2567 mDeviceBroker.postApplyVolumeOnDevice(streamType, device, "makeLeAudioDeviceAvailable"); 2568 2569 updateBluetoothPreferredModes_l(btInfo.mDevice /*connectedDevice*/); 2570 } 2571 2572 @GuardedBy("mDevicesLock") 2573 private void makeLeAudioDeviceUnavailableNow(String address, int device, 2574 @AudioSystem.AudioFormatNativeEnumForBtCodec int codec) { 2575 AudioDeviceAttributes ada = null; 2576 if (device != AudioSystem.DEVICE_NONE) { 2577 ada = new AudioDeviceAttributes(device, address); 2578 final int res = AudioSystem.setDeviceConnectionState(ada, 2579 AudioSystem.DEVICE_STATE_UNAVAILABLE, 2580 codec); 2581 2582 if (res != AudioSystem.AUDIO_STATUS_OK) { 2583 AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent( 2584 "APM failed to make unavailable LE Audio device addr=" + address 2585 + " error=" + res).printSlog(EventLogger.Event.ALOGE, TAG)); 2586 // TODO: failed to disconnect, stop here 2587 // TODO: return; 2588 } else { 2589 AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent( 2590 "LE Audio device addr=" + Utils.anonymizeBluetoothAddress(address) 2591 + " made unavailable").printSlog(EventLogger.Event.ALOGI, TAG)); 2592 } 2593 mConnectedDevices.remove(DeviceInfo.makeDeviceListKey(device, address)); 2594 } 2595 2596 setCurrentAudioRouteNameIfPossible(null, false /*fromA2dp*/); 2597 updateBluetoothPreferredModes_l(null /*connectedDevice*/); 2598 purgeDevicesRoles_l(); 2599 if (ada != null) { 2600 mDeviceBroker.postCheckCommunicationDeviceRemoval(ada); 2601 } 2602 } 2603 2604 @GuardedBy("mDevicesLock") 2605 private void makeLeAudioDeviceUnavailableLater( 2606 String address, int device, int codec, int delayMs) { 2607 // prevent any activity on the LEA output to avoid unwanted 2608 // reconnection of the sink. 2609 mDeviceBroker.setLeAudioSuspended( 2610 true /*enable*/, true /*internal*/, "makeLeAudioDeviceUnavailableLater"); 2611 // the device will be made unavailable later, so consider it disconnected right away 2612 mConnectedDevices.remove(DeviceInfo.makeDeviceListKey(device, address)); 2613 // send the delayed message to make the device unavailable later 2614 mDeviceBroker.setLeAudioTimeout(address, device, codec, delayMs); 2615 } 2616 2617 @GuardedBy("mDevicesLock") 2618 private void setCurrentAudioRouteNameIfPossible(String name, boolean fromA2dp) { 2619 synchronized (mCurAudioRoutes) { 2620 if (TextUtils.equals(mCurAudioRoutes.bluetoothName, name)) { 2621 return; 2622 } 2623 if (name != null || !isCurrentDeviceConnected()) { 2624 mCurAudioRoutes.bluetoothName = name; 2625 mDeviceBroker.postReportNewRoutes(fromA2dp); 2626 } 2627 } 2628 } 2629 2630 @GuardedBy("mDevicesLock") 2631 private boolean isCurrentDeviceConnected() { 2632 return mConnectedDevices.values().stream().anyMatch(deviceInfo -> 2633 TextUtils.equals(deviceInfo.mDeviceName, mCurAudioRoutes.bluetoothName)); 2634 } 2635 2636 // Devices which removal triggers intent ACTION_AUDIO_BECOMING_NOISY. The intent is only 2637 // sent if: 2638 // - none of these devices are connected anymore after one is disconnected AND 2639 // - the device being disconnected is actually used for music. 2640 // Access synchronized on mConnectedDevices 2641 private static final Set<Integer> BECOMING_NOISY_INTENT_DEVICES_SET; 2642 static { 2643 BECOMING_NOISY_INTENT_DEVICES_SET = new HashSet<>(); 2644 BECOMING_NOISY_INTENT_DEVICES_SET.add(AudioSystem.DEVICE_OUT_WIRED_HEADSET); 2645 BECOMING_NOISY_INTENT_DEVICES_SET.add(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE); 2646 BECOMING_NOISY_INTENT_DEVICES_SET.add(AudioSystem.DEVICE_OUT_HDMI); 2647 BECOMING_NOISY_INTENT_DEVICES_SET.add(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET); 2648 BECOMING_NOISY_INTENT_DEVICES_SET.add(AudioSystem.DEVICE_OUT_LINE); 2649 BECOMING_NOISY_INTENT_DEVICES_SET.add(DEVICE_OUT_HEARING_AID); 2650 BECOMING_NOISY_INTENT_DEVICES_SET.add(AudioSystem.DEVICE_OUT_BLE_HEADSET); 2651 BECOMING_NOISY_INTENT_DEVICES_SET.add(AudioSystem.DEVICE_OUT_BLE_BROADCAST); 2652 BECOMING_NOISY_INTENT_DEVICES_SET.addAll(DEVICE_OUT_ALL_A2DP_SET); 2653 BECOMING_NOISY_INTENT_DEVICES_SET.addAll(AudioSystem.DEVICE_OUT_ALL_USB_SET); 2654 BECOMING_NOISY_INTENT_DEVICES_SET.addAll(DEVICE_OUT_ALL_BLE_SET); 2655 } 2656 2657 // must be called before removing the device from mConnectedDevices 2658 // musicDevice argument is used when not AudioSystem.DEVICE_NONE instead of querying 2659 // from AudioSystem 2660 @GuardedBy("mDevicesLock") 2661 private int checkSendBecomingNoisyIntentInt(int device, 2662 @AudioService.ConnectionState int state, int musicDevice) { 2663 MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId 2664 + "checkSendBecomingNoisyIntentInt") 2665 .set(MediaMetrics.Property.DEVICE, AudioSystem.getDeviceName(device)) 2666 .set(MediaMetrics.Property.STATE, 2667 state == AudioService.CONNECTION_STATE_CONNECTED 2668 ? MediaMetrics.Value.CONNECTED : MediaMetrics.Value.DISCONNECTED); 2669 if (state != AudioService.CONNECTION_STATE_DISCONNECTED) { 2670 Log.i(TAG, "not sending NOISY: state=" + state); 2671 mmi.set(MediaMetrics.Property.DELAY_MS, 0).record(); // OK to return 2672 return 0; 2673 } 2674 if (!BECOMING_NOISY_INTENT_DEVICES_SET.contains(device)) { 2675 Log.i(TAG, "not sending NOISY: device=0x" + Integer.toHexString(device) 2676 + " not in set " + BECOMING_NOISY_INTENT_DEVICES_SET); 2677 mmi.set(MediaMetrics.Property.DELAY_MS, 0).record(); // OK to return 2678 return 0; 2679 } 2680 int delay = 0; 2681 Set<Integer> devices = new HashSet<>(); 2682 for (DeviceInfo di : mConnectedDevices.values()) { 2683 if (!AudioSystem.isInputDevice(di.mDeviceType) 2684 && BECOMING_NOISY_INTENT_DEVICES_SET.contains(di.mDeviceType)) { 2685 devices.add(di.mDeviceType); 2686 Log.i(TAG, "NOISY: adding 0x" + Integer.toHexString(di.mDeviceType)); 2687 } 2688 } 2689 if (musicDevice == AudioSystem.DEVICE_NONE) { 2690 musicDevice = mDeviceBroker.getDeviceForStream(AudioSystem.STREAM_MUSIC); 2691 Log.i(TAG, "NOISY: musicDevice changing from NONE to 0x" 2692 + Integer.toHexString(musicDevice)); 2693 } 2694 2695 // always ignore condition on device being actually used for music when in communication 2696 // because music routing is altered in this case. 2697 // also checks whether media routing if affected by a dynamic policy or mirroring 2698 final boolean inCommunication = mDeviceBroker.isInCommunication(); 2699 final boolean singleAudioDeviceType = AudioSystem.isSingleAudioDeviceType(devices, device); 2700 final boolean hasMediaDynamicPolicy = mDeviceBroker.hasMediaDynamicPolicy(); 2701 if (((device == musicDevice) || inCommunication) 2702 && singleAudioDeviceType 2703 && !hasMediaDynamicPolicy 2704 && (musicDevice != AudioSystem.DEVICE_OUT_REMOTE_SUBMIX)) { 2705 if (!mAudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0 /*not looking in past*/) 2706 && !mDeviceBroker.hasAudioFocusUsers()) { 2707 // no media playback, not a "becoming noisy" situation, otherwise it could cause 2708 // the pausing of some apps that are playing remotely 2709 AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent( 2710 "dropping ACTION_AUDIO_BECOMING_NOISY")).printLog(TAG)); 2711 mmi.set(MediaMetrics.Property.DELAY_MS, 0).record(); // OK to return 2712 return 0; 2713 } 2714 mDeviceBroker.postBroadcastBecomingNoisy(); 2715 delay = AudioService.BECOMING_NOISY_DELAY_MS; 2716 } else { 2717 Log.i(TAG, "not sending NOISY: device:0x" + Integer.toHexString(device) 2718 + " musicDevice:0x" + Integer.toHexString(musicDevice) 2719 + " inComm:" + inCommunication 2720 + " mediaPolicy:" + hasMediaDynamicPolicy 2721 + " singleDevice:" + singleAudioDeviceType); 2722 } 2723 2724 mmi.set(MediaMetrics.Property.DELAY_MS, delay).record(); 2725 return delay; 2726 } 2727 2728 // Intent "extra" data keys. 2729 private static final String CONNECT_INTENT_KEY_PORT_NAME = "portName"; 2730 private static final String CONNECT_INTENT_KEY_STATE = "state"; 2731 private static final String CONNECT_INTENT_KEY_ADDRESS = "address"; 2732 2733 private void sendDeviceConnectionIntent(int device, int state, String address, 2734 String deviceName) { 2735 if (AudioService.DEBUG_DEVICES) { 2736 Slog.i(TAG, "sendDeviceConnectionIntent(dev:0x" + Integer.toHexString(device) 2737 + " state:0x" + Integer.toHexString(state) + " address:" + address 2738 + " name:" + deviceName + ");"); 2739 } 2740 Intent intent = new Intent(); 2741 2742 switch(device) { 2743 case AudioSystem.DEVICE_OUT_WIRED_HEADSET: 2744 intent.setAction(Intent.ACTION_HEADSET_PLUG); 2745 intent.putExtra("microphone", 1); 2746 break; 2747 case AudioSystem.DEVICE_OUT_WIRED_HEADPHONE: 2748 case AudioSystem.DEVICE_OUT_LINE: 2749 intent.setAction(Intent.ACTION_HEADSET_PLUG); 2750 intent.putExtra("microphone", 0); 2751 break; 2752 case AudioSystem.DEVICE_OUT_USB_HEADSET: 2753 intent.setAction(Intent.ACTION_HEADSET_PLUG); 2754 intent.putExtra("microphone", 2755 AudioSystem.getDeviceConnectionState(AudioSystem.DEVICE_IN_USB_HEADSET, "") 2756 == AudioSystem.DEVICE_STATE_AVAILABLE ? 1 : 0); 2757 break; 2758 case AudioSystem.DEVICE_IN_USB_HEADSET: 2759 if (AudioSystem.getDeviceConnectionState(AudioSystem.DEVICE_OUT_USB_HEADSET, "") 2760 == AudioSystem.DEVICE_STATE_AVAILABLE) { 2761 intent.setAction(Intent.ACTION_HEADSET_PLUG); 2762 intent.putExtra("microphone", 1); 2763 } else { 2764 // do not send ACTION_HEADSET_PLUG when only the input side is seen as changing 2765 return; 2766 } 2767 break; 2768 case AudioSystem.DEVICE_OUT_HDMI: 2769 case AudioSystem.DEVICE_OUT_HDMI_ARC: 2770 case AudioSystem.DEVICE_OUT_HDMI_EARC: 2771 configureHdmiPlugIntent(intent, state); 2772 break; 2773 } 2774 2775 if (intent.getAction() == null) { 2776 return; 2777 } 2778 2779 intent.putExtra(CONNECT_INTENT_KEY_STATE, state); 2780 intent.putExtra(CONNECT_INTENT_KEY_ADDRESS, address); 2781 intent.putExtra(CONNECT_INTENT_KEY_PORT_NAME, deviceName); 2782 2783 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 2784 2785 final long ident = Binder.clearCallingIdentity(); 2786 try { 2787 mDeviceBroker.broadcastStickyIntentToCurrentProfileGroup(intent); 2788 } finally { 2789 Binder.restoreCallingIdentity(ident); 2790 } 2791 } 2792 2793 private void updateAudioRoutes(int device, int state) { 2794 int connType = 0; 2795 2796 switch (device) { 2797 case AudioSystem.DEVICE_OUT_WIRED_HEADSET: 2798 connType = AudioRoutesInfo.MAIN_HEADSET; 2799 break; 2800 case AudioSystem.DEVICE_OUT_WIRED_HEADPHONE: 2801 case AudioSystem.DEVICE_OUT_LINE: 2802 connType = AudioRoutesInfo.MAIN_HEADPHONES; 2803 break; 2804 case AudioSystem.DEVICE_OUT_HDMI: 2805 case AudioSystem.DEVICE_OUT_HDMI_ARC: 2806 case AudioSystem.DEVICE_OUT_HDMI_EARC: 2807 connType = AudioRoutesInfo.MAIN_HDMI; 2808 break; 2809 case AudioSystem.DEVICE_OUT_USB_DEVICE: 2810 case AudioSystem.DEVICE_OUT_USB_HEADSET: 2811 connType = AudioRoutesInfo.MAIN_USB; 2812 break; 2813 case AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET: 2814 connType = AudioRoutesInfo.MAIN_DOCK_SPEAKERS; 2815 break; 2816 } 2817 2818 synchronized (mCurAudioRoutes) { 2819 if (connType == 0) { 2820 return; 2821 } 2822 int newConn = mCurAudioRoutes.mainType; 2823 if (state != 0) { 2824 newConn |= connType; 2825 } else { 2826 newConn &= ~connType; 2827 } 2828 if (newConn != mCurAudioRoutes.mainType) { 2829 mCurAudioRoutes.mainType = newConn; 2830 mDeviceBroker.postReportNewRoutes(false /*fromA2dp*/); 2831 } 2832 } 2833 } 2834 2835 private void configureHdmiPlugIntent(Intent intent, @AudioService.ConnectionState int state) { 2836 intent.setAction(AudioManager.ACTION_HDMI_AUDIO_PLUG); 2837 intent.putExtra(AudioManager.EXTRA_AUDIO_PLUG_STATE, state); 2838 if (state != AudioService.CONNECTION_STATE_CONNECTED) { 2839 return; 2840 } 2841 ArrayList<AudioPort> ports = new ArrayList<AudioPort>(); 2842 int[] portGeneration = new int[1]; 2843 int status = AudioSystem.listAudioPorts(ports, portGeneration); 2844 if (status != AudioManager.SUCCESS) { 2845 Log.e(TAG, "listAudioPorts error " + status + " in configureHdmiPlugIntent"); 2846 return; 2847 } 2848 for (AudioPort port : ports) { 2849 if (!(port instanceof AudioDevicePort)) { 2850 continue; 2851 } 2852 final AudioDevicePort devicePort = (AudioDevicePort) port; 2853 if (devicePort.type() != AudioManager.DEVICE_OUT_HDMI 2854 && devicePort.type() != AudioManager.DEVICE_OUT_HDMI_ARC 2855 && devicePort.type() != AudioManager.DEVICE_OUT_HDMI_EARC) { 2856 continue; 2857 } 2858 // found an HDMI port: format the list of supported encodings 2859 int[] formats = AudioFormat.filterPublicFormats(devicePort.formats()); 2860 if (formats.length > 0) { 2861 ArrayList<Integer> encodingList = new ArrayList(1); 2862 for (int format : formats) { 2863 // a format in the list can be 0, skip it 2864 if (format != AudioFormat.ENCODING_INVALID) { 2865 encodingList.add(format); 2866 } 2867 } 2868 final int[] encodingArray = encodingList.stream().mapToInt(i -> i).toArray(); 2869 intent.putExtra(AudioManager.EXTRA_ENCODINGS, encodingArray); 2870 } 2871 // find the maximum supported number of channels 2872 int maxChannels = 0; 2873 for (int mask : devicePort.channelMasks()) { 2874 int channelCount = AudioFormat.channelCountFromOutChannelMask(mask); 2875 if (channelCount > maxChannels) { 2876 maxChannels = channelCount; 2877 } 2878 } 2879 intent.putExtra(AudioManager.EXTRA_MAX_CHANNEL_COUNT, maxChannels); 2880 } 2881 } 2882 2883 private void dispatchPreferredDevice(int strategy, 2884 @NonNull List<AudioDeviceAttributes> devices) { 2885 final int nbDispatchers = mPrefDevDispatchers.beginBroadcast(); 2886 for (int i = 0; i < nbDispatchers; i++) { 2887 try { 2888 if (!((Boolean) mPrefDevDispatchers.getBroadcastCookie(i))) { 2889 devices = mDeviceBroker.anonymizeAudioDeviceAttributesListUnchecked(devices); 2890 } 2891 mPrefDevDispatchers.getBroadcastItem(i).dispatchPrefDevicesChanged( 2892 strategy, devices); 2893 } catch (RemoteException e) { 2894 Log.e(TAG, "dispatchPreferredDevice ", e); 2895 } 2896 } 2897 mPrefDevDispatchers.finishBroadcast(); 2898 } 2899 2900 private void dispatchNonDefaultDevice(int strategy, 2901 @NonNull List<AudioDeviceAttributes> devices) { 2902 final int nbDispatchers = mNonDefDevDispatchers.beginBroadcast(); 2903 for (int i = 0; i < nbDispatchers; i++) { 2904 try { 2905 if (!((Boolean) mNonDefDevDispatchers.getBroadcastCookie(i))) { 2906 devices = mDeviceBroker.anonymizeAudioDeviceAttributesListUnchecked(devices); 2907 } 2908 mNonDefDevDispatchers.getBroadcastItem(i).dispatchNonDefDevicesChanged( 2909 strategy, devices); 2910 } catch (RemoteException e) { 2911 Log.e(TAG, "dispatchNonDefaultDevice ", e); 2912 } 2913 } 2914 mNonDefDevDispatchers.finishBroadcast(); 2915 } 2916 2917 private void dispatchDevicesRoleForCapturePreset( 2918 int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devices) { 2919 final int nbDispatchers = mDevRoleCapturePresetDispatchers.beginBroadcast(); 2920 for (int i = 0; i < nbDispatchers; ++i) { 2921 try { 2922 if (!((Boolean) mDevRoleCapturePresetDispatchers.getBroadcastCookie(i))) { 2923 devices = mDeviceBroker.anonymizeAudioDeviceAttributesListUnchecked(devices); 2924 } 2925 mDevRoleCapturePresetDispatchers.getBroadcastItem(i).dispatchDevicesRoleChanged( 2926 capturePreset, role, devices); 2927 } catch (RemoteException e) { 2928 Log.e(TAG, "dispatchDevicesRoleForCapturePreset ", e); 2929 } 2930 } 2931 mDevRoleCapturePresetDispatchers.finishBroadcast(); 2932 } 2933 2934 List<String> getDeviceIdentityAddresses(AudioDeviceAttributes device) { 2935 List<String> addresses = new ArrayList<String>(); 2936 final String key = DeviceInfo.makeDeviceListKey(device.getInternalType(), 2937 device.getAddress()); 2938 synchronized (mDevicesLock) { 2939 DeviceInfo di = mConnectedDevices.get(key); 2940 if (di != null) { 2941 if (!di.mDeviceIdentityAddress.isEmpty()) { 2942 addresses.add(di.mDeviceIdentityAddress); 2943 } 2944 if (!di.mPeerIdentityDeviceAddress.isEmpty() 2945 && !di.mPeerIdentityDeviceAddress.equals(di.mDeviceIdentityAddress)) { 2946 addresses.add(di.mPeerIdentityDeviceAddress); 2947 } 2948 } 2949 } 2950 return addresses; 2951 } 2952 2953 /*package*/ String getDeviceSettings() { 2954 int deviceCatalogSize = 0; 2955 synchronized (mDeviceInventoryLock) { 2956 deviceCatalogSize = mDeviceInventory.size(); 2957 2958 final StringBuilder settingsBuilder = new StringBuilder( 2959 deviceCatalogSize * AdiDeviceState.getPeristedMaxSize()); 2960 2961 Iterator<AdiDeviceState> iterator = mDeviceInventory.values().iterator(); 2962 if (iterator.hasNext()) { 2963 settingsBuilder.append(iterator.next().toPersistableString()); 2964 } 2965 while (iterator.hasNext()) { 2966 settingsBuilder.append(SETTING_DEVICE_SEPARATOR_CHAR); 2967 settingsBuilder.append(iterator.next().toPersistableString()); 2968 } 2969 return settingsBuilder.toString(); 2970 } 2971 } 2972 2973 /*package*/ void setDeviceSettings(String settings) { 2974 clearDeviceInventory(); 2975 String[] devSettings = TextUtils.split(Objects.requireNonNull(settings), 2976 SETTING_DEVICE_SEPARATOR); 2977 // small list, not worth overhead of Arrays.stream(devSettings) 2978 for (String setting : devSettings) { 2979 AdiDeviceState devState = AdiDeviceState.fromPersistedString(setting); 2980 // Note if the device is not compatible with spatialization mode or the device 2981 // type is not canonical, it will be ignored in {@link SpatializerHelper}. 2982 if (devState != null) { 2983 addOrUpdateDeviceSAStateInInventory(devState, false /*syncInventory*/); 2984 addOrUpdateAudioDeviceCategoryInInventory(devState, false /*syncInventory*/); 2985 } 2986 } 2987 } 2988 2989 //---------------------------------------------------------- 2990 // For tests only 2991 2992 /** 2993 * Check if device is in the list of connected devices 2994 * @param device the device to query 2995 * @return true if connected 2996 */ 2997 @VisibleForTesting 2998 public boolean isA2dpDeviceConnected(@NonNull BluetoothDevice device) { 2999 for (DeviceInfo di : getConnectedDevicesOfTypes( 3000 Sets.newHashSet(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP))) { 3001 if (di.mDeviceAddress.equals(device.getAddress())) { 3002 return true; 3003 } 3004 } 3005 return false; 3006 } 3007 } 3008