Merge "le_audio: Introduce connection state handling for LE Audio group"

This commit is contained in:
Jakub Pawlowski
2021-09-03 20:03:15 +00:00
committed by Gerrit Code Review
6 changed files with 433 additions and 18 deletions

View File

@@ -65,9 +65,6 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
* <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
* {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
* {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
* receive.
*/ */
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED = public static final String ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED =
@@ -82,15 +79,83 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
* be null if no device is active. </li> * be null if no device is active. </li>
* </ul> * </ul>
* *
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
* receive.
*
* @hide * @hide
*/ */
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED = public static final String ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED =
"android.bluetooth.action.LE_AUDIO_ACTIVE_DEVICE_CHANGED"; "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. * This represents an invalid group ID.
* *
@@ -98,6 +163,71 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
*/ */
public static final int GROUP_ID_INVALID = IBluetoothLeAudio.LE_AUDIO_GROUP_ID_INVALID; 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 BluetoothAdapter mAdapter;
private final BluetoothProfileConnector<IBluetoothLeAudio> mProfileConnector = private final BluetoothProfileConnector<IBluetoothLeAudio> mProfileConnector =
new BluetoothProfileConnector(this, BluetoothProfile.LE_AUDIO, "BluetoothLeAudio", new BluetoothProfileConnector(this, BluetoothProfile.LE_AUDIO, "BluetoothLeAudio",

View File

@@ -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 * Indicate A2DP source or sink connection state change and eventually suppress
* the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent. * the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent.

View File

@@ -256,6 +256,11 @@ interface IAudioService {
void setBluetoothHearingAidDeviceConnectionState(in BluetoothDevice device, void setBluetoothHearingAidDeviceConnectionState(in BluetoothDevice device,
int state, boolean suppressNoisyIntent, int musicDevice); 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, void setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(in BluetoothDevice device,
int state, int profile, boolean suppressNoisyIntent, int a2dpVolume); int state, int profile, boolean suppressNoisyIntent, int a2dpVolume);

View File

@@ -563,6 +563,37 @@ import java.util.concurrent.atomic.AtomicBoolean;
sendLMsgNoDelay(MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT, SENDMSG_QUEUE, info); 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(). * Current Bluetooth SCO audio active state indicated by BtHelper via setBluetoothScoOn().
@@ -849,6 +880,23 @@ import java.util.concurrent.atomic.AtomicBoolean;
delay); 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() { /*package*/ void postDisconnectA2dp() {
sendMsgNoDelay(MSG_DISCONNECT_A2DP, SENDMSG_QUEUE); sendMsgNoDelay(MSG_DISCONNECT_A2DP, SENDMSG_QUEUE);
} }
@@ -1154,7 +1202,20 @@ import java.util.concurrent.atomic.AtomicBoolean;
synchronized (mDeviceStateLock) { synchronized (mDeviceStateLock) {
mDeviceInventory.onSetHearingAidConnectionState( mDeviceInventory.onSetHearingAidConnectionState(
(BluetoothDevice) msg.obj, msg.arg1, (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; break;
case MSG_BT_HEADSET_CNCT_FAILED: case MSG_BT_HEADSET_CNCT_FAILED:
@@ -1343,6 +1404,31 @@ import java.util.concurrent.atomic.AtomicBoolean;
final int capturePreset = msg.arg1; final int capturePreset = msg.arg1;
mDeviceInventory.onSaveClearPreferredDevicesForCapturePreset(capturePreset); mDeviceInventory.onSaveClearPreferredDevicesForCapturePreset(capturePreset);
} break; } 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: default:
Log.wtf(TAG, "Invalid message " + msg.what); 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_IL_SET_PREF_DEVICES_FOR_STRATEGY = 40;
private static final int MSG_I_REMOVE_PREF_DEVICES_FOR_STRATEGY = 41; 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) { private static boolean isMessageHandledUnderWakelock(int msgId) {
switch(msgId) { switch(msgId) {
case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE: 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_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION:
case MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT: case MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT:
case MSG_CHECK_MUTE_MUSIC: 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; return true;
default: default:
return false; return false;

View File

@@ -21,6 +21,7 @@ import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHearingAid; import android.bluetooth.BluetoothHearingAid;
import android.bluetooth.BluetoothLeAudio;
import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothProfile;
import android.content.Intent; import android.content.Intent;
import android.media.AudioDeviceAttributes; 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") @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ void onBluetoothA2dpActiveDeviceChange( /*package*/ void onBluetoothA2dpActiveDeviceChange(
@NonNull BtHelper.BluetoothA2dpDeviceInfo btInfo, int event) { @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 // Internal utilities
@@ -1114,6 +1176,36 @@ public class AudioDeviceInventory {
.record(); .record();
} }
@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") @GuardedBy("mDevicesLock")
private void setCurrentAudioRouteNameIfPossible(String name, boolean fromA2dp) { private void setCurrentAudioRouteNameIfPossible(String name, boolean fromA2dp) {
synchronized (mCurAudioRoutes) { synchronized (mCurAudioRoutes) {
@@ -1150,6 +1242,7 @@ public class AudioDeviceInventory {
BECOMING_NOISY_INTENT_DEVICES_SET.add(AudioSystem.DEVICE_OUT_HEARING_AID); 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_A2DP_SET);
BECOMING_NOISY_INTENT_DEVICES_SET.addAll(AudioSystem.DEVICE_OUT_ALL_USB_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 // must be called before removing the device from mConnectedDevices

View File

@@ -163,6 +163,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.BitSet;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; 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 // retry delay in case of failure to indicate system ready to AudioFlinger
private static final int INDICATE_SYSTEM_READY_RETRY_DELAY_MS = 1000; 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 */ /** @see AudioSystemThread */
private AudioSystemThread mAudioSystemThread; private AudioSystemThread mAudioSystemThread;
/** @see AudioHandler */ /** @see AudioHandler */
@@ -2529,7 +2536,7 @@ public class AudioService extends IAudioService.Stub
if (device == AudioSystem.DEVICE_OUT_HEARING_AID) { if (device == AudioSystem.DEVICE_OUT_HEARING_AID) {
// only modify the hearing aid attenuation when the stream to modify matches // only modify the hearing aid attenuation when the stream to modify matches
// the one expected by the hearing aid // the one expected by the hearing aid
if (streamType == getHearingAidStreamType()) { if (streamType == getBluetoothContextualVolumeStream()) {
if (DEBUG_VOL) { if (DEBUG_VOL) {
Log.d(TAG, "adjustSreamVolume postSetHearingAidVolumeIndex index=" Log.d(TAG, "adjustSreamVolume postSetHearingAidVolumeIndex index="
+ newIndex + " stream=" + streamType); + newIndex + " stream=" + streamType);
@@ -2872,11 +2879,11 @@ public class AudioService extends IAudioService.Stub
} }
} }
/*package*/ int getHearingAidStreamType() { /*package*/ int getBluetoothContextualVolumeStream() {
return getHearingAidStreamType(mMode); return getBluetoothContextualVolumeStream(mMode);
} }
private int getHearingAidStreamType(int mode) { private int getBluetoothContextualVolumeStream(int mode) {
switch (mode) { switch (mode) {
case AudioSystem.MODE_IN_COMMUNICATION: case AudioSystem.MODE_IN_COMMUNICATION:
case AudioSystem.MODE_IN_CALL: case AudioSystem.MODE_IN_CALL:
@@ -2922,7 +2929,7 @@ public class AudioService extends IAudioService.Stub
} }
private void updateHearingAidVolumeOnVoiceActivityUpdate() { private void updateHearingAidVolumeOnVoiceActivityUpdate() {
final int streamType = getHearingAidStreamType(); final int streamType = getBluetoothContextualVolumeStream();
final int index = getStreamVolume(streamType); final int index = getStreamVolume(streamType);
sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_VOICE_ACTIVITY_HEARING_AID, sVolumeLogger.log(new VolumeEvent(VolumeEvent.VOL_VOICE_ACTIVITY_HEARING_AID,
mVoiceActive.get(), streamType, index)); mVoiceActive.get(), streamType, index));
@@ -2954,7 +2961,7 @@ public class AudioService extends IAudioService.Stub
return; return;
} }
int streamType = getHearingAidStreamType(newMode); int streamType = getBluetoothContextualVolumeStream(newMode);
final Set<Integer> deviceTypes = AudioSystem.generateAudioDeviceTypesSet( final Set<Integer> deviceTypes = AudioSystem.generateAudioDeviceTypesSet(
AudioSystem.getDevicesForStream(streamType)); AudioSystem.getDevicesForStream(streamType));
@@ -3036,7 +3043,7 @@ public class AudioService extends IAudioService.Stub
} }
if (device == AudioSystem.DEVICE_OUT_HEARING_AID if (device == AudioSystem.DEVICE_OUT_HEARING_AID
&& streamType == getHearingAidStreamType()) { && streamType == getBluetoothContextualVolumeStream()) {
Log.i(TAG, "setStreamVolume postSetHearingAidVolumeIndex index=" + index Log.i(TAG, "setStreamVolume postSetHearingAidVolumeIndex index=" + index
+ " stream=" + streamType); + " stream=" + streamType);
mDeviceBroker.postSetHearingAidVolumeIndex(index, streamType); mDeviceBroker.postSetHearingAidVolumeIndex(index, streamType);
@@ -5496,15 +5503,68 @@ public class AudioService extends IAudioService.Stub
throw new IllegalArgumentException("Illegal BluetoothProfile state for device " throw new IllegalArgumentException("Illegal BluetoothProfile state for device "
+ " (dis)connection, got " + state); + " (dis)connection, got " + state);
} }
if (state == BluetoothProfile.STATE_CONNECTED) { synchronized (mConnectedStateBitset) {
mPlaybackMonitor.registerPlaybackCallback(mVoiceActivityMonitor, true); if (state == BluetoothProfile.STATE_CONNECTED) {
} else { if (mConnectedStateBitset.isEmpty()) {
mPlaybackMonitor.unregisterPlaybackCallback(mVoiceActivityMonitor); 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( mDeviceBroker.postBluetoothHearingAidDeviceConnectionState(
device, state, suppressNoisyIntent, musicDevice, "AudioService"); 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() * See AudioManager.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent()
*/ */