AudioService: change to BT service profile listener events

Put the BT service profile listener events on the AudioDeviceBroker
  event thread to ensure correct locking order.

Bug: 112863932
Test: test enabling / disabling of BT with devices connected
Change-Id: I10a79673c7e7ef1dc6250beb8e781f3de242d248
This commit is contained in:
Jean-Michel Trivi
2019-02-07 12:08:39 -08:00
parent 74d6997124
commit 43ff4f3aba
2 changed files with 144 additions and 88 deletions

View File

@@ -21,6 +21,8 @@ import static com.android.server.audio.AudioService.CONNECTION_STATE_DISCONNECTE
import android.annotation.NonNull;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothHearingAid;
import android.bluetooth.BluetoothProfile;
import android.content.ContentResolver;
import android.content.Context;
@@ -424,11 +426,28 @@ import java.util.ArrayList;
}
/*package*/ void postDisconnectHearingAid() {
sendMsgNoDelay(MSG_DISCONNECT_HEARING_AID, SENDMSG_QUEUE);
sendMsgNoDelay(MSG_DISCONNECT_BT_HEARING_AID, SENDMSG_QUEUE);
}
/*package*/ void postDisconnectHeadset() {
sendMsgNoDelay(MSG_DISCONNECT_HEADSET, SENDMSG_QUEUE);
sendMsgNoDelay(MSG_DISCONNECT_BT_HEADSET, SENDMSG_QUEUE);
}
/*package*/ void postBtA2dpProfileConnected(BluetoothA2dp a2dpProfile) {
sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP, SENDMSG_QUEUE, a2dpProfile);
}
/*package*/ void postBtA2dpSinkProfileConnected(BluetoothProfile profile) {
sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP_SINK, SENDMSG_QUEUE, profile);
}
/*package*/ void postBtHeasetProfileConnected(BluetoothHeadset headsetProfile) {
sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEADSET, SENDMSG_QUEUE, headsetProfile);
}
/*package*/ void postBtHearingAidProfileConnected(BluetoothHearingAid hearingAidProfile) {
sendLMsgNoDelay(MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEARING_AID, SENDMSG_QUEUE,
hearingAidProfile);
}
//---------------------------------------------------------------------
@@ -460,16 +479,14 @@ import java.util.ArrayList;
}
}
@GuardedBy("mDeviceStateLock")
/*package*/ void handleSetA2dpSinkConnectionState(@BluetoothProfile.BtProfileState int state,
@NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo) {
final int intState = (state == BluetoothA2dp.STATE_CONNECTED)
? CONNECTION_STATE_CONNECTED : CONNECTION_STATE_DISCONNECTED;
final int delay;
synchronized (mDeviceStateLock) {
delay = mDeviceInventory.checkSendBecomingNoisyIntent(
final int delay = mDeviceInventory.checkSendBecomingNoisyIntent(
AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, intState,
AudioSystem.DEVICE_NONE);
}
final String addr = btDeviceInfo == null ? "null" : btDeviceInfo.getBtDevice().getAddress();
if (AudioService.DEBUG_DEVICES) {
@@ -719,16 +736,36 @@ import java.util.ArrayList;
mDeviceInventory.disconnectA2dpSink();
}
break;
case MSG_DISCONNECT_HEARING_AID:
case MSG_DISCONNECT_BT_HEARING_AID:
synchronized (mDeviceStateLock) {
mDeviceInventory.disconnectHearingAid();
}
break;
case MSG_DISCONNECT_HEADSET:
case MSG_DISCONNECT_BT_HEADSET:
synchronized (mDeviceStateLock) {
mBtHelper.disconnectHeadset();
}
break;
case MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP:
synchronized (mDeviceStateLock) {
mBtHelper.onA2dpProfileConnected((BluetoothA2dp) msg.obj);
}
break;
case MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP_SINK:
synchronized (mDeviceStateLock) {
mBtHelper.onA2dpSinkProfileConnected((BluetoothProfile) msg.obj);
}
break;
case MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEARING_AID:
synchronized (mDeviceStateLock) {
mBtHelper.onHearingAidProfileConnected((BluetoothHearingAid) msg.obj);
}
break;
case MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEADSET:
synchronized (mDeviceStateLock) {
mBtHelper.onHeadsetProfileConnected((BluetoothHeadset) msg.obj);
}
break;
default:
Log.wtf(TAG, "Invalid message " + msg.what);
}
@@ -766,8 +803,12 @@ import java.util.ArrayList;
private static final int MSG_L_A2DP_ACTIVE_DEVICE_CHANGE = 18;
private static final int MSG_DISCONNECT_A2DP = 19;
private static final int MSG_DISCONNECT_A2DP_SINK = 20;
private static final int MSG_DISCONNECT_HEARING_AID = 21;
private static final int MSG_DISCONNECT_HEADSET = 22;
private static final int MSG_DISCONNECT_BT_HEARING_AID = 21;
private static final int MSG_DISCONNECT_BT_HEADSET = 22;
private static final int MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP = 23;
private static final int MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP_SINK = 24;
private static final int MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEARING_AID = 25;
private static final int MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEADSET = 26;
private static boolean isMessageHandledUnderWakelock(int msgId) {

View File

@@ -393,6 +393,84 @@ public class BtHelper {
mBluetoothHeadset = null;
}
/*package*/ synchronized void onA2dpProfileConnected(BluetoothA2dp a2dp) {
mA2dp = a2dp;
final List<BluetoothDevice> deviceList = mA2dp.getConnectedDevices();
if (deviceList.isEmpty()) {
return;
}
final BluetoothDevice btDevice = deviceList.get(0);
final @BluetoothProfile.BtProfileState int state = mA2dp.getConnectionState(btDevice);
mDeviceBroker.handleSetA2dpSinkConnectionState(
state, new BluetoothA2dpDeviceInfo(btDevice));
}
/*package*/ synchronized void onA2dpSinkProfileConnected(BluetoothProfile profile) {
final List<BluetoothDevice> deviceList = profile.getConnectedDevices();
if (deviceList.isEmpty()) {
return;
}
final BluetoothDevice btDevice = deviceList.get(0);
final @BluetoothProfile.BtProfileState int state =
profile.getConnectionState(btDevice);
mDeviceBroker.postSetA2dpSourceConnectionState(
state, new BluetoothA2dpDeviceInfo(btDevice));
}
/*package*/ synchronized void onHearingAidProfileConnected(BluetoothHearingAid hearingAid) {
mHearingAid = hearingAid;
final List<BluetoothDevice> deviceList = mHearingAid.getConnectedDevices();
if (deviceList.isEmpty()) {
return;
}
final BluetoothDevice btDevice = deviceList.get(0);
final @BluetoothProfile.BtProfileState int state =
mHearingAid.getConnectionState(btDevice);
mDeviceBroker.setBluetoothHearingAidDeviceConnectionState(
btDevice, state,
/*suppressNoisyIntent*/ false,
/*musicDevice*/ android.media.AudioSystem.DEVICE_NONE,
/*eventSource*/ "mBluetoothProfileServiceListener");
}
/*package*/ synchronized void onHeadsetProfileConnected(BluetoothHeadset headset) {
// Discard timeout message
mDeviceBroker.handleCancelFailureToConnectToBtHeadsetService();
mBluetoothHeadset = headset;
setBtScoActiveDevice(mBluetoothHeadset.getActiveDevice());
// Refresh SCO audio state
checkScoAudioState();
if (mScoAudioState != SCO_STATE_ACTIVATE_REQ
&& mScoAudioState != SCO_STATE_DEACTIVATE_REQ) {
return;
}
boolean status = false;
if (mBluetoothHeadsetDevice != null) {
switch (mScoAudioState) {
case SCO_STATE_ACTIVATE_REQ:
status = connectBluetoothScoAudioHelper(
mBluetoothHeadset,
mBluetoothHeadsetDevice, mScoAudioMode);
if (status) {
mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
}
break;
case SCO_STATE_DEACTIVATE_REQ:
status = disconnectBluetoothScoAudioHelper(
mBluetoothHeadset,
mBluetoothHeadsetDevice, mScoAudioMode);
if (status) {
mScoAudioState = SCO_STATE_DEACTIVATING;
}
break;
}
}
if (!status) {
mScoAudioState = SCO_STATE_INACTIVE;
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
}
}
//----------------------------------------------------------------------
private void broadcastScoConnectionState(int state) {
mDeviceBroker.postBroadcastScoConnectionState(state);
@@ -462,99 +540,36 @@ public class BtHelper {
}
}
// NOTE this listener is NOT called from AudioDeviceBroker event thread, only call async
// methods inside listener.
private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
new BluetoothProfile.ServiceListener() {
public void onServiceConnected(int profile, BluetoothProfile proxy) {
final BluetoothDevice btDevice;
List<BluetoothDevice> deviceList;
switch(profile) {
case BluetoothProfile.A2DP:
synchronized (BtHelper.this) {
mA2dp = (BluetoothA2dp) proxy;
deviceList = mA2dp.getConnectedDevices();
if (deviceList.size() > 0) {
btDevice = deviceList.get(0);
if (btDevice == null) {
Log.e(TAG, "Invalid null device in BT profile listener");
return;
}
final @BluetoothProfile.BtProfileState int state =
mA2dp.getConnectionState(btDevice);
mDeviceBroker.handleSetA2dpSinkConnectionState(
state, new BluetoothA2dpDeviceInfo(btDevice));
}
}
AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
"BT profile service: connecting A2DP profile"));
mDeviceBroker.postBtA2dpProfileConnected((BluetoothA2dp) proxy);
break;
case BluetoothProfile.A2DP_SINK:
deviceList = proxy.getConnectedDevices();
if (deviceList.size() > 0) {
btDevice = deviceList.get(0);
final @BluetoothProfile.BtProfileState int state =
proxy.getConnectionState(btDevice);
mDeviceBroker.postSetA2dpSourceConnectionState(
state, new BluetoothA2dpDeviceInfo(btDevice));
}
AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
"BT profile service: connecting A2DP_SINK profile"));
mDeviceBroker.postBtA2dpSinkProfileConnected(proxy);
break;
case BluetoothProfile.HEADSET:
synchronized (BtHelper.this) {
// Discard timeout message
mDeviceBroker.handleCancelFailureToConnectToBtHeadsetService();
mBluetoothHeadset = (BluetoothHeadset) proxy;
setBtScoActiveDevice(mBluetoothHeadset.getActiveDevice());
// Refresh SCO audio state
checkScoAudioState();
// Continue pending action if any
if (mScoAudioState == SCO_STATE_ACTIVATE_REQ
|| mScoAudioState == SCO_STATE_DEACTIVATE_REQ) {
boolean status = false;
if (mBluetoothHeadsetDevice != null) {
switch (mScoAudioState) {
case SCO_STATE_ACTIVATE_REQ:
status = connectBluetoothScoAudioHelper(
mBluetoothHeadset,
mBluetoothHeadsetDevice, mScoAudioMode);
if (status) {
mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
}
break;
case SCO_STATE_DEACTIVATE_REQ:
status = disconnectBluetoothScoAudioHelper(
mBluetoothHeadset,
mBluetoothHeadsetDevice, mScoAudioMode);
if (status) {
mScoAudioState = SCO_STATE_DEACTIVATING;
}
break;
}
}
if (!status) {
mScoAudioState = SCO_STATE_INACTIVE;
broadcastScoConnectionState(
AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
}
}
}
AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
"BT profile service: connecting HEADSET profile"));
mDeviceBroker.postBtHeasetProfileConnected((BluetoothHeadset) proxy);
break;
case BluetoothProfile.HEARING_AID:
synchronized (BtHelper.this) {
mHearingAid = (BluetoothHearingAid) proxy;
deviceList = mHearingAid.getConnectedDevices();
if (deviceList.size() > 0) {
btDevice = deviceList.get(0);
final @BluetoothProfile.BtProfileState int state =
mHearingAid.getConnectionState(btDevice);
mDeviceBroker.setBluetoothHearingAidDeviceConnectionState(
btDevice, state,
/*suppressNoisyIntent*/ false,
/*musicDevice*/ android.media.AudioSystem.DEVICE_NONE,
/*eventSource*/ "mBluetoothProfileServiceListener");
}
}
AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
"BT profile service: connecting HEARING_AID profile"));
mDeviceBroker.postBtHearingAidProfileConnected(
(BluetoothHearingAid) proxy);
break;
default:
break;
}