From ad46dc5b5e97cd499f5f94f2a78cd31440e56089 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Grzegorz=20Ko=C5=82odziejczyk?= Date: Wed, 13 Oct 2021 08:49:17 +0000 Subject: [PATCH] server/audio: Add LeAudio support This patch adds handling active device and set volume Bug: 150670922 Tag: #feature Sponsor: jpawlowski@ Test: Manual Change-Id: I50a966ed2f199464381ff561fd83342b0a9b08a9 --- media/java/android/media/MediaRoute2Info.java | 10 +++- .../server/audio/AudioDeviceBroker.java | 53 ++++++++++++++++ .../server/audio/AudioDeviceInventory.java | 27 +++++++++ .../android/server/audio/AudioService.java | 24 ++++++++ .../server/audio/AudioServiceEvents.java | 13 ++++ .../com/android/server/audio/BtHelper.java | 56 +++++++++++++++++ .../server/media/BluetoothRouteProvider.java | 60 +++++++++++++++++-- 7 files changed, 237 insertions(+), 6 deletions(-) diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java index 7e9d2d809fdd..9c9e83b0987d 100644 --- a/media/java/android/media/MediaRoute2Info.java +++ b/media/java/android/media/MediaRoute2Info.java @@ -106,7 +106,7 @@ public final class MediaRoute2Info implements Parcelable { @IntDef({ TYPE_UNKNOWN, TYPE_BUILTIN_SPEAKER, TYPE_WIRED_HEADSET, TYPE_WIRED_HEADPHONES, TYPE_BLUETOOTH_A2DP, TYPE_HDMI, TYPE_USB_DEVICE, - TYPE_USB_ACCESSORY, TYPE_DOCK, TYPE_USB_HEADSET, TYPE_HEARING_AID, + TYPE_USB_ACCESSORY, TYPE_DOCK, TYPE_USB_HEADSET, TYPE_HEARING_AID, TYPE_BLE_HEADSET, TYPE_REMOTE_TV, TYPE_REMOTE_SPEAKER, TYPE_GROUP}) @Retention(RetentionPolicy.SOURCE) public @interface Type {} @@ -201,6 +201,14 @@ public final class MediaRoute2Info implements Parcelable { */ public static final int TYPE_HEARING_AID = AudioDeviceInfo.TYPE_HEARING_AID; + /** + * A route type describing a BLE HEADSET. + * + * @see #getType + * @hide + */ + public static final int TYPE_BLE_HEADSET = AudioDeviceInfo.TYPE_BLE_HEADSET; + /** * A route type indicating the presentation of the media is happening on a TV. * diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index ddbb32041a81..c383f5120407 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -22,6 +22,7 @@ import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothHearingAid; import android.bluetooth.BluetoothProfile; +import android.bluetooth.BluetoothLeAudio; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; @@ -503,6 +504,18 @@ import java.util.concurrent.atomic.AtomicBoolean; } } + /*package*/ static final class BleVolumeInfo { + final int mIndex; + final int mMaxIndex; + final int mStreamType; + + BleVolumeInfo(int index, int maxIndex, int streamType) { + mIndex = index; + mMaxIndex = maxIndex; + mStreamType = streamType; + } + }; + /*package*/ static final class BtDeviceConnectionInfo { final @NonNull BluetoothDevice mDevice; final @AudioService.BtProfileConnectionState int mState; @@ -711,6 +724,11 @@ import java.util.concurrent.atomic.AtomicBoolean; sendIIMsgNoDelay(MSG_II_SET_HEARING_AID_VOLUME, SENDMSG_REPLACE, index, streamType); } + /*package*/ void postSetLeAudioVolumeIndex(int index, int maxIndex, int streamType) { + BleVolumeInfo info = new BleVolumeInfo(index, maxIndex, streamType); + sendLMsgNoDelay(MSG_II_SET_LE_AUDIO_OUT_VOLUME, SENDMSG_REPLACE, info); + } + /*package*/ void postSetModeOwnerPid(int pid, int mode) { sendIIMsgNoDelay(MSG_I_SET_MODE_OWNER_PID, SENDMSG_REPLACE, pid, mode); } @@ -851,6 +869,10 @@ import java.util.concurrent.atomic.AtomicBoolean; return mAudioService.getVssVolumeForDevice(streamType, device); } + /*package*/ int getMaxVssVolumeForStream(int streamType) { + return mAudioService.getMaxVssVolumeForStream(streamType); + } + /*package*/ int getDeviceForStream(int streamType) { return mAudioService.getDeviceForStream(streamType); } @@ -962,6 +984,10 @@ import java.util.concurrent.atomic.AtomicBoolean; sendMsgNoDelay(MSG_DISCONNECT_BT_HEARING_AID, SENDMSG_QUEUE); } + /*package*/ void postDisconnectLeAudio() { + sendMsgNoDelay(MSG_DISCONNECT_BT_LE_AUDIO, SENDMSG_QUEUE); + } + /*package*/ void postDisconnectHeadset() { sendMsgNoDelay(MSG_DISCONNECT_BT_HEADSET, SENDMSG_QUEUE); } @@ -983,6 +1009,11 @@ import java.util.concurrent.atomic.AtomicBoolean; hearingAidProfile); } + /*package*/ void postBtLeAudioProfileConnected(BluetoothLeAudio leAudioProfile) { + sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_LE_AUDIO, SENDMSG_QUEUE, + leAudioProfile); + } + /*package*/ void postCommunicationRouteClientDied(CommunicationRouteClient client) { sendLMsgNoDelay(MSG_L_COMMUNICATION_ROUTE_CLIENT_DIED, SENDMSG_QUEUE, client); } @@ -1321,6 +1352,12 @@ import java.util.concurrent.atomic.AtomicBoolean; mBtHelper.setHearingAidVolume(msg.arg1, msg.arg2); } break; + case MSG_II_SET_LE_AUDIO_OUT_VOLUME: { + final BleVolumeInfo info = (BleVolumeInfo) msg.obj; + synchronized (mDeviceStateLock) { + mBtHelper.setLeAudioVolume(info.mIndex, info.mMaxIndex, info.mStreamType); + } + } break; case MSG_I_SET_AVRCP_ABSOLUTE_VOLUME: synchronized (mDeviceStateLock) { mBtHelper.setAvrcpAbsoluteVolumeIndex(msg.arg1); @@ -1384,6 +1421,11 @@ import java.util.concurrent.atomic.AtomicBoolean; } } break; + case MSG_DISCONNECT_BT_LE_AUDIO: + synchronized(mDeviceStateLock) { + mDeviceInventory.disconnectLeAudio(); + } + break; case MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP: synchronized (mDeviceStateLock) { mBtHelper.onA2dpProfileConnected((BluetoothA2dp) msg.obj); @@ -1399,6 +1441,12 @@ import java.util.concurrent.atomic.AtomicBoolean; mBtHelper.onHearingAidProfileConnected((BluetoothHearingAid) msg.obj); } break; + + case MSG_L_BT_SERVICE_CONNECTED_PROFILE_LE_AUDIO: + synchronized(mDeviceStateLock) { + mBtHelper.onLeAudioProfileConnected((BluetoothLeAudio) msg.obj); + } + break; case MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEADSET: synchronized (mSetModeLock) { synchronized (mDeviceStateLock) { @@ -1586,6 +1634,11 @@ import java.util.concurrent.atomic.AtomicBoolean; private static final int MSG_IL_SET_LE_AUDIO_IN_CONNECTION_STATE = 43; private static final int MSG_L_LE_AUDIO_DEVICE_OUT_CONNECTION_CHANGE_EXT = 44; private static final int MSG_L_LE_AUDIO_DEVICE_IN_CONNECTION_CHANGE_EXT = 45; + // process set volume for Le Audio, obj is BleVolumeInfo + private static final int MSG_II_SET_LE_AUDIO_OUT_VOLUME = 46; + + private static final int MSG_L_BT_SERVICE_CONNECTED_PROFILE_LE_AUDIO = 47; + private static final int MSG_DISCONNECT_BT_LE_AUDIO = 48; private static boolean isMessageHandledUnderWakelock(int msgId) { switch(msgId) { diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java index 64e620eeb8a0..6c3c736aeb93 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java +++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java @@ -887,6 +887,28 @@ public class AudioDeviceInventory { } } + /*package*/ void disconnectLeAudio() { + synchronized (mDevicesLock) { + final ArraySet toRemove = new ArraySet<>(); + // Disconnect ALL DEVICE_OUT_BLE_HEADSET devices + mConnectedDevices.values().forEach(deviceInfo -> { + if (deviceInfo.mDeviceType == AudioSystem.DEVICE_OUT_BLE_HEADSET) { + toRemove.add(deviceInfo.mDeviceAddress); + } + }); + new MediaMetrics.Item(mMetricsId + "disconnectLeAudio") + .record(); + if (toRemove.size() > 0) { + final int delay = checkSendBecomingNoisyIntentInt( + AudioSystem.DEVICE_OUT_BLE_HEADSET, 0, AudioSystem.DEVICE_NONE); + toRemove.stream().forEach(deviceAddress -> + makeLeAudioDeviceUnavailable(deviceAddress, + AudioSystem.DEVICE_OUT_BLE_HEADSET) + ); + } + } + } + // must be called before removing the device from mConnectedDevices // musicDevice argument is used when not AudioSystem.DEVICE_NONE instead of querying // from AudioSystem @@ -1195,6 +1217,10 @@ public class AudioDeviceInventory { return; } + final int leAudioVolIndex = mDeviceBroker.getVssVolumeForDevice(streamType, + AudioSystem.DEVICE_OUT_BLE_HEADSET); + final int maxIndex = mDeviceBroker.getMaxVssVolumeForStream(streamType); + mDeviceBroker.postSetLeAudioVolumeIndex(leAudioVolIndex, maxIndex, streamType); mDeviceBroker.postApplyVolumeOnDevice(streamType, device, "makeLeAudioDeviceAvailable"); } @@ -1243,6 +1269,7 @@ public class AudioDeviceInventory { BECOMING_NOISY_INTENT_DEVICES_SET.add(AudioSystem.DEVICE_OUT_DGTL_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(AudioSystem.DEVICE_OUT_BLE_HEADSET); BECOMING_NOISY_INTENT_DEVICES_SET.addAll(AudioSystem.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); diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index bf5f4c2d3d33..8e1b06b8ca09 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -334,6 +334,10 @@ public class AudioService extends IAudioService.Stub return mStreamStates[stream].getIndex(device); } + /*package*/ int getMaxVssVolumeForStream(int stream) { + return mStreamStates[stream].getMaxIndex(); + } + private SettingsObserver mSettingsObserver; private AtomicInteger mMode = new AtomicInteger(AudioSystem.MODE_NORMAL); @@ -2952,6 +2956,16 @@ public class AudioService extends IAudioService.Stub mDeviceBroker.postSetAvrcpAbsoluteVolumeIndex(newIndex / 10); } + if (device == AudioSystem.DEVICE_OUT_BLE_HEADSET + && streamType == getBluetoothContextualVolumeStream()) { + if (DEBUG_VOL) { + Log.d(TAG, "adjustSreamVolume postSetLeAudioVolumeIndex index=" + + newIndex + " stream=" + streamType); + } + mDeviceBroker.postSetLeAudioVolumeIndex(newIndex, + mStreamStates[streamType].getMaxIndex(), streamType); + } + // Check if volume update should be send to Hearing Aid if (device == AudioSystem.DEVICE_OUT_HEARING_AID) { // only modify the hearing aid attenuation when the stream to modify matches @@ -3580,6 +3594,16 @@ public class AudioService extends IAudioService.Stub mDeviceBroker.postSetAvrcpAbsoluteVolumeIndex(index / 10); } + if (device == AudioSystem.DEVICE_OUT_BLE_HEADSET + && streamType == getBluetoothContextualVolumeStream()) { + if (DEBUG_VOL) { + Log.d(TAG, "adjustSreamVolume postSetLeAudioVolumeIndex index=" + + index + " stream=" + streamType); + } + mDeviceBroker.postSetLeAudioVolumeIndex(index, + mStreamStates[streamType].getMaxIndex(), streamType); + } + if (device == AudioSystem.DEVICE_OUT_HEARING_AID && streamType == getBluetoothContextualVolumeStream()) { Log.i(TAG, "setStreamVolume postSetHearingAidVolumeIndex index=" + index diff --git a/services/core/java/com/android/server/audio/AudioServiceEvents.java b/services/core/java/com/android/server/audio/AudioServiceEvents.java index 0eb5a5d1fb48..3137fa5784d2 100644 --- a/services/core/java/com/android/server/audio/AudioServiceEvents.java +++ b/services/core/java/com/android/server/audio/AudioServiceEvents.java @@ -155,6 +155,7 @@ public class AudioServiceEvents { static final int VOL_MODE_CHANGE_HEARING_AID = 7; static final int VOL_SET_GROUP_VOL = 8; static final int VOL_MUTE_STREAM_INT = 9; + static final int VOL_SET_LE_AUDIO_VOL = 10; final int mOp; final int mStream; @@ -310,6 +311,13 @@ public class AudioServiceEvents { .set(MediaMetrics.Property.INDEX, mVal1) .record(); return; + case VOL_SET_LE_AUDIO_VOL: + new MediaMetrics.Item(mMetricsId) + .set(MediaMetrics.Property.EVENT, "setLeAudioVolume") + .set(MediaMetrics.Property.INDEX, mVal1) + .set(MediaMetrics.Property.MAX_INDEX, mVal2) + .record(); + return; case VOL_SET_AVRCP_VOL: new MediaMetrics.Item(mMetricsId) .set(MediaMetrics.Property.EVENT, "setAvrcpVolume") @@ -382,6 +390,11 @@ public class AudioServiceEvents { .append(" index:").append(mVal1) .append(" gain dB:").append(mVal2) .toString(); + case VOL_SET_LE_AUDIO_VOL: + return new StringBuilder("setLeAudioVolume:") + .append(" index:").append(mVal1) + .append(" gain dB:").append(mVal2) + .toString(); case VOL_SET_AVRCP_VOL: return new StringBuilder("setAvrcpVolume:") .append(" index:").append(mVal1) diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java index 52e8edff5ffa..c924fde23f9d 100644 --- a/services/core/java/com/android/server/audio/BtHelper.java +++ b/services/core/java/com/android/server/audio/BtHelper.java @@ -25,6 +25,7 @@ import android.bluetooth.BluetoothCodecStatus; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothHearingAid; +import android.bluetooth.BluetoothLeAudio; import android.bluetooth.BluetoothProfile; import android.content.Intent; import android.media.AudioDeviceAttributes; @@ -63,6 +64,8 @@ public class BtHelper { private @Nullable BluetoothHearingAid mHearingAid; + private @Nullable BluetoothLeAudio mLeAudio; + // Reference to BluetoothA2dp to query for AbsoluteVolume. private @Nullable BluetoothA2dp mA2dp; @@ -106,6 +109,8 @@ public class BtHelper { private static final int SCO_MODE_MAX = 2; private static final int BT_HEARING_AID_GAIN_MIN = -128; + private static final int BT_LE_AUDIO_MIN_VOL = 0; + private static final int BT_LE_AUDIO_MAX_VOL = 255; /** * Returns a string representation of the scoAudioMode. @@ -235,6 +240,8 @@ public class BtHelper { mBluetoothProfileServiceListener, BluetoothProfile.A2DP); adapter.getProfileProxy(mDeviceBroker.getContext(), mBluetoothProfileServiceListener, BluetoothProfile.HEARING_AID); + adapter.getProfileProxy(mDeviceBroker.getContext(), + mBluetoothProfileServiceListener, BluetoothProfile.LE_AUDIO); } } @@ -389,6 +396,26 @@ public class BtHelper { return requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, SCO_MODE_VIRTUAL_CALL); } + /*package*/ synchronized void setLeAudioVolume(int index, int maxIndex, int streamType) { + if (mLeAudio == null) { + if (AudioService.DEBUG_VOL) { + Log.i(TAG, "setLeAudioVolume: null mLeAudio"); + } + return; + } + /* leaudio expect volume value in range 0 to 255 + */ + int volume = (index * (BT_LE_AUDIO_MAX_VOL - BT_LE_AUDIO_MIN_VOL)) / maxIndex ; + + if (AudioService.DEBUG_VOL) { + Log.i(TAG, "setLeAudioVolume: calling mLeAudio.setVolume idx=" + + index + " volume=" + volume); + } + AudioService.sVolumeLogger.log(new AudioServiceEvents.VolumeEvent( + AudioServiceEvents.VolumeEvent.VOL_SET_LE_AUDIO_VOL, index, maxIndex)); + mLeAudio.setVolume(volume); + } + /*package*/ synchronized void setHearingAidVolume(int index, int streamType) { if (mHearingAid == null) { if (AudioService.DEBUG_VOL) { @@ -428,6 +455,7 @@ public class BtHelper { mDeviceBroker.postDisconnectA2dpSink(); mDeviceBroker.postDisconnectHeadset(); mDeviceBroker.postDisconnectHearingAid(); + mDeviceBroker.postDisconnectLeAudio(); } // @GuardedBy("AudioDeviceBroker.mSetModeLock") @@ -488,6 +516,23 @@ public class BtHelper { /*eventSource*/ "mBluetoothProfileServiceListener"); } + /*package*/ synchronized void onLeAudioProfileConnected(BluetoothLeAudio leAudio) { + mLeAudio = leAudio; + final List deviceList = mLeAudio.getConnectedDevices(); + if (deviceList.isEmpty()) { + return; + } + + final BluetoothDevice btDevice = deviceList.get(0); + final @BluetoothProfile.BtProfileState int state = + mLeAudio.getConnectionState(btDevice); + mDeviceBroker.postBluetoothLeAudioOutDeviceConnectionState( + btDevice, state, + /*suppressNoisyIntent*/ false, + /*musicDevice android.media.AudioSystem.DEVICE_NONE,*/ + /*eventSource*/ "mBluetoothProfileServiceListener"); + } + // @GuardedBy("AudioDeviceBroker.mSetModeLock") @GuardedBy("AudioDeviceBroker.mDeviceStateLock") /*package*/ synchronized void onHeadsetProfileConnected(BluetoothHeadset headset) { @@ -655,6 +700,13 @@ public class BtHelper { mDeviceBroker.postBtHearingAidProfileConnected( (BluetoothHearingAid) proxy); break; + + case BluetoothProfile.LE_AUDIO: + AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( + "BT profile service: connecting LE_AUDIO profile")); + mDeviceBroker.postBtLeAudioProfileConnected( + (BluetoothLeAudio) proxy); + break; default: break; } @@ -677,6 +729,9 @@ public class BtHelper { case BluetoothProfile.HEARING_AID: mDeviceBroker.postDisconnectHearingAid(); break; + case BluetoothProfile.LE_AUDIO: + mDeviceBroker.postDisconnectLeAudio(); + break; default: break; @@ -899,6 +954,7 @@ public class BtHelper { pw.println(prefix + "mScoAudioState: " + scoAudioStateToString(mScoAudioState)); pw.println(prefix + "mScoAudioMode: " + scoAudioModeToString(mScoAudioMode)); pw.println("\n" + prefix + "mHearingAid: " + mHearingAid); + pw.println("\n" + prefix + "mLeAudio: " + mLeAudio); pw.println(prefix + "mA2dp: " + mA2dp); pw.println(prefix + "mAvrcpAbsVolSupported: " + mAvrcpAbsVolSupported); } diff --git a/services/core/java/com/android/server/media/BluetoothRouteProvider.java b/services/core/java/com/android/server/media/BluetoothRouteProvider.java index 7afa81aa047d..73de0f814325 100644 --- a/services/core/java/com/android/server/media/BluetoothRouteProvider.java +++ b/services/core/java/com/android/server/media/BluetoothRouteProvider.java @@ -26,6 +26,7 @@ import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHearingAid; +import android.bluetooth.BluetoothLeAudio; import android.bluetooth.BluetoothProfile; import android.content.BroadcastReceiver; import android.content.Context; @@ -56,6 +57,7 @@ class BluetoothRouteProvider { private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private static final String HEARING_AID_ROUTE_ID_PREFIX = "HEARING_AID_"; + private static final String LE_AUDIO_ROUTE_ID_PREFIX = "LE_AUDIO_"; @SuppressWarnings("WeakerAccess") /* synthetic access */ // Maps hardware address to BluetoothRouteInfo @@ -66,6 +68,8 @@ class BluetoothRouteProvider { BluetoothA2dp mA2dpProfile; @SuppressWarnings("WeakerAccess") /* synthetic access */ BluetoothHearingAid mHearingAidProfile; + @SuppressWarnings("WeakerAccess") /* synthetic access */ + BluetoothLeAudio mLeAudioProfile; // Route type -> volume map private final SparseIntArray mVolumeMap = new SparseIntArray(); @@ -108,6 +112,7 @@ class BluetoothRouteProvider { public void start(UserHandle user) { mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.A2DP); mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.HEARING_AID); + mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.LE_AUDIO); // Bluetooth on/off broadcasts addEventReceiver(BluetoothAdapter.ACTION_STATE_CHANGED, new AdapterStateChangedReceiver()); @@ -119,6 +124,10 @@ class BluetoothRouteProvider { deviceStateChangedReceiver); addEventReceiver(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED, deviceStateChangedReceiver); + addEventReceiver(BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED, + deviceStateChangedReceiver); + addEventReceiver(BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED, + deviceStateChangedReceiver); mContext.registerReceiverAsUser(mBroadcastReceiver, user, mIntentFilter, null, null); @@ -240,6 +249,8 @@ class BluetoothRouteProvider { | AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES | AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)) != 0) { routeType = MediaRoute2Info.TYPE_BLUETOOTH_A2DP; + } else if ((devices & (AudioManager.DEVICE_OUT_BLE_HEADSET)) != 0) { + routeType = MediaRoute2Info.TYPE_BLE_HEADSET; } else { return false; } @@ -288,6 +299,12 @@ class BluetoothRouteProvider { routeId = HEARING_AID_ROUTE_ID_PREFIX + mHearingAidProfile.getHiSyncId(device); type = MediaRoute2Info.TYPE_HEARING_AID; } + if (mLeAudioProfile != null + && mLeAudioProfile.getConnectedDevices().contains(device)) { + newBtRoute.connectedProfiles.put(BluetoothProfile.LE_AUDIO, true); + routeId = LE_AUDIO_ROUTE_ID_PREFIX + mLeAudioProfile.getGroupId(device); + type = MediaRoute2Info.TYPE_BLE_HEADSET; + } // Current volume will be set when connected. newBtRoute.route = new MediaRoute2Info.Builder(routeId, deviceName) @@ -358,11 +375,7 @@ class BluetoothRouteProvider { } } - private void addActiveHearingAidDevices(BluetoothDevice device) { - if (DEBUG) { - Log.d(TAG, "Setting active hearing aid devices. device=" + device); - } - + private void addActiveDevices(BluetoothDevice device) { // Let the given device be the first active device BluetoothRouteInfo activeBtRoute = mBluetoothRoutes.get(device.getAddress()); addActiveRoute(activeBtRoute); @@ -376,6 +389,21 @@ class BluetoothRouteProvider { } } } + private void addActiveHearingAidDevices(BluetoothDevice device) { + if (DEBUG) { + Log.d(TAG, "Setting active hearing aid devices. device=" + device); + } + + addActiveDevices(device); + } + + private void addActiveLeAudioDevices(BluetoothDevice device) { + if (DEBUG) { + Log.d(TAG, "Setting active le audio devices. device=" + device); + } + + addActiveDevices(device); + } interface BluetoothRoutesUpdatedListener { void onBluetoothRoutesUpdated(@NonNull List routes); @@ -392,6 +420,11 @@ class BluetoothRouteProvider { if (connectedProfiles.get(BluetoothProfile.HEARING_AID, false)) { return MediaRoute2Info.TYPE_HEARING_AID; } + + if (connectedProfiles.get(BluetoothProfile.LE_AUDIO, false)) { + return MediaRoute2Info.TYPE_BLE_HEADSET; + } + return MediaRoute2Info.TYPE_BLUETOOTH_A2DP; } } @@ -410,6 +443,10 @@ class BluetoothRouteProvider { mHearingAidProfile = (BluetoothHearingAid) proxy; activeDevices = mHearingAidProfile.getActiveDevices(); break; + case BluetoothProfile.LE_AUDIO: + mLeAudioProfile = (BluetoothLeAudio) proxy; + activeDevices = mLeAudioProfile.getActiveDevices(); + break; default: return; } @@ -434,6 +471,9 @@ class BluetoothRouteProvider { case BluetoothProfile.HEARING_AID: mHearingAidProfile = null; break; + case BluetoothProfile.LE_AUDIO: + mLeAudioProfile = null; + break; default: return; } @@ -490,12 +530,22 @@ class BluetoothRouteProvider { } notifyBluetoothRoutesUpdated(); break; + case BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED: + clearActiveRoutesWithType(MediaRoute2Info.TYPE_BLE_HEADSET); + if (device != null) { + addActiveLeAudioDevices(device); + } + notifyBluetoothRoutesUpdated(); + break; case BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED: handleConnectionStateChanged(BluetoothProfile.A2DP, intent, device); break; case BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED: handleConnectionStateChanged(BluetoothProfile.HEARING_AID, intent, device); break; + case BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED: + handleConnectionStateChanged(BluetoothProfile.LE_AUDIO, intent, device); + break; } }