diff options
author | Jakub Pawlowski <jpawlowski@google.com> | 2021-09-03 20:03:15 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2021-09-03 20:03:15 +0000 |
commit | fe2fd0e0ade66217626667157efde0ac66cae0ec (patch) | |
tree | 8026d4d093b3a41bbdb9f5db55df31416b0a6ff9 | |
parent | 1e5f1a293a5c0669f96479e1d137e82ea236a67c (diff) | |
parent | 46aba35609adc153d77d43d597c5f63b41ca0271 (diff) | |
download | base-fe2fd0e0ade66217626667157efde0ac66cae0ec.tar.gz |
Merge "le_audio: Introduce connection state handling for LE Audio group"
6 files changed, 433 insertions, 18 deletions
diff --git a/core/java/android/bluetooth/BluetoothLeAudio.java b/core/java/android/bluetooth/BluetoothLeAudio.java index 75fcab935936..6bbe95e31271 100644 --- a/core/java/android/bluetooth/BluetoothLeAudio.java +++ b/core/java/android/bluetooth/BluetoothLeAudio.java @@ -65,9 +65,6 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. - * - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to - * receive. */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED = @@ -82,9 +79,6 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { * be null if no device is active. </li> * </ul> * - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to - * receive. - * * @hide */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) @@ -92,12 +86,148 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable { "android.bluetooth.action.LE_AUDIO_ACTIVE_DEVICE_CHANGED"; /** + * Intent used to broadcast group node status information. + * + * <p>This intent will have 3 extra: + * <ul> + * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can + * be null if no device is active. </li> + * <li> {@link #EXTRA_LE_AUDIO_GROUP_ID} - Group id. </li> + * <li> {@link #EXTRA_LE_AUDIO_GROUP_NODE_STATUS} - Group node status. </li> + * </ul> + * + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_LE_AUDIO_GROUP_NODE_STATUS_CHANGED = + "android.bluetooth.action.LE_AUDIO_GROUP_NODE_STATUS_CHANGED"; + + + /** + * Intent used to broadcast group status information. + * + * <p>This intent will have 4 extra: + * <ul> + * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can + * be null if no device is active. </li> + * <li> {@link #EXTRA_LE_AUDIO_GROUP_ID} - Group id. </li> + * <li> {@link #EXTRA_LE_AUDIO_GROUP_STATUS} - Group status. </li> + * </ul> + * + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_LE_AUDIO_GROUP_STATUS_CHANGED = + "android.bluetooth.action.LE_AUDIO_GROUP_STATUS_CHANGED"; + + /** + * Intent used to broadcast group audio configuration changed information. + * + * <p>This intent will have 5 extra: + * <ul> + * <li> {@link #EXTRA_LE_AUDIO_GROUP_ID} - Group id. </li> + * <li> {@link #EXTRA_LE_AUDIO_DIRECTION} - Direction as bit mask. </li> + * <li> {@link #EXTRA_LE_AUDIO_SINK_LOCATION} - Sink location as per Bluetooth Assigned + * Numbers </li> + * <li> {@link #EXTRA_LE_AUDIO_SOURCE_LOCATION} - Source location as per Bluetooth Assigned + * Numbers </li> + * <li> {@link #EXTRA_LE_AUDIO_AVAILABLE_CONTEXTS} - Available contexts for group as per + * Bluetooth Assigned Numbers </li> + * </ul> + * + * @hide + */ + @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED) + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_LE_AUDIO_CONF_CHANGED = + "android.bluetooth.action.LE_AUDIO_CONF_CHANGED"; + + /** + * Indicates conversation between humans as, for example, in telephony or video calls. + * @hide + */ + public static final int CONTEXT_TYPE_COMMUNICATION = 0x0002; + + /** + * Indicates media as, for example, in music, public radio, podcast or video soundtrack. + * @hide + */ + public static final int CONTEXT_TYPE_MEDIA = 0x0004; + + /** * This represents an invalid group ID. * * @hide */ public static final int GROUP_ID_INVALID = IBluetoothLeAudio.LE_AUDIO_GROUP_ID_INVALID; + /** + * Contains group id. + * @hide + */ + public static final String EXTRA_LE_AUDIO_GROUP_ID = + "android.bluetooth.extra.LE_AUDIO_GROUP_ID"; + + /** + * Contains group node status, can be any of + * <p> + * <ul> + * <li> {@link #GROUP_NODE_ADDED} </li> + * <li> {@link #GROUP_NODE_REMOVED} </li> + * </ul> + * <p> + * @hide + */ + public static final String EXTRA_LE_AUDIO_GROUP_NODE_STATUS = + "android.bluetooth.extra.LE_AUDIO_GROUP_NODE_STATUS"; + + /** + * Contains group status, can be any of + * + * <p> + * <ul> + * <li> {@link #GROUP_STATUS_IDLE} </li> + * <li> {@link #GROUP_STATUS_STREAMING} </li> + * <li> {@link #GROUP_STATUS_SUSPENDED} </li> + * <li> {@link #GROUP_STATUS_RECONFIGURED} </li> + * <li> {@link #GROUP_STATUS_DESTROYED} </li> + * </ul> + * <p> + * @hide + */ + public static final String EXTRA_LE_AUDIO_GROUP_STATUS = + "android.bluetooth.extra.LE_AUDIO_GROUP_STATUS"; + + /** + * Contains bit mask for direction, bit 0 set when Sink, bit 1 set when Source. + * @hide + */ + public static final String EXTRA_LE_AUDIO_DIRECTION = + "android.bluetooth.extra.LE_AUDIO_DIRECTION"; + + /** + * Contains source location as per Bluetooth Assigned Numbers + * @hide + */ + public static final String EXTRA_LE_AUDIO_SOURCE_LOCATION = + "android.bluetooth.extra.LE_AUDIO_SOURCE_LOCATION"; + + /** + * Contains sink location as per Bluetooth Assigned Numbers + * @hide + */ + public static final String EXTRA_LE_AUDIO_SINK_LOCATION = + "android.bluetooth.extra.LE_AUDIO_SINK_LOCATION"; + + /** + * Contains available context types for group as per Bluetooth Assigned Numbers + * @hide + */ + public static final String EXTRA_LE_AUDIO_AVAILABLE_CONTEXTS = + "android.bluetooth.extra.LE_AUDIO_AVAILABLE_CONTEXTS"; + private BluetoothAdapter mAdapter; private final BluetoothProfileConnector<IBluetoothLeAudio> mProfileConnector = new BluetoothProfileConnector(this, BluetoothProfile.LE_AUDIO, "BluetoothLeAudio", diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index d22e97c231fd..8390ae4c9a50 100755 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -5273,6 +5273,40 @@ public class AudioManager { } } + /** + * Indicate Le Audio output device connection state change and eventually suppress + * the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent. + * @param device Bluetooth device connected/disconnected + * @param state new connection state (BluetoothProfile.STATE_xxx) + * @param suppressNoisyIntent if true the + * {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent will not be sent. + * {@hide} + */ + public void setBluetoothLeAudioOutDeviceConnectionState(BluetoothDevice device, int state, + boolean suppressNoisyIntent) { + final IAudioService service = getService(); + try { + service.setBluetoothLeAudioOutDeviceConnectionState(device, state, suppressNoisyIntent); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** + * Indicate Le Audio input connection state change. + * @param device Bluetooth device connected/disconnected + * @param state new connection state (BluetoothProfile.STATE_xxx) + * {@hide} + */ + public void setBluetoothLeAudioInDeviceConnectionState(BluetoothDevice device, int state) { + final IAudioService service = getService(); + try { + service.setBluetoothLeAudioInDeviceConnectionState(device, state); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + /** * Indicate A2DP source or sink connection state change and eventually suppress * the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent. diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index ed48b569b166..73bc4281df1c 100755 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -256,6 +256,11 @@ interface IAudioService { void setBluetoothHearingAidDeviceConnectionState(in BluetoothDevice device, int state, boolean suppressNoisyIntent, int musicDevice); + void setBluetoothLeAudioOutDeviceConnectionState(in BluetoothDevice device, int state, + boolean suppressNoisyIntent); + + void setBluetoothLeAudioInDeviceConnectionState(in BluetoothDevice device, int state); + void setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(in BluetoothDevice device, int state, int profile, boolean suppressNoisyIntent, int a2dpVolume); diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index cd3d27db37e6..b5e6c11ec7b2 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -563,6 +563,37 @@ import java.util.concurrent.atomic.AtomicBoolean; sendLMsgNoDelay(MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT, SENDMSG_QUEUE, info); } + private static final class LeAudioDeviceConnectionInfo { + final @NonNull BluetoothDevice mDevice; + final @AudioService.BtProfileConnectionState int mState; + final boolean mSupprNoisy; + final @NonNull String mEventSource; + + LeAudioDeviceConnectionInfo(@NonNull BluetoothDevice device, + @AudioService.BtProfileConnectionState int state, + boolean suppressNoisyIntent, @NonNull String eventSource) { + mDevice = device; + mState = state; + mSupprNoisy = suppressNoisyIntent; + mEventSource = eventSource; + } + } + + /*package*/ void postBluetoothLeAudioOutDeviceConnectionState( + @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state, + boolean suppressNoisyIntent, @NonNull String eventSource) { + final LeAudioDeviceConnectionInfo info = new LeAudioDeviceConnectionInfo( + device, state, suppressNoisyIntent, eventSource); + sendLMsgNoDelay(MSG_L_LE_AUDIO_DEVICE_OUT_CONNECTION_CHANGE_EXT, SENDMSG_QUEUE, info); + } + + /*package*/ void postBluetoothLeAudioInDeviceConnectionState( + @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state, + @NonNull String eventSource) { + final LeAudioDeviceConnectionInfo info = new LeAudioDeviceConnectionInfo( + device, state, false, eventSource); + sendLMsgNoDelay(MSG_L_LE_AUDIO_DEVICE_IN_CONNECTION_CHANGE_EXT, SENDMSG_QUEUE, info); + } /** * Current Bluetooth SCO audio active state indicated by BtHelper via setBluetoothScoOn(). @@ -849,6 +880,23 @@ import java.util.concurrent.atomic.AtomicBoolean; delay); } + /*package*/ void postSetLeAudioOutConnectionState( + @AudioService.BtProfileConnectionState int state, + @NonNull BluetoothDevice device, int delay) { + sendILMsg(MSG_IL_SET_LE_AUDIO_OUT_CONNECTION_STATE, SENDMSG_QUEUE, + state, + device, + delay); + } + + /*package*/ void postSetLeAudioInConnectionState( + @AudioService.BtProfileConnectionState int state, + @NonNull BluetoothDevice device) { + sendILMsgNoDelay(MSG_IL_SET_LE_AUDIO_IN_CONNECTION_STATE, SENDMSG_QUEUE, + state, + device); + } + /*package*/ void postDisconnectA2dp() { sendMsgNoDelay(MSG_DISCONNECT_A2DP, SENDMSG_QUEUE); } @@ -1154,7 +1202,20 @@ import java.util.concurrent.atomic.AtomicBoolean; synchronized (mDeviceStateLock) { mDeviceInventory.onSetHearingAidConnectionState( (BluetoothDevice) msg.obj, msg.arg1, - mAudioService.getHearingAidStreamType()); + mAudioService.getBluetoothContextualVolumeStream()); + } + break; + case MSG_IL_SET_LE_AUDIO_OUT_CONNECTION_STATE: + synchronized (mDeviceStateLock) { + mDeviceInventory.onSetLeAudioOutConnectionState( + (BluetoothDevice) msg.obj, msg.arg1, + mAudioService.getBluetoothContextualVolumeStream()); + } + break; + case MSG_IL_SET_LE_AUDIO_IN_CONNECTION_STATE: + synchronized (mDeviceStateLock) { + mDeviceInventory.onSetLeAudioInConnectionState( + (BluetoothDevice) msg.obj, msg.arg1); } break; case MSG_BT_HEADSET_CNCT_FAILED: @@ -1343,6 +1404,31 @@ import java.util.concurrent.atomic.AtomicBoolean; final int capturePreset = msg.arg1; mDeviceInventory.onSaveClearPreferredDevicesForCapturePreset(capturePreset); } break; + case MSG_L_LE_AUDIO_DEVICE_OUT_CONNECTION_CHANGE_EXT: { + final LeAudioDeviceConnectionInfo info = + (LeAudioDeviceConnectionInfo) msg.obj; + AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent( + "setLeAudioDeviceOutConnectionState state=" + info.mState + + " addr=" + info.mDevice.getAddress() + + " supprNoisy=" + info.mSupprNoisy + + " src=" + info.mEventSource)).printLog(TAG)); + synchronized (mDeviceStateLock) { + mDeviceInventory.setBluetoothLeAudioOutDeviceConnectionState( + info.mDevice, info.mState, info.mSupprNoisy); + } + } break; + case MSG_L_LE_AUDIO_DEVICE_IN_CONNECTION_CHANGE_EXT: { + final LeAudioDeviceConnectionInfo info = + (LeAudioDeviceConnectionInfo) msg.obj; + AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent( + "setLeAudioDeviceInConnectionState state=" + info.mState + + " addr=" + info.mDevice.getAddress() + + " src=" + info.mEventSource)).printLog(TAG)); + synchronized (mDeviceStateLock) { + mDeviceInventory.setBluetoothLeAudioInDeviceConnectionState(info.mDevice, + info.mState); + } + } break; default: Log.wtf(TAG, "Invalid message " + msg.what); } @@ -1424,6 +1510,11 @@ import java.util.concurrent.atomic.AtomicBoolean; private static final int MSG_IL_SET_PREF_DEVICES_FOR_STRATEGY = 40; private static final int MSG_I_REMOVE_PREF_DEVICES_FOR_STRATEGY = 41; + private static final int MSG_IL_SET_LE_AUDIO_OUT_CONNECTION_STATE = 42; + 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; + private static boolean isMessageHandledUnderWakelock(int msgId) { switch(msgId) { case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE: @@ -1439,6 +1530,8 @@ import java.util.concurrent.atomic.AtomicBoolean; case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION: case MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT: case MSG_CHECK_MUTE_MUSIC: + case MSG_L_LE_AUDIO_DEVICE_OUT_CONNECTION_CHANGE_EXT: + case MSG_L_LE_AUDIO_DEVICE_IN_CONNECTION_CHANGE_EXT: return true; default: return false; diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java index 82586b8f9b23..78daabc14fb0 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java +++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java @@ -21,6 +21,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.Intent; import android.media.AudioDeviceAttributes; @@ -423,6 +424,45 @@ public class AudioDeviceInventory { } } + /*package*/ void onSetLeAudioConnectionState(BluetoothDevice btDevice, + @AudioService.BtProfileConnectionState int state, int streamType, int device) { + String address = btDevice.getAddress(); + if (!BluetoothAdapter.checkBluetoothAddress(address)) { + address = ""; + } + AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent( + "onSetLeAudioConnectionState addr=" + address)); + + synchronized (mDevicesLock) { + DeviceInfo di = null; + boolean isConnected = false; + + String key = DeviceInfo.makeDeviceListKey(device, btDevice.getAddress()); + di = mConnectedDevices.get(key); + isConnected = di != null; + + if (isConnected && state != BluetoothProfile.STATE_CONNECTED) { + makeLeAudioDeviceUnavailable(address, device); + } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) { + makeLeAudioDeviceAvailable(address, BtHelper.getName(btDevice), streamType, + device, "onSetLeAudioConnectionState"); + } + } + } + + /*package*/ void onSetLeAudioOutConnectionState(BluetoothDevice btDevice, + @AudioService.BtProfileConnectionState int state, int streamType) { + // TODO: b/198610537 clarify DEVICE_OUT_BLE_HEADSET vs DEVICE_OUT_BLE_SPEAKER criteria + onSetLeAudioConnectionState(btDevice, state, streamType, + AudioSystem.DEVICE_OUT_BLE_HEADSET); + } + + /*package*/ void onSetLeAudioInConnectionState(BluetoothDevice btDevice, + @AudioService.BtProfileConnectionState int state) { + onSetLeAudioConnectionState(btDevice, state, AudioSystem.STREAM_DEFAULT, + AudioSystem.DEVICE_IN_BLE_HEADSET); + } + @GuardedBy("AudioDeviceBroker.mDeviceStateLock") /*package*/ void onBluetoothA2dpActiveDeviceChange( @NonNull BtHelper.BluetoothA2dpDeviceInfo btInfo, int event) { @@ -943,6 +983,28 @@ public class AudioDeviceInventory { } } + /*package*/ int setBluetoothLeAudioOutDeviceConnectionState( + @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state, + boolean suppressNoisyIntent) { + synchronized (mDevicesLock) { + /* Active device become null and it's previous device is not connected anymore */ + int delay = 0; + if (!suppressNoisyIntent) { + int intState = (state == BluetoothLeAudio.STATE_CONNECTED) ? 1 : 0; + delay = checkSendBecomingNoisyIntentInt(AudioSystem.DEVICE_OUT_BLE_HEADSET, + intState, AudioSystem.DEVICE_NONE); + } + mDeviceBroker.postSetLeAudioOutConnectionState(state, device, delay); + return delay; + } + } + + /*package*/ void setBluetoothLeAudioInDeviceConnectionState( + @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state) { + synchronized (mDevicesLock) { + mDeviceBroker.postSetLeAudioInConnectionState(state, device); + } + } //------------------------------------------------------------------- // Internal utilities @@ -1115,6 +1177,36 @@ public class AudioDeviceInventory { } @GuardedBy("mDevicesLock") + private void makeLeAudioDeviceAvailable(String address, String name, int streamType, int device, + String eventSource) { + if (device != AudioSystem.DEVICE_NONE) { + AudioSystem.setDeviceConnectionState(device, AudioSystem.DEVICE_STATE_AVAILABLE, + address, name, AudioSystem.AUDIO_FORMAT_DEFAULT); + mConnectedDevices.put(DeviceInfo.makeDeviceListKey(device, address), + new DeviceInfo(device, name, address, AudioSystem.AUDIO_FORMAT_DEFAULT)); + mDeviceBroker.postAccessoryPlugMediaUnmute(device); + } + + if (streamType == AudioSystem.STREAM_DEFAULT) { + // No need to update volume for input devices + return; + } + + mDeviceBroker.postApplyVolumeOnDevice(streamType, device, "makeLeAudioDeviceAvailable"); + } + + @GuardedBy("mDevicesLock") + private void makeLeAudioDeviceUnavailable(String address, int device) { + if (device != AudioSystem.DEVICE_NONE) { + AudioSystem.setDeviceConnectionState(device, AudioSystem.DEVICE_STATE_UNAVAILABLE, + address, "", AudioSystem.AUDIO_FORMAT_DEFAULT); + mConnectedDevices.remove(DeviceInfo.makeDeviceListKey(device, address)); + } + + setCurrentAudioRouteNameIfPossible(null, false /*fromA2dp*/); + } + + @GuardedBy("mDevicesLock") private void setCurrentAudioRouteNameIfPossible(String name, boolean fromA2dp) { synchronized (mCurAudioRoutes) { if (TextUtils.equals(mCurAudioRoutes.bluetoothName, name)) { @@ -1150,6 +1242,7 @@ public class AudioDeviceInventory { BECOMING_NOISY_INTENT_DEVICES_SET.add(AudioSystem.DEVICE_OUT_HEARING_AID); 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); } // must be called before removing the device from mConnectedDevices diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index d0a307932db0..97a20c4928ce 100755 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -163,6 +163,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Arrays; +import java.util.BitSet; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; @@ -307,6 +308,12 @@ public class AudioService extends IAudioService.Stub // retry delay in case of failure to indicate system ready to AudioFlinger private static final int INDICATE_SYSTEM_READY_RETRY_DELAY_MS = 1000; + // Bits representing connected devices to monitor playback monitor voice activity + private static final int CONNECTED_STATE_HEARING_AID_BIT = 0; + private static final int CONNECTED_STATE_LE_AUDIO_BIT = 1; + + private BitSet mConnectedStateBitset = new BitSet(2); + /** @see AudioSystemThread */ private AudioSystemThread mAudioSystemThread; /** @see AudioHandler */ @@ -2529,7 +2536,7 @@ public class AudioService extends IAudioService.Stub if (device == AudioSystem.DEVICE_OUT_HEARING_AID) { // only modify the hearing aid attenuation when the stream to modify matches // the one expected by the hearing aid - if (streamType == getHearingAidStreamType()) { + if (streamType == getBluetoothContextualVolumeStream()) { if (DEBUG_VOL) { Log.d(TAG, "adjustSreamVolume postSetHearingAidVolumeIndex index=" + newIndex + " stream=" + streamType); @@ -2872,11 +2879,11 @@ public class AudioService extends IAudioService.Stub } } - /*package*/ int getHearingAidStreamType() { - return getHearingAidStreamType(mMode); + /*package*/ int getBluetoothContextualVolumeStream() { + return getBluetoothContextualVolumeStream(mMode); } - private int getHearingAidStreamType(int mode) { + private int getBluetoothContextualVolumeStream(int mode) { switch (mode) { case AudioSystem.MODE_IN_COMMUNICATION: case AudioSystem.MODE_IN_CALL: @@ -2922,7 +2929,7 @@ public class AudioService extends IAudioService.Stub } private void updateHearingAidVolumeOnVoiceActivityUpdate() { - final int streamType = getHearingAidStreamType(); + final int streamType = getBluetoothContextualVolumeStream(); final int index = getStreamVolume(streamType); sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_VOICE_ACTIVITY_HEARING_AID, mVoiceActive.get(), streamType, index)); @@ -2954,7 +2961,7 @@ public class AudioService extends IAudioService.Stub return; } - int streamType = getHearingAidStreamType(newMode); + int streamType = getBluetoothContextualVolumeStream(newMode); final Set<Integer> deviceTypes = AudioSystem.generateAudioDeviceTypesSet( AudioSystem.getDevicesForStream(streamType)); @@ -3036,7 +3043,7 @@ public class AudioService extends IAudioService.Stub } if (device == AudioSystem.DEVICE_OUT_HEARING_AID - && streamType == getHearingAidStreamType()) { + && streamType == getBluetoothContextualVolumeStream()) { Log.i(TAG, "setStreamVolume postSetHearingAidVolumeIndex index=" + index + " stream=" + streamType); mDeviceBroker.postSetHearingAidVolumeIndex(index, streamType); @@ -5496,15 +5503,68 @@ public class AudioService extends IAudioService.Stub throw new IllegalArgumentException("Illegal BluetoothProfile state for device " + " (dis)connection, got " + state); } - if (state == BluetoothProfile.STATE_CONNECTED) { - mPlaybackMonitor.registerPlaybackCallback(mVoiceActivityMonitor, true); - } else { - mPlaybackMonitor.unregisterPlaybackCallback(mVoiceActivityMonitor); + synchronized (mConnectedStateBitset) { + if (state == BluetoothProfile.STATE_CONNECTED) { + if (mConnectedStateBitset.isEmpty()) { + mPlaybackMonitor.registerPlaybackCallback(mVoiceActivityMonitor, true); + } + mConnectedStateBitset.set(CONNECTED_STATE_HEARING_AID_BIT); + } else { + mConnectedStateBitset.clear(CONNECTED_STATE_HEARING_AID_BIT); + if (mConnectedStateBitset.isEmpty()) { + mPlaybackMonitor.unregisterPlaybackCallback(mVoiceActivityMonitor); + } + } } mDeviceBroker.postBluetoothHearingAidDeviceConnectionState( device, state, suppressNoisyIntent, musicDevice, "AudioService"); } + private void setBluetoothLeAudioDeviceConnectionState(@NonNull BluetoothDevice device, + @BtProfileConnectionState int state) { + if (device == null) { + throw new IllegalArgumentException("Illegal null device"); + } + if (state != BluetoothProfile.STATE_CONNECTED + && state != BluetoothProfile.STATE_DISCONNECTED) { + throw new IllegalArgumentException("Illegal BluetoothProfile state for device " + + " (dis)connection, got " + state); + } + synchronized (mConnectedStateBitset) { + if (state == BluetoothProfile.STATE_CONNECTED) { + if (mConnectedStateBitset.isEmpty()) { + mPlaybackMonitor.registerPlaybackCallback(mVoiceActivityMonitor, true); + } + mConnectedStateBitset.set(CONNECTED_STATE_LE_AUDIO_BIT); + } else { + mConnectedStateBitset.clear(CONNECTED_STATE_LE_AUDIO_BIT); + if (mConnectedStateBitset.isEmpty()) { + mPlaybackMonitor.unregisterPlaybackCallback(mVoiceActivityMonitor); + } + } + } + } + + /** + * See AudioManager.setBluetoothLeAudioOutDeviceConnectionState() + */ + public void setBluetoothLeAudioOutDeviceConnectionState( + @NonNull BluetoothDevice device, @BtProfileConnectionState int state, + boolean suppressNoisyIntent) { + setBluetoothLeAudioDeviceConnectionState(device, state); + mDeviceBroker.postBluetoothLeAudioOutDeviceConnectionState(device, state, + suppressNoisyIntent, "AudioService"); + } + + /** + * See AudioManager.setBluetoothLeAudioInDeviceConnectionState() + */ + public void setBluetoothLeAudioInDeviceConnectionState( + @NonNull BluetoothDevice device, @BtProfileConnectionState int state) { + setBluetoothLeAudioDeviceConnectionState(device, state); + mDeviceBroker.postBluetoothLeAudioInDeviceConnectionState(device, state, "AudioService"); + } + /** * See AudioManager.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent() */ |