diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java index 282a12da6bb8e..96bb73f3107ce 100644 --- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java +++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java @@ -503,7 +503,7 @@ import java.util.concurrent.atomic.AtomicBoolean; } } - private static final class BtDeviceConnectionInfo { + /*package*/ static final class BtDeviceConnectionInfo { final @NonNull BluetoothDevice mDevice; final @AudioService.BtProfileConnectionState int mState; final int mProfile; @@ -520,6 +520,14 @@ import java.util.concurrent.atomic.AtomicBoolean; mVolume = vol; } + BtDeviceConnectionInfo(@NonNull BtDeviceConnectionInfo info) { + mDevice = info.mDevice; + mState = info.mState; + mProfile = info.mProfile; + mSupprNoisy = info.mSupprNoisy; + mVolume = info.mVolume; + } + // redefine equality op so we can match messages intended for this device @Override public boolean equals(Object o) { @@ -541,18 +549,19 @@ import java.util.concurrent.atomic.AtomicBoolean; } } - /*package*/ void postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( - @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state, - int profile, boolean suppressNoisyIntent, int a2dpVolume) { - final BtDeviceConnectionInfo info = new BtDeviceConnectionInfo(device, state, profile, - suppressNoisyIntent, a2dpVolume); - - final String name = TextUtils.emptyIfNull(device.getName()); + /** + * will block on mDeviceStateLock, which is held during an A2DP (dis) connection + * not just a simple message post + * @param info struct with the (dis)connection information + */ + /*package*/ void queueBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( + @NonNull BtDeviceConnectionInfo info) { + final String name = TextUtils.emptyIfNull(info.mDevice.getName()); new MediaMetrics.Item(MediaMetrics.Name.AUDIO_DEVICE + MediaMetrics.SEPARATOR + "postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent") - .set(MediaMetrics.Property.STATE, state == BluetoothProfile.STATE_CONNECTED + .set(MediaMetrics.Property.STATE, info.mState == BluetoothProfile.STATE_CONNECTED ? MediaMetrics.Value.CONNECTED : MediaMetrics.Value.DISCONNECTED) - .set(MediaMetrics.Property.INDEX, a2dpVolume) + .set(MediaMetrics.Property.INDEX, info.mVolume) .set(MediaMetrics.Property.NAME, name) .record(); @@ -562,10 +571,10 @@ import java.util.concurrent.atomic.AtomicBoolean; // when receiving a request to change the connection state of a device, this last // request is the source of truth, so cancel all previous requests that are already in // the handler - removeScheduledA2dpEvents(device); + removeScheduledA2dpEvents(info.mDevice); sendLMsgNoDelay( - state == BluetoothProfile.STATE_CONNECTED + info.mState == BluetoothProfile.STATE_CONNECTED ? MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION : MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION, SENDMSG_QUEUE, info); diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 2de2fdf1f188a..8508cebbca678 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -310,6 +310,8 @@ public class AudioService extends IAudioService.Stub private static final int MSG_UPDATE_A11Y_SERVICE_UIDS = 35; private static final int MSG_UPDATE_AUDIO_MODE = 36; private static final int MSG_RECORDING_CONFIG_CHANGE = 37; + private static final int MSG_SET_A2DP_DEV_CONNECTION_STATE = 38; + private static final int MSG_A2DP_DEV_CONFIG_CHANGE = 39; // start of messages handled under wakelock // these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(), @@ -6074,7 +6076,7 @@ public class AudioService extends IAudioService.Stub * See AudioManager.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent() */ public void setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( - @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state, + @NonNull BluetoothDevice device, @BtProfileConnectionState int state, int profile, boolean suppressNoisyIntent, int a2dpVolume) { if (device == null) { throw new IllegalArgumentException("Illegal null device"); @@ -6084,8 +6086,13 @@ public class AudioService extends IAudioService.Stub throw new IllegalArgumentException("Illegal BluetoothProfile state for device " + " (dis)connection, got " + state); } - mDeviceBroker.postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(device, state, - profile, suppressNoisyIntent, a2dpVolume); + + AudioDeviceBroker.BtDeviceConnectionInfo info = + new AudioDeviceBroker.BtDeviceConnectionInfo(device, state, + profile, suppressNoisyIntent, a2dpVolume); + sendMsg(mAudioHandler, MSG_SET_A2DP_DEV_CONNECTION_STATE, SENDMSG_QUEUE, + 0 /*arg1*/, 0 /*arg2*/, + /*obj*/ info, 0 /*delay*/); } /** only public for mocking/spying, do not call outside of AudioService */ @@ -6103,7 +6110,8 @@ public class AudioService extends IAudioService.Stub if (device == null) { throw new IllegalArgumentException("Illegal null device"); } - mDeviceBroker.postBluetoothA2dpDeviceConfigChange(device); + sendMsg(mAudioHandler, MSG_A2DP_DEV_CONFIG_CHANGE, SENDMSG_QUEUE, 0, 0, + /*obj*/ device, /*delay*/ 0); } private static final Set DEVICE_MEDIA_UNMUTED_ON_PLUG_SET; @@ -7465,6 +7473,15 @@ public class AudioService extends IAudioService.Stub onUpdateAudioMode(msg.arg1, msg.arg2, (String) msg.obj, false /*force*/); } break; + + case MSG_SET_A2DP_DEV_CONNECTION_STATE: + mDeviceBroker.queueBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( + (AudioDeviceBroker.BtDeviceConnectionInfo) msg.obj); + break; + + case MSG_A2DP_DEV_CONFIG_CHANGE: + mDeviceBroker.postBluetoothA2dpDeviceConfigChange((BluetoothDevice) msg.obj); + break; } } } diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java index c57d5afa5f0ca..52e8edff5ffa8 100644 --- a/services/core/java/com/android/server/audio/BtHelper.java +++ b/services/core/java/com/android/server/audio/BtHelper.java @@ -454,8 +454,10 @@ public class BtHelper { } final BluetoothDevice btDevice = deviceList.get(0); // the device is guaranteed CONNECTED - mDeviceBroker.postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(btDevice, - BluetoothA2dp.STATE_CONNECTED, BluetoothProfile.A2DP_SINK, true, -1); + mDeviceBroker.queueBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( + new AudioDeviceBroker.BtDeviceConnectionInfo(btDevice, + BluetoothA2dp.STATE_CONNECTED, BluetoothProfile.A2DP_SINK, + true, -1)); } /*package*/ synchronized void onA2dpSinkProfileConnected(BluetoothProfile profile) { diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java index 79a5ed65b999b..5c53d43fa1df3 100644 --- a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java +++ b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java @@ -98,8 +98,9 @@ public class AudioDeviceBrokerTest { Log.i(TAG, "starting testPostA2dpDeviceConnectionChange"); Assert.assertNotNull("invalid null BT device", mFakeBtDevice); - mAudioDeviceBroker.postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(mFakeBtDevice, - BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP, true, 1); + mAudioDeviceBroker.queueBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( + new AudioDeviceBroker.BtDeviceConnectionInfo(mFakeBtDevice, + BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP, true, 1)); Thread.sleep(2 * MAX_MESSAGE_HANDLING_DELAY_MS); verify(mSpyDevInventory, times(1)).setBluetoothA2dpDeviceConnectionState( any(BluetoothDevice.class), @@ -209,20 +210,23 @@ public class AudioDeviceBrokerTest { ((NoOpAudioSystemAdapter) mSpyAudioSystem).configureIsStreamActive(mockMediaPlayback); // first connection: ensure the device is connected as a starting condition for the test - mAudioDeviceBroker.postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(mFakeBtDevice, - BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP, true, 1); + mAudioDeviceBroker.queueBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( + new AudioDeviceBroker.BtDeviceConnectionInfo(mFakeBtDevice, + BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP, true, 1)); Thread.sleep(MAX_MESSAGE_HANDLING_DELAY_MS); // disconnection - mAudioDeviceBroker.postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(mFakeBtDevice, - BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.A2DP, false, -1); + mAudioDeviceBroker.queueBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( + new AudioDeviceBroker.BtDeviceConnectionInfo(mFakeBtDevice, + BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.A2DP, false, -1)); if (delayAfterDisconnection > 0) { Thread.sleep(delayAfterDisconnection); } // reconnection - mAudioDeviceBroker.postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(mFakeBtDevice, - BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP, true, 2); + mAudioDeviceBroker.queueBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent( + new AudioDeviceBroker.BtDeviceConnectionInfo(mFakeBtDevice, + BluetoothProfile.STATE_CONNECTED, BluetoothProfile.A2DP, true, 2)); Thread.sleep(AudioService.BECOMING_NOISY_DELAY_MS + MAX_MESSAGE_HANDLING_DELAY_MS); // Verify disconnection has been cancelled and we're seeing two connections attempts,