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