summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceBroker.java76
-rw-r--r--services/core/java/com/android/server/audio/AudioDeviceInventory.java239
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java14
-rw-r--r--services/core/java/com/android/server/audio/BtHelper.java60
-rw-r--r--services/core/java/com/android/server/audio/SpatializerHelper.java40
-rw-r--r--services/tests/servicestests/src/com/android/server/audio/SpatializerHelperTest.java2
6 files changed, 334 insertions, 97 deletions
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 28970750a5ac..5d4f711b9432 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -70,7 +70,6 @@ import java.util.List;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
-import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -172,7 +171,7 @@ public class AudioDeviceBroker {
@NonNull AudioSystemAdapter audioSystem) {
mContext = context;
mAudioService = service;
- mBtHelper = new BtHelper(this);
+ mBtHelper = new BtHelper(this, context);
mDeviceInventory = new AudioDeviceInventory(this);
mSystemServer = SystemServerAdapter.getDefaultAdapter(mContext);
mAudioSystem = audioSystem;
@@ -188,7 +187,7 @@ public class AudioDeviceBroker {
@NonNull AudioSystemAdapter audioSystem) {
mContext = context;
mAudioService = service;
- mBtHelper = new BtHelper(this);
+ mBtHelper = new BtHelper(this, context);
mDeviceInventory = mockDeviceInventory;
mSystemServer = mockSystemServer;
mAudioSystem = audioSystem;
@@ -1392,6 +1391,10 @@ public class AudioDeviceBroker {
return mAudioService.hasAudioFocusUsers();
}
+ /*package*/ void postInitSpatializerHeadTrackingSensors() {
+ mAudioService.postInitSpatializerHeadTrackingSensors();
+ }
+
//---------------------------------------------------------------------
// Message handling on behalf of helper classes.
// Each of these methods posts a message to mBrokerHandler message queue.
@@ -1475,6 +1478,15 @@ public class AudioDeviceBroker {
sendLMsgNoDelay(MSG_L_RECEIVED_BT_EVENT, SENDMSG_QUEUE, intent);
}
+ /*package*/ void postUpdateLeAudioGroupAddresses(int groupId) {
+ sendIMsgNoDelay(
+ MSG_I_UPDATE_LE_AUDIO_GROUP_ADDRESSES, SENDMSG_QUEUE, groupId);
+ }
+
+ /*package*/ void postSynchronizeLeDevicesInInventory(AdiDeviceState deviceState) {
+ sendLMsgNoDelay(MSG_L_SYNCHRONIZE_LE_DEVICES_IN_INVENTORY, SENDMSG_QUEUE, deviceState);
+ }
+
/*package*/ static final class CommunicationDeviceInfo {
final @NonNull IBinder mCb; // Identifies the requesting client for death handler
final int mUid; // Requester UID
@@ -1604,6 +1616,14 @@ public class AudioDeviceBroker {
}
}
+ /*package*/ int getLeAudioDeviceGroupId(BluetoothDevice device) {
+ return mBtHelper.getLeAudioDeviceGroupId(device);
+ }
+
+ /*package*/ List<String> getLeAudioGroupAddresses(int groupId) {
+ return mBtHelper.getLeAudioGroupAddresses(groupId);
+ }
+
/*package*/ void broadcastStickyIntentToCurrentProfileGroup(Intent intent) {
mSystemServer.broadcastStickyIntentToCurrentProfileGroup(intent);
}
@@ -1976,6 +1996,22 @@ public class AudioDeviceBroker {
onCheckCommunicationRouteClientState(msg.arg1, msg.arg2 == 1);
}
} break;
+
+ case MSG_I_UPDATE_LE_AUDIO_GROUP_ADDRESSES:
+ synchronized (mSetModeLock) {
+ synchronized (mDeviceStateLock) {
+ mDeviceInventory.onUpdateLeAudioGroupAddresses(msg.arg1);
+ }
+ } break;
+
+ case MSG_L_SYNCHRONIZE_LE_DEVICES_IN_INVENTORY:
+ synchronized (mSetModeLock) {
+ synchronized (mDeviceStateLock) {
+ mDeviceInventory.onSynchronizeLeDevicesInInventory(
+ (AdiDeviceState) msg.obj);
+ }
+ } break;
+
default:
Log.wtf(TAG, "Invalid message " + msg.what);
}
@@ -2058,6 +2094,10 @@ public class AudioDeviceBroker {
private static final int MSG_L_RECEIVED_BT_EVENT = 55;
private static final int MSG_CHECK_COMMUNICATION_ROUTE_CLIENT_STATE = 56;
+ private static final int MSG_I_UPDATE_LE_AUDIO_GROUP_ADDRESSES = 57;
+ private static final int MSG_L_SYNCHRONIZE_LE_DEVICES_IN_INVENTORY = 58;
+
+
private static boolean isMessageHandledUnderWakelock(int msgId) {
switch(msgId) {
@@ -2582,9 +2622,9 @@ public class AudioDeviceBroker {
}
}
- @Nullable UUID getDeviceSensorUuid(AudioDeviceAttributes device) {
+ List<String> getDeviceAddresses(AudioDeviceAttributes device) {
synchronized (mDeviceStateLock) {
- return mDeviceInventory.getDeviceSensorUuid(device);
+ return mDeviceInventory.getDeviceAddresses(device);
}
}
@@ -2605,15 +2645,19 @@ public class AudioDeviceBroker {
* in order to be mocked by a test a the same package
* (see https://code.google.com/archive/p/mockito/issues/127)
*/
- public void persistAudioDeviceSettings() {
+ public void postPersistAudioDeviceSettings() {
sendMsg(MSG_PERSIST_AUDIO_DEVICE_SETTINGS, SENDMSG_REPLACE, /*delay*/ 1000);
}
void onPersistAudioDeviceSettings() {
final String deviceSettings = mDeviceInventory.getDeviceSettings();
- Log.v(TAG, "saving AdiDeviceState: " + deviceSettings);
- final SettingsAdapter settings = mAudioService.getSettings();
- boolean res = settings.putSecureStringForUser(mAudioService.getContentResolver(),
+ Log.v(TAG, "onPersistAudioDeviceSettings AdiDeviceState: " + deviceSettings);
+ String currentSettings = readDeviceSettings();
+ if (deviceSettings.equals(currentSettings)) {
+ return;
+ }
+ final SettingsAdapter settingsAdapter = mAudioService.getSettings();
+ boolean res = settingsAdapter.putSecureStringForUser(mAudioService.getContentResolver(),
Settings.Secure.AUDIO_DEVICE_INVENTORY,
deviceSettings, UserHandle.USER_CURRENT);
if (!res) {
@@ -2621,11 +2665,17 @@ public class AudioDeviceBroker {
}
}
- void onReadAudioDeviceSettings() {
+ private String readDeviceSettings() {
final SettingsAdapter settingsAdapter = mAudioService.getSettings();
final ContentResolver contentResolver = mAudioService.getContentResolver();
- String settings = settingsAdapter.getSecureStringForUser(contentResolver,
+ return settingsAdapter.getSecureStringForUser(contentResolver,
Settings.Secure.AUDIO_DEVICE_INVENTORY, UserHandle.USER_CURRENT);
+ }
+
+ void onReadAudioDeviceSettings() {
+ final SettingsAdapter settingsAdapter = mAudioService.getSettings();
+ final ContentResolver contentResolver = mAudioService.getContentResolver();
+ String settings = readDeviceSettings();
if (settings == null) {
Log.i(TAG, "reading AdiDeviceState from legacy key"
+ Settings.Secure.SPATIAL_AUDIO_ENABLED);
@@ -2688,8 +2738,8 @@ public class AudioDeviceBroker {
}
@Nullable
- AdiDeviceState findBtDeviceStateForAddress(String address, boolean isBle) {
- return mDeviceInventory.findBtDeviceStateForAddress(address, isBle);
+ AdiDeviceState findBtDeviceStateForAddress(String address, int deviceType) {
+ return mDeviceInventory.findBtDeviceStateForAddress(address, deviceType);
}
//------------------------------------------------
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index e59fd77919c5..7ba0827f2016 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -15,14 +15,23 @@
*/
package com.android.server.audio;
+import static android.media.AudioSystem.DEVICE_IN_ALL_SCO_SET;
import static android.media.AudioSystem.DEVICE_OUT_ALL_A2DP_SET;
import static android.media.AudioSystem.DEVICE_OUT_ALL_BLE_SET;
+import static android.media.AudioSystem.DEVICE_OUT_ALL_SCO_SET;
+import static android.media.AudioSystem.DEVICE_OUT_HEARING_AID;
+import static android.media.AudioSystem.isBluetoothA2dpOutDevice;
import static android.media.AudioSystem.isBluetoothDevice;
+import static android.media.AudioSystem.isBluetoothLeOutDevice;
+import static android.media.AudioSystem.isBluetoothOutDevice;
+import static android.media.AudioSystem.isBluetoothScoOutDevice;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothLeAudio;
import android.bluetooth.BluetoothProfile;
import android.content.Intent;
import android.media.AudioDeviceAttributes;
@@ -72,7 +81,6 @@ import java.util.List;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
-import java.util.UUID;
import java.util.stream.Stream;
/**
@@ -118,6 +126,7 @@ public class AudioDeviceInventory {
return oldState;
});
}
+ mDeviceBroker.postSynchronizeLeDevicesInInventory(deviceState);
}
/**
@@ -125,23 +134,28 @@ public class AudioDeviceInventory {
* Bluetooth device and no corresponding entry already exists.
* @param ada the device to add if needed
*/
- void addAudioDeviceInInventoryIfNeeded(AudioDeviceAttributes ada) {
- if (!AudioSystem.isBluetoothOutDevice(ada.getInternalType())) {
+ void addAudioDeviceInInventoryIfNeeded(int deviceType, String address, String peerAddres) {
+ if (!isBluetoothOutDevice(deviceType)) {
return;
}
synchronized (mDeviceInventoryLock) {
- if (findDeviceStateForAudioDeviceAttributes(ada, ada.getType()) != null) {
+ AdiDeviceState ads = findBtDeviceStateForAddress(address, deviceType);
+ if (ads == null) {
+ ads = findBtDeviceStateForAddress(peerAddres, deviceType);
+ }
+ if (ads != null) {
+ mDeviceBroker.postSynchronizeLeDevicesInInventory(ads);
return;
}
- AdiDeviceState ads = new AdiDeviceState(
- ada.getType(), ada.getInternalType(), ada.getAddress());
+ ads = new AdiDeviceState(AudioDeviceInfo.convertInternalDeviceToDeviceType(deviceType),
+ deviceType, address);
mDeviceInventory.put(ads.getDeviceId(), ads);
+ mDeviceBroker.postPersistAudioDeviceSettings();
}
- mDeviceBroker.persistAudioDeviceSettings();
}
/**
- * Adds a new AdiDeviceState or updates the audio device cateogory of the matching
+ * Adds a new AdiDeviceState or updates the audio device category of the matching
* AdiDeviceState in the {@link AudioDeviceInventory#mDeviceInventory} list.
* @param deviceState the device to update
*/
@@ -152,6 +166,63 @@ public class AudioDeviceInventory {
return oldState;
});
}
+ mDeviceBroker.postSynchronizeLeDevicesInInventory(deviceState);
+ }
+
+ /**
+ * synchronize AdiDeviceState for LE devices in the same group
+ */
+ void onSynchronizeLeDevicesInInventory(AdiDeviceState updatedDevice) {
+ synchronized (mDevicesLock) {
+ synchronized (mDeviceInventoryLock) {
+ boolean found = false;
+ for (DeviceInfo di : mConnectedDevices.values()) {
+ if (di.mDeviceType != updatedDevice.getInternalDeviceType()) {
+ continue;
+ }
+ if (di.mDeviceAddress.equals(updatedDevice.getDeviceAddress())) {
+ for (AdiDeviceState ads2 : mDeviceInventory.values()) {
+ if (!(di.mDeviceType == ads2.getInternalDeviceType()
+ && di.mPeerDeviceAddress.equals(ads2.getDeviceAddress()))) {
+ continue;
+ }
+ ads2.setHasHeadTracker(updatedDevice.hasHeadTracker());
+ ads2.setHeadTrackerEnabled(updatedDevice.isHeadTrackerEnabled());
+ ads2.setSAEnabled(updatedDevice.isSAEnabled());
+ ads2.setAudioDeviceCategory(updatedDevice.getAudioDeviceCategory());
+ found = true;
+ AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
+ "onSynchronizeLeDevicesInInventory synced device pair ads1="
+ + updatedDevice + " ads2=" + ads2).printLog(TAG));
+ break;
+ }
+ }
+ if (di.mPeerDeviceAddress.equals(updatedDevice.getDeviceAddress())) {
+ for (AdiDeviceState ads2 : mDeviceInventory.values()) {
+ if (!(di.mDeviceType == ads2.getInternalDeviceType()
+ && di.mDeviceAddress.equals(ads2.getDeviceAddress()))) {
+ continue;
+ }
+ ads2.setHasHeadTracker(updatedDevice.hasHeadTracker());
+ ads2.setHeadTrackerEnabled(updatedDevice.isHeadTrackerEnabled());
+ ads2.setSAEnabled(updatedDevice.isSAEnabled());
+ ads2.setAudioDeviceCategory(updatedDevice.getAudioDeviceCategory());
+ found = true;
+ AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
+ "onSynchronizeLeDevicesInInventory synced device pair ads1="
+ + updatedDevice + " peer ads2=" + ads2).printLog(TAG));
+ break;
+ }
+ }
+ if (found) {
+ break;
+ }
+ }
+ if (found) {
+ mDeviceBroker.postPersistAudioDeviceSettings();
+ }
+ }
+ }
}
/**
@@ -163,9 +234,21 @@ public class AudioDeviceInventory {
* @return the found {@link AdiDeviceState} or {@code null} otherwise.
*/
@Nullable
- AdiDeviceState findBtDeviceStateForAddress(String address, boolean isBle) {
+ AdiDeviceState findBtDeviceStateForAddress(String address, int deviceType) {
+ Set<Integer> deviceSet;
+ if (isBluetoothA2dpOutDevice(deviceType)) {
+ deviceSet = DEVICE_OUT_ALL_A2DP_SET;
+ } else if (isBluetoothLeOutDevice(deviceType)) {
+ deviceSet = DEVICE_OUT_ALL_BLE_SET;
+ } else if (isBluetoothScoOutDevice(deviceType)) {
+ deviceSet = DEVICE_OUT_ALL_SCO_SET;
+ } else if (deviceType == DEVICE_OUT_HEARING_AID) {
+ deviceSet = new HashSet<>();
+ deviceSet.add(DEVICE_OUT_HEARING_AID);
+ } else {
+ return null;
+ }
synchronized (mDeviceInventoryLock) {
- final Set<Integer> deviceSet = isBle ? DEVICE_OUT_ALL_BLE_SET : DEVICE_OUT_ALL_A2DP_SET;
for (Integer internalType : deviceSet) {
AdiDeviceState deviceState = mDeviceInventory.get(
new Pair<>(internalType, address));
@@ -345,7 +428,8 @@ public class AudioDeviceInventory {
final @NonNull String mDeviceName;
final @NonNull String mDeviceAddress;
int mDeviceCodecFormat;
- final UUID mSensorUuid;
+ @NonNull String mPeerDeviceAddress;
+ final int mGroupId;
/** Disabled operating modes for this device. Use a negative logic so that by default
* an empty list means all modes are allowed.
@@ -353,12 +437,13 @@ public class AudioDeviceInventory {
@NonNull ArraySet<String> mDisabledModes = new ArraySet(0);
DeviceInfo(int deviceType, String deviceName, String deviceAddress,
- int deviceCodecFormat, @Nullable UUID sensorUuid) {
+ int deviceCodecFormat, String peerDeviceAddress, int groupId) {
mDeviceType = deviceType;
mDeviceName = deviceName == null ? "" : deviceName;
mDeviceAddress = deviceAddress == null ? "" : deviceAddress;
mDeviceCodecFormat = deviceCodecFormat;
- mSensorUuid = sensorUuid;
+ mPeerDeviceAddress = peerDeviceAddress == null ? "" : peerDeviceAddress;
+ mGroupId = groupId;
}
void setModeDisabled(String mode) {
@@ -379,7 +464,8 @@ public class AudioDeviceInventory {
DeviceInfo(int deviceType, String deviceName, String deviceAddress,
int deviceCodecFormat) {
- this(deviceType, deviceName, deviceAddress, deviceCodecFormat, null);
+ this(deviceType, deviceName, deviceAddress, deviceCodecFormat,
+ null, BluetoothLeAudio.GROUP_ID_INVALID);
}
DeviceInfo(int deviceType, String deviceName, String deviceAddress) {
@@ -393,7 +479,8 @@ public class AudioDeviceInventory {
+ ") name:" + mDeviceName
+ " addr:" + mDeviceAddress
+ " codec: " + Integer.toHexString(mDeviceCodecFormat)
- + " sensorUuid: " + Objects.toString(mSensorUuid)
+ + " peer addr:" + mPeerDeviceAddress
+ + " group:" + mGroupId
+ " disabled modes: " + mDisabledModes + "]";
}
@@ -714,6 +801,27 @@ public class AudioDeviceInventory {
}
}
+
+ /*package*/ void onUpdateLeAudioGroupAddresses(int groupId) {
+ synchronized (mDevicesLock) {
+ for (DeviceInfo di : mConnectedDevices.values()) {
+ if (di.mGroupId == groupId) {
+ List<String> addresses = mDeviceBroker.getLeAudioGroupAddresses(groupId);
+ if (di.mPeerDeviceAddress.equals("")) {
+ for (String addr : addresses) {
+ if (!addr.equals(di.mDeviceAddress)) {
+ di.mPeerDeviceAddress = addr;
+ break;
+ }
+ }
+ } else if (!addresses.contains(di.mPeerDeviceAddress)) {
+ di.mPeerDeviceAddress = "";
+ }
+ }
+ }
+ }
+ }
+
/*package*/ void onReportNewRoutes() {
int n = mRoutesObservers.beginBroadcast();
if (n > 0) {
@@ -1419,7 +1527,7 @@ public class AudioDeviceInventory {
if (!connect) {
purgeDevicesRoles_l();
} else {
- addAudioDeviceInInventoryIfNeeded(attributes);
+ addAudioDeviceInInventoryIfNeeded(device, address, "");
}
}
mmi.set(MediaMetrics.Property.STATE, MediaMetrics.Value.CONNECTED).record();
@@ -1477,7 +1585,7 @@ public class AudioDeviceInventory {
final ArraySet<String> toRemove = new ArraySet<>();
// Disconnect ALL DEVICE_OUT_HEARING_AID devices
mConnectedDevices.values().forEach(deviceInfo -> {
- if (deviceInfo.mDeviceType == AudioSystem.DEVICE_OUT_HEARING_AID) {
+ if (deviceInfo.mDeviceType == DEVICE_OUT_HEARING_AID) {
toRemove.add(deviceInfo.mDeviceAddress);
}
});
@@ -1485,8 +1593,8 @@ public class AudioDeviceInventory {
.set(MediaMetrics.Property.EVENT, "disconnectHearingAid")
.record();
if (toRemove.size() > 0) {
- final int delay = checkSendBecomingNoisyIntentInt(
- AudioSystem.DEVICE_OUT_HEARING_AID, 0, AudioSystem.DEVICE_NONE);
+ final int delay = checkSendBecomingNoisyIntentInt(DEVICE_OUT_HEARING_AID,
+ AudioService.CONNECTION_STATE_DISCONNECTED, AudioSystem.DEVICE_NONE);
toRemove.stream().forEach(deviceAddress ->
// TODO delay not used?
makeHearingAidDeviceUnavailable(deviceAddress /*, delay*/)
@@ -1687,12 +1795,8 @@ public class AudioDeviceInventory {
// Reset A2DP suspend state each time a new sink is connected
mDeviceBroker.clearA2dpSuspended(true /* internalOnly */);
- // The convention for head tracking sensors associated with A2DP devices is to
- // use a UUID derived from the MAC address as follows:
- // time_low = 0, time_mid = 0, time_hi = 0, clock_seq = 0, node = MAC Address
- UUID sensorUuid = UuidUtils.uuidFromAudioDeviceAttributes(ada);
final DeviceInfo di = new DeviceInfo(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, name,
- address, codec, sensorUuid);
+ address, codec);
final String diKey = di.getKey();
mConnectedDevices.put(diKey, di);
// on a connection always overwrite the device seen by AudioPolicy, see comment above when
@@ -1703,7 +1807,7 @@ public class AudioDeviceInventory {
setCurrentAudioRouteNameIfPossible(name, true /*fromA2dp*/);
updateBluetoothPreferredModes_l(btInfo.mDevice /*connectedDevice*/);
- addAudioDeviceInInventoryIfNeeded(ada);
+ addAudioDeviceInInventoryIfNeeded(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address, "");
}
static final int[] CAPTURE_PRESETS = new int[] {AudioSource.MIC, AudioSource.CAMCORDER,
@@ -1723,15 +1827,15 @@ public class AudioDeviceInventory {
return;
}
DeviceInfo leOutDevice =
- getFirstConnectedDeviceOfTypes(AudioSystem.DEVICE_OUT_ALL_BLE_SET);
+ getFirstConnectedDeviceOfTypes(DEVICE_OUT_ALL_BLE_SET);
DeviceInfo leInDevice =
getFirstConnectedDeviceOfTypes(AudioSystem.DEVICE_IN_ALL_BLE_SET);
DeviceInfo a2dpDevice =
- getFirstConnectedDeviceOfTypes(AudioSystem.DEVICE_OUT_ALL_A2DP_SET);
+ getFirstConnectedDeviceOfTypes(DEVICE_OUT_ALL_A2DP_SET);
DeviceInfo scoOutDevice =
- getFirstConnectedDeviceOfTypes(AudioSystem.DEVICE_OUT_ALL_SCO_SET);
+ getFirstConnectedDeviceOfTypes(DEVICE_OUT_ALL_SCO_SET);
DeviceInfo scoInDevice =
- getFirstConnectedDeviceOfTypes(AudioSystem.DEVICE_IN_ALL_SCO_SET);
+ getFirstConnectedDeviceOfTypes(DEVICE_IN_ALL_SCO_SET);
boolean disableA2dp = (leOutDevice != null && leOutDevice.isOutputOnlyModeEnabled());
boolean disableSco = (leOutDevice != null && leOutDevice.isDuplexModeEnabled())
|| (leInDevice != null && leInDevice.isDuplexModeEnabled());
@@ -1765,7 +1869,7 @@ public class AudioDeviceInventory {
continue;
}
- if (AudioSystem.isBluetoothOutDevice(di.mDeviceType)) {
+ if (isBluetoothOutDevice(di.mDeviceType)) {
for (AudioProductStrategy strategy : mStrategies) {
boolean disable = false;
if (strategy.getId() == mDeviceBroker.mCommunicationStrategyId) {
@@ -1832,23 +1936,20 @@ public class AudioDeviceInventory {
int checkProfileIsConnected(int profile) {
switch (profile) {
case BluetoothProfile.HEADSET:
- if (getFirstConnectedDeviceOfTypes(
- AudioSystem.DEVICE_OUT_ALL_SCO_SET) != null
- || getFirstConnectedDeviceOfTypes(
- AudioSystem.DEVICE_IN_ALL_SCO_SET) != null) {
+ if (getFirstConnectedDeviceOfTypes(DEVICE_OUT_ALL_SCO_SET) != null
+ || getFirstConnectedDeviceOfTypes(DEVICE_IN_ALL_SCO_SET) != null) {
return profile;
}
break;
case BluetoothProfile.A2DP:
- if (getFirstConnectedDeviceOfTypes(
- AudioSystem.DEVICE_OUT_ALL_A2DP_SET) != null) {
+ if (getFirstConnectedDeviceOfTypes(DEVICE_OUT_ALL_A2DP_SET) != null) {
return profile;
}
break;
case BluetoothProfile.LE_AUDIO:
case BluetoothProfile.LE_AUDIO_BROADCAST:
if (getFirstConnectedDeviceOfTypes(
- AudioSystem.DEVICE_OUT_ALL_BLE_SET) != null
+ DEVICE_OUT_ALL_BLE_SET) != null
|| getFirstConnectedDeviceOfTypes(
AudioSystem.DEVICE_IN_ALL_BLE_SET) != null) {
return profile;
@@ -2006,28 +2107,28 @@ public class AudioDeviceInventory {
private void makeHearingAidDeviceAvailable(
String address, String name, int streamType, String eventSource) {
final int hearingAidVolIndex = mDeviceBroker.getVssVolumeForDevice(streamType,
- AudioSystem.DEVICE_OUT_HEARING_AID);
+ DEVICE_OUT_HEARING_AID);
mDeviceBroker.postSetHearingAidVolumeIndex(hearingAidVolIndex, streamType);
mDeviceBroker.setBluetoothA2dpOnInt(true, false /*fromA2dp*/, eventSource);
AudioDeviceAttributes ada = new AudioDeviceAttributes(
- AudioSystem.DEVICE_OUT_HEARING_AID, address, name);
+ DEVICE_OUT_HEARING_AID, address, name);
mAudioSystem.setDeviceConnectionState(ada,
AudioSystem.DEVICE_STATE_AVAILABLE,
AudioSystem.AUDIO_FORMAT_DEFAULT);
mConnectedDevices.put(
- DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_HEARING_AID, address),
- new DeviceInfo(AudioSystem.DEVICE_OUT_HEARING_AID, name, address));
- mDeviceBroker.postAccessoryPlugMediaUnmute(AudioSystem.DEVICE_OUT_HEARING_AID);
+ DeviceInfo.makeDeviceListKey(DEVICE_OUT_HEARING_AID, address),
+ new DeviceInfo(DEVICE_OUT_HEARING_AID, name, address));
+ mDeviceBroker.postAccessoryPlugMediaUnmute(DEVICE_OUT_HEARING_AID);
mDeviceBroker.postApplyVolumeOnDevice(streamType,
- AudioSystem.DEVICE_OUT_HEARING_AID, "makeHearingAidDeviceAvailable");
+ DEVICE_OUT_HEARING_AID, "makeHearingAidDeviceAvailable");
setCurrentAudioRouteNameIfPossible(name, false /*fromA2dp*/);
- addAudioDeviceInInventoryIfNeeded(ada);
+ addAudioDeviceInInventoryIfNeeded(DEVICE_OUT_HEARING_AID, address, "");
new MediaMetrics.Item(mMetricsId + "makeHearingAidDeviceAvailable")
.set(MediaMetrics.Property.ADDRESS, address != null ? address : "")
.set(MediaMetrics.Property.DEVICE,
- AudioSystem.getDeviceName(AudioSystem.DEVICE_OUT_HEARING_AID))
+ AudioSystem.getDeviceName(DEVICE_OUT_HEARING_AID))
.set(MediaMetrics.Property.NAME, name)
.set(MediaMetrics.Property.STREAM_TYPE,
AudioSystem.streamToString(streamType))
@@ -2037,18 +2138,18 @@ public class AudioDeviceInventory {
@GuardedBy("mDevicesLock")
private void makeHearingAidDeviceUnavailable(String address) {
AudioDeviceAttributes ada = new AudioDeviceAttributes(
- AudioSystem.DEVICE_OUT_HEARING_AID, address);
+ DEVICE_OUT_HEARING_AID, address);
mAudioSystem.setDeviceConnectionState(ada,
AudioSystem.DEVICE_STATE_UNAVAILABLE,
AudioSystem.AUDIO_FORMAT_DEFAULT);
mConnectedDevices.remove(
- DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_HEARING_AID, address));
+ DeviceInfo.makeDeviceListKey(DEVICE_OUT_HEARING_AID, address));
// Remove Hearing Aid routes as well
setCurrentAudioRouteNameIfPossible(null, false /*fromA2dp*/);
new MediaMetrics.Item(mMetricsId + "makeHearingAidDeviceUnavailable")
.set(MediaMetrics.Property.ADDRESS, address != null ? address : "")
.set(MediaMetrics.Property.DEVICE,
- AudioSystem.getDeviceName(AudioSystem.DEVICE_OUT_HEARING_AID))
+ AudioSystem.getDeviceName(DEVICE_OUT_HEARING_AID))
.record();
mDeviceBroker.postCheckCommunicationDeviceRemoval(ada);
}
@@ -2060,7 +2161,7 @@ public class AudioDeviceInventory {
*/
boolean isHearingAidConnected() {
return getFirstConnectedDeviceOfTypes(
- Sets.newHashSet(AudioSystem.DEVICE_OUT_HEARING_AID)) != null;
+ Sets.newHashSet(DEVICE_OUT_HEARING_AID)) != null;
}
/**
@@ -2102,6 +2203,20 @@ public class AudioDeviceInventory {
final String address = btInfo.mDevice.getAddress();
String name = BtHelper.getName(btInfo.mDevice);
+ // Find LE Group ID and peer headset address if available
+ final int groupId = mDeviceBroker.getLeAudioDeviceGroupId(btInfo.mDevice);
+ String peerAddress = "";
+ if (groupId != BluetoothLeAudio.GROUP_ID_INVALID) {
+ List<String> addresses = mDeviceBroker.getLeAudioGroupAddresses(groupId);
+ if (addresses.size() > 1) {
+ for (String addr : addresses) {
+ if (!addr.equals(address)) {
+ peerAddress = addr;
+ break;
+ }
+ }
+ }
+ }
// The BT Stack does not provide a name for LE Broadcast devices
if (device == AudioSystem.DEVICE_OUT_BLE_BROADCAST && name.equals("")) {
name = "Broadcast";
@@ -2127,14 +2242,12 @@ public class AudioDeviceInventory {
}
// Reset LEA suspend state each time a new sink is connected
mDeviceBroker.clearLeAudioSuspended(true /* internalOnly */);
-
- UUID sensorUuid = UuidUtils.uuidFromAudioDeviceAttributes(ada);
mConnectedDevices.put(DeviceInfo.makeDeviceListKey(device, address),
new DeviceInfo(device, name, address, AudioSystem.AUDIO_FORMAT_DEFAULT,
- sensorUuid));
+ peerAddress, groupId));
mDeviceBroker.postAccessoryPlugMediaUnmute(device);
setCurrentAudioRouteNameIfPossible(name, /*fromA2dp=*/false);
- addAudioDeviceInInventoryIfNeeded(ada);
+ addAudioDeviceInInventoryIfNeeded(device, address, peerAddress);
}
if (streamType == AudioSystem.STREAM_DEFAULT) {
@@ -2226,12 +2339,12 @@ public class AudioDeviceInventory {
BECOMING_NOISY_INTENT_DEVICES_SET.add(AudioSystem.DEVICE_OUT_HDMI);
BECOMING_NOISY_INTENT_DEVICES_SET.add(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET);
BECOMING_NOISY_INTENT_DEVICES_SET.add(AudioSystem.DEVICE_OUT_LINE);
- BECOMING_NOISY_INTENT_DEVICES_SET.add(AudioSystem.DEVICE_OUT_HEARING_AID);
+ BECOMING_NOISY_INTENT_DEVICES_SET.add(DEVICE_OUT_HEARING_AID);
BECOMING_NOISY_INTENT_DEVICES_SET.add(AudioSystem.DEVICE_OUT_BLE_HEADSET);
BECOMING_NOISY_INTENT_DEVICES_SET.add(AudioSystem.DEVICE_OUT_BLE_BROADCAST);
- BECOMING_NOISY_INTENT_DEVICES_SET.addAll(AudioSystem.DEVICE_OUT_ALL_A2DP_SET);
+ BECOMING_NOISY_INTENT_DEVICES_SET.addAll(DEVICE_OUT_ALL_A2DP_SET);
BECOMING_NOISY_INTENT_DEVICES_SET.addAll(AudioSystem.DEVICE_OUT_ALL_USB_SET);
- BECOMING_NOISY_INTENT_DEVICES_SET.addAll(AudioSystem.DEVICE_OUT_ALL_BLE_SET);
+ BECOMING_NOISY_INTENT_DEVICES_SET.addAll(DEVICE_OUT_ALL_BLE_SET);
}
// must be called before removing the device from mConnectedDevices
@@ -2512,16 +2625,22 @@ public class AudioDeviceInventory {
mDevRoleCapturePresetDispatchers.finishBroadcast();
}
- @Nullable UUID getDeviceSensorUuid(AudioDeviceAttributes device) {
+ List<String> getDeviceAddresses(AudioDeviceAttributes device) {
+ List<String> addresses = new ArrayList<String>();
final String key = DeviceInfo.makeDeviceListKey(device.getInternalType(),
device.getAddress());
synchronized (mDevicesLock) {
DeviceInfo di = mConnectedDevices.get(key);
- if (di == null) {
- return null;
+ if (di != null) {
+ if (!di.mDeviceAddress.isEmpty()) {
+ addresses.add(di.mDeviceAddress);
+ }
+ if (!di.mPeerDeviceAddress.isEmpty()) {
+ addresses.add(di.mPeerDeviceAddress);
+ }
}
- return di.mSensorUuid;
}
+ return addresses;
}
/*package*/ String getDeviceSettings() {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index d0ae0f25d6cd..99321c44931b 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -237,7 +237,6 @@ import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;
-import java.util.UUID;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
@@ -11054,7 +11053,9 @@ public class AudioService extends IAudioService.Stub
final String addr = Objects.requireNonNull(address);
- AdiDeviceState deviceState = mDeviceBroker.findBtDeviceStateForAddress(addr, isBle);
+ AdiDeviceState deviceState = mDeviceBroker.findBtDeviceStateForAddress(addr,
+ (isBle ? AudioSystem.DEVICE_OUT_BLE_HEADSET
+ : AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP));
int internalType = !isBle ? DEVICE_OUT_BLUETOOTH_A2DP
: ((btAudioDeviceCategory == AUDIO_DEVICE_CATEGORY_HEADPHONES)
@@ -11070,7 +11071,7 @@ public class AudioService extends IAudioService.Stub
deviceState.setAudioDeviceCategory(btAudioDeviceCategory);
mDeviceBroker.addOrUpdateBtAudioDeviceCategoryInInventory(deviceState);
- mDeviceBroker.persistAudioDeviceSettings();
+ mDeviceBroker.postPersistAudioDeviceSettings();
mSpatializerHelper.refreshDevice(deviceState.getAudioDeviceAttributes());
mSoundDoseHelper.setAudioDeviceCategory(addr, internalType,
@@ -11084,7 +11085,8 @@ public class AudioService extends IAudioService.Stub
super.getBluetoothAudioDeviceCategory_enforcePermission();
final AdiDeviceState deviceState = mDeviceBroker.findBtDeviceStateForAddress(
- Objects.requireNonNull(address), isBle);
+ Objects.requireNonNull(address), (isBle ? AudioSystem.DEVICE_OUT_BLE_HEADSET
+ : AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP));
if (deviceState == null) {
return AUDIO_DEVICE_CATEGORY_UNKNOWN;
}
@@ -13596,8 +13598,8 @@ public class AudioService extends IAudioService.Stub
return activeAssistantUids;
}
- UUID getDeviceSensorUuid(AudioDeviceAttributes device) {
- return mDeviceBroker.getDeviceSensorUuid(device);
+ List<String> getDeviceAddresses(AudioDeviceAttributes device) {
+ return mDeviceBroker.getDeviceAddresses(device);
}
//======================
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index cce6bd2938d1..7b9621581adf 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -26,7 +26,9 @@ import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothHearingAid;
import android.bluetooth.BluetoothLeAudio;
+import android.bluetooth.BluetoothLeAudioCodecStatus;
import android.bluetooth.BluetoothProfile;
+import android.content.Context;
import android.content.Intent;
import android.media.AudioDeviceAttributes;
import android.media.AudioManager;
@@ -43,6 +45,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.server.utils.EventLogger;
import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -57,9 +60,11 @@ public class BtHelper {
private static final String TAG = "AS.BtHelper";
private final @NonNull AudioDeviceBroker mDeviceBroker;
+ private final @NonNull Context mContext;
- BtHelper(@NonNull AudioDeviceBroker broker) {
+ BtHelper(@NonNull AudioDeviceBroker broker, Context context) {
mDeviceBroker = broker;
+ mContext = context;
}
// BluetoothHeadset API to control SCO connection
@@ -498,6 +503,32 @@ public class BtHelper {
}
}
+ // BluetoothLeAudio callback used to update the list of addresses in the same group as a
+ // connected LE Audio device
+ MyLeAudioCallback mLeAudioCallback = null;
+
+ class MyLeAudioCallback implements BluetoothLeAudio.Callback {
+ @Override
+ public void onCodecConfigChanged(int groupId,
+ @NonNull BluetoothLeAudioCodecStatus status) {
+ // Do nothing
+ }
+
+ @Override
+ public void onGroupNodeAdded(@NonNull BluetoothDevice device, int groupId) {
+ mDeviceBroker.postUpdateLeAudioGroupAddresses(groupId);
+ }
+
+ @Override
+ public void onGroupNodeRemoved(@NonNull BluetoothDevice device, int groupId) {
+ mDeviceBroker.postUpdateLeAudioGroupAddresses(groupId);
+ }
+ @Override
+ public void onGroupStatusChanged(int groupId, int groupStatus) {
+ mDeviceBroker.postUpdateLeAudioGroupAddresses(groupId);
+ }
+ }
+
// @GuardedBy("mDeviceBroker.mSetModeLock")
@GuardedBy("AudioDeviceBroker.this.mDeviceStateLock")
/*package*/ synchronized void onBtProfileConnected(int profile, BluetoothProfile proxy) {
@@ -519,6 +550,11 @@ public class BtHelper {
mHearingAid = (BluetoothHearingAid) proxy;
break;
case BluetoothProfile.LE_AUDIO:
+ if (mLeAudio == null) {
+ mLeAudioCallback = new MyLeAudioCallback();
+ ((BluetoothLeAudio) proxy).registerCallback(
+ mContext.getMainExecutor(), mLeAudioCallback);
+ }
mLeAudio = (BluetoothLeAudio) proxy;
break;
case BluetoothProfile.A2DP_SINK:
@@ -977,6 +1013,28 @@ public class BtHelper {
return result;
}
+ /*package*/ int getLeAudioDeviceGroupId(BluetoothDevice device) {
+ if (mLeAudio == null || device == null) {
+ return BluetoothLeAudio.GROUP_ID_INVALID;
+ }
+ return mLeAudio.getGroupId(device);
+ }
+
+ /*package*/ List<String> getLeAudioGroupAddresses(int groupId) {
+ List<String> addresses = new ArrayList<String>();
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ if (adapter == null || mLeAudio == null) {
+ return addresses;
+ }
+ List<BluetoothDevice> activeDevices = adapter.getActiveDevices(BluetoothProfile.LE_AUDIO);
+ for (BluetoothDevice device : activeDevices) {
+ if (device != null && mLeAudio.getGroupId(device) == groupId) {
+ addresses.add(device.getAddress());
+ }
+ }
+ return addresses;
+ }
+
/**
* Returns the String equivalent of the btCodecType.
*
diff --git a/services/core/java/com/android/server/audio/SpatializerHelper.java b/services/core/java/com/android/server/audio/SpatializerHelper.java
index 7abd9c7f750b..ea92154f2df0 100644
--- a/services/core/java/com/android/server/audio/SpatializerHelper.java
+++ b/services/core/java/com/android/server/audio/SpatializerHelper.java
@@ -564,7 +564,7 @@ public class SpatializerHelper {
}
if (updatedDevice != null) {
onRoutingUpdated();
- mDeviceBroker.persistAudioDeviceSettings();
+ mDeviceBroker.postPersistAudioDeviceSettings();
logDeviceState(updatedDevice, "addCompatibleAudioDevice");
}
}
@@ -614,7 +614,7 @@ public class SpatializerHelper {
if (deviceState != null && deviceState.isSAEnabled()) {
deviceState.setSAEnabled(false);
onRoutingUpdated();
- mDeviceBroker.persistAudioDeviceSettings();
+ mDeviceBroker.postPersistAudioDeviceSettings();
logDeviceState(deviceState, "removeCompatibleAudioDevice");
}
}
@@ -716,7 +716,7 @@ public class SpatializerHelper {
ada.getAddress());
initSAState(deviceState);
mDeviceBroker.addOrUpdateDeviceSAStateInInventory(deviceState);
- mDeviceBroker.persistAudioDeviceSettings();
+ mDeviceBroker.postPersistAudioDeviceSettings();
logDeviceState(deviceState, "addWirelessDeviceIfNew"); // may be updated later.
}
}
@@ -1206,7 +1206,7 @@ public class SpatializerHelper {
}
Log.i(TAG, "setHeadTrackerEnabled enabled:" + enabled + " device:" + ada);
deviceState.setHeadTrackerEnabled(enabled);
- mDeviceBroker.persistAudioDeviceSettings();
+ mDeviceBroker.postPersistAudioDeviceSettings();
logDeviceState(deviceState, "setHeadTrackerEnabled");
// check current routing to see if it affects the headtracking mode
@@ -1248,7 +1248,7 @@ public class SpatializerHelper {
if (deviceState != null) {
if (!deviceState.hasHeadTracker()) {
deviceState.setHasHeadTracker(true);
- mDeviceBroker.persistAudioDeviceSettings();
+ mDeviceBroker.postPersistAudioDeviceSettings();
logDeviceState(deviceState, "setHasHeadTracker");
}
return deviceState.isHeadTrackerEnabled();
@@ -1631,25 +1631,33 @@ public class SpatializerHelper {
return headHandle;
}
final AudioDeviceAttributes currentDevice = sRoutingDevices.get(0);
- UUID routingDeviceUuid = mAudioService.getDeviceSensorUuid(currentDevice);
+ List<String> deviceAddresses = mAudioService.getDeviceAddresses(currentDevice);
+
// We limit only to Sensor.TYPE_HEAD_TRACKER here to avoid confusion
// with gaming sensors. (Note that Sensor.TYPE_ROTATION_VECTOR
// and Sensor.TYPE_GAME_ROTATION_VECTOR are supported internally by
// SensorPoseProvider).
// Note: this is a dynamic sensor list right now.
List<Sensor> sensors = mSensorManager.getDynamicSensorList(Sensor.TYPE_HEAD_TRACKER);
- for (Sensor sensor : sensors) {
- final UUID uuid = sensor.getUuid();
- if (uuid.equals(routingDeviceUuid)) {
- headHandle = sensor.getHandle();
- if (!setHasHeadTracker(currentDevice)) {
- headHandle = -1;
+ for (String address : deviceAddresses) {
+ UUID routingDeviceUuid = UuidUtils.uuidFromAudioDeviceAttributes(
+ new AudioDeviceAttributes(currentDevice.getInternalType(), address));
+ for (Sensor sensor : sensors) {
+ final UUID uuid = sensor.getUuid();
+ if (uuid.equals(routingDeviceUuid)) {
+ headHandle = sensor.getHandle();
+ if (!setHasHeadTracker(currentDevice)) {
+ headHandle = -1;
+ }
+ break;
+ }
+ if (uuid.equals(UuidUtils.STANDALONE_UUID)) {
+ headHandle = sensor.getHandle();
+ // we do not break, perhaps we find a head tracker on device.
}
- break;
}
- if (uuid.equals(UuidUtils.STANDALONE_UUID)) {
- headHandle = sensor.getHandle();
- // we do not break, perhaps we find a head tracker on device.
+ if (headHandle != -1) {
+ break;
}
}
return headHandle;
diff --git a/services/tests/servicestests/src/com/android/server/audio/SpatializerHelperTest.java b/services/tests/servicestests/src/com/android/server/audio/SpatializerHelperTest.java
index ad09ef0ccdc1..061b8ffa05a2 100644
--- a/services/tests/servicestests/src/com/android/server/audio/SpatializerHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/SpatializerHelperTest.java
@@ -79,7 +79,7 @@ public class SpatializerHelperTest {
final AudioDeviceAttributes dev3 =
new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, "R2:D2:bloop");
- doNothing().when(mSpyDeviceBroker).persistAudioDeviceSettings();
+ doNothing().when(mSpyDeviceBroker).postPersistAudioDeviceSettings();
mSpatHelper.initForTest(true /*binaural*/, true /*transaural*/);
// test with single device