Refactor locks in "audio" service for device management

Do not use shared locks across AudioDeviceBroker, BtHelper
  and AudioDeviceInventory.
Synchronize package private  tmethods on BtHelper and avoid
  dealing with external locks.
Clean up code ordering, more nullability annotations and
  annotation for connection state.

Bug: 112863932
Test: regression test on BT usecases
Change-Id: Id9e19e3c5e5241af913d06275f325fe505c1b509
This commit is contained in:
Jean-Michel Trivi
2019-02-01 09:58:54 -08:00
parent f2bfd385c9
commit c4b9c96d58
4 changed files with 449 additions and 420 deletions

View File

@@ -15,6 +15,9 @@
*/
package com.android.server.audio;
import static com.android.server.audio.AudioService.CONNECTION_STATE_CONNECTED;
import static com.android.server.audio.AudioService.CONNECTION_STATE_DISCONNECTED;
import android.annotation.NonNull;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothDevice;
@@ -43,7 +46,7 @@ import java.util.ArrayList;
/** @hide */
/*package*/ final class AudioDeviceBroker {
private static final String TAG = "AudioDeviceBroker";
private static final String TAG = "AS.AudioDeviceBroker";
private static final long BROKER_WAKELOCK_TIMEOUT_MS = 5000; //5s
@@ -62,27 +65,27 @@ import java.util.ArrayList;
private int mForcedUseForCommExt;
// Manages all connected devices, only ever accessed on the message loop
//### or make it synchronized
private final AudioDeviceInventory mDeviceInventory;
// Manages notifications to BT service
private final BtHelper mBtHelper;
//-------------------------------------------------------------------
// we use a different lock than mDeviceStateLock so as not to create
// lock contention between enqueueing a message and handling them
private static final Object sLastDeviceConnectionMsgTimeLock = new Object();
@GuardedBy("sLastDeviceConnectionMsgTimeLock")
private static long sLastDeviceConnectMsgTime = 0;
private final Object mBluetoothA2dpEnabledLock = new Object();
// General lock to be taken whenever the state of the audio devices is to be checked or changed
private final Object mDeviceStateLock = new Object();
// Request to override default use of A2DP for media.
@GuardedBy("mBluetoothA2dpEnabledLock")
@GuardedBy("mDeviceStateLock")
private boolean mBluetoothA2dpEnabled;
// lock always taken synchronized on mConnectedDevices
/*package*/ final Object mA2dpAvrcpLock = new Object();
// lock always taken synchronized on mConnectedDevices
/*package*/ final Object mHearingAidLock = new Object();
// lock always taken when accessing AudioService.mSetModeDeathHandlers
// TODO do not "share" the lock between AudioService and BtHelpr, see b/123769055
/*package*/ final Object mSetModeLock = new Object();
//-------------------------------------------------------------------
@@ -109,13 +112,17 @@ import java.util.ArrayList;
// All post* methods are asynchronous
/*package*/ void onSystemReady() {
mBtHelper.onSystemReady();
synchronized (mDeviceStateLock) {
mBtHelper.onSystemReady();
}
}
/*package*/ void onAudioServerDied() {
// Restore forced usage for communications and record
onSetForceUse(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, "onAudioServerDied");
onSetForceUse(AudioSystem.FOR_RECORD, mForcedUseForComm, "onAudioServerDied");
synchronized (mDeviceStateLock) {
onSetForceUse(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, "onAudioServerDied");
onSetForceUse(AudioSystem.FOR_RECORD, mForcedUseForComm, "onAudioServerDied");
}
// restore devices
sendMsgNoDelay(MSG_RESTORE_DEVICES, SENDMSG_REPLACE);
}
@@ -130,7 +137,9 @@ import java.util.ArrayList;
}
/*package*/ void disconnectAllBluetoothProfiles() {
mBtHelper.disconnectAllBluetoothProfiles();
synchronized (mDeviceStateLock) {
mBtHelper.disconnectAllBluetoothProfiles();
}
}
/**
@@ -140,11 +149,13 @@ import java.util.ArrayList;
* @param intent
*/
/*package*/ void receiveBtEvent(@NonNull Intent intent) {
mBtHelper.receiveBtEvent(intent);
synchronized (mDeviceStateLock) {
mBtHelper.receiveBtEvent(intent);
}
}
/*package*/ void setBluetoothA2dpOn_Async(boolean on, String source) {
synchronized (mBluetoothA2dpEnabledLock) {
synchronized (mDeviceStateLock) {
if (mBluetoothA2dpEnabled == on) {
return;
}
@@ -158,28 +169,34 @@ import java.util.ArrayList;
}
/*package*/ void setSpeakerphoneOn(boolean on, String eventSource) {
if (on) {
if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) {
setForceUse_Async(AudioSystem.FOR_RECORD, AudioSystem.FORCE_NONE, eventSource);
synchronized (mDeviceStateLock) {
if (on) {
if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) {
setForceUse_Async(AudioSystem.FOR_RECORD, AudioSystem.FORCE_NONE, eventSource);
}
mForcedUseForComm = AudioSystem.FORCE_SPEAKER;
} else if (mForcedUseForComm == AudioSystem.FORCE_SPEAKER) {
mForcedUseForComm = AudioSystem.FORCE_NONE;
}
mForcedUseForComm = AudioSystem.FORCE_SPEAKER;
} else if (mForcedUseForComm == AudioSystem.FORCE_SPEAKER) {
mForcedUseForComm = AudioSystem.FORCE_NONE;
}
mForcedUseForCommExt = mForcedUseForComm;
setForceUse_Async(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, eventSource);
mForcedUseForCommExt = mForcedUseForComm;
setForceUse_Async(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, eventSource);
}
}
/*package*/ boolean isSpeakerphoneOn() {
return (mForcedUseForCommExt == AudioSystem.FORCE_SPEAKER);
synchronized (mDeviceStateLock) {
return (mForcedUseForCommExt == AudioSystem.FORCE_SPEAKER);
}
}
/*package*/ void setWiredDeviceConnectionState(int type,
@AudioService.ConnectionState int state, String address, String name,
String caller) {
//TODO move logging here just like in setBluetooth* methods
mDeviceInventory.setWiredDeviceConnectionState(type, state, address, name, caller);
synchronized (mDeviceStateLock) {
mDeviceInventory.setWiredDeviceConnectionState(type, state, address, name, caller);
}
}
/*package*/ int setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
@@ -192,22 +209,27 @@ import java.util.ArrayList;
+ " addr=" + device.getAddress()
+ " prof=" + profile + " supprNoisy=" + suppressNoisyIntent
+ " vol=" + a2dpVolume)).printLog(TAG));
if (mBrokerHandler.hasMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE,
new BtHelper.BluetoothA2dpDeviceInfo(device))) {
AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
"A2DP connection state ignored"));
return 0;
synchronized (mDeviceStateLock) {
if (mBrokerHandler.hasMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE,
new BtHelper.BluetoothA2dpDeviceInfo(device))) {
AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
"A2DP connection state ignored"));
return 0;
}
return mDeviceInventory.setBluetoothA2dpDeviceConnectionState(
device, state, profile, suppressNoisyIntent,
AudioSystem.DEVICE_NONE, a2dpVolume);
}
return mDeviceInventory.setBluetoothA2dpDeviceConnectionState(
device, state, profile, suppressNoisyIntent, AudioSystem.DEVICE_NONE, a2dpVolume);
}
/*package*/ int handleBluetoothA2dpActiveDeviceChange(
@NonNull BluetoothDevice device,
@AudioService.BtProfileConnectionState int state, int profile,
boolean suppressNoisyIntent, int a2dpVolume) {
return mDeviceInventory.handleBluetoothA2dpActiveDeviceChange(device, state, profile,
suppressNoisyIntent, a2dpVolume);
synchronized (mDeviceStateLock) {
return mDeviceInventory.handleBluetoothA2dpActiveDeviceChange(device, state, profile,
suppressNoisyIntent, a2dpVolume);
}
}
/*package*/ int setBluetoothHearingAidDeviceConnectionState(
@@ -218,57 +240,69 @@ import java.util.ArrayList;
+ " addr=" + device.getAddress()
+ " supprNoisy=" + suppressNoisyIntent
+ " src=" + eventSource)).printLog(TAG));
return mDeviceInventory.setBluetoothHearingAidDeviceConnectionState(
device, state, suppressNoisyIntent, musicDevice);
synchronized (mDeviceStateLock) {
return mDeviceInventory.setBluetoothHearingAidDeviceConnectionState(
device, state, suppressNoisyIntent, musicDevice);
}
}
// never called by system components
/*package*/ void setBluetoothScoOnByApp(boolean on) {
mForcedUseForCommExt = on ? AudioSystem.FORCE_BT_SCO : AudioSystem.FORCE_NONE;
synchronized (mDeviceStateLock) {
mForcedUseForCommExt = on ? AudioSystem.FORCE_BT_SCO : AudioSystem.FORCE_NONE;
}
}
/*package*/ boolean isBluetoothScoOnForApp() {
return mForcedUseForCommExt == AudioSystem.FORCE_BT_SCO;
synchronized (mDeviceStateLock) {
return mForcedUseForCommExt == AudioSystem.FORCE_BT_SCO;
}
}
/*package*/ void setBluetoothScoOn(boolean on, String eventSource) {
//Log.i(TAG, "setBluetoothScoOnInt: " + on + " " + eventSource);
if (on) {
// do not accept SCO ON if SCO audio is not connected
if (!mBtHelper.isBluetoothScoOn()) {
mForcedUseForCommExt = AudioSystem.FORCE_BT_SCO;
return;
synchronized (mDeviceStateLock) {
if (on) {
// do not accept SCO ON if SCO audio is not connected
if (!mBtHelper.isBluetoothScoOn()) {
mForcedUseForCommExt = AudioSystem.FORCE_BT_SCO;
return;
}
mForcedUseForComm = AudioSystem.FORCE_BT_SCO;
} else if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) {
mForcedUseForComm = AudioSystem.FORCE_NONE;
}
mForcedUseForComm = AudioSystem.FORCE_BT_SCO;
} else if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) {
mForcedUseForComm = AudioSystem.FORCE_NONE;
mForcedUseForCommExt = mForcedUseForComm;
AudioSystem.setParameters("BT_SCO=" + (on ? "on" : "off"));
sendIILMsgNoDelay(MSG_IIL_SET_FORCE_USE, SENDMSG_QUEUE,
AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, eventSource);
sendIILMsgNoDelay(MSG_IIL_SET_FORCE_USE, SENDMSG_QUEUE,
AudioSystem.FOR_RECORD, mForcedUseForComm, eventSource);
// Un-mute ringtone stream volume
mAudioService.setUpdateRingerModeServiceInt();
}
mForcedUseForCommExt = mForcedUseForComm;
AudioSystem.setParameters("BT_SCO=" + (on ? "on" : "off"));
sendIILMsgNoDelay(MSG_IIL_SET_FORCE_USE, SENDMSG_QUEUE,
AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, eventSource);
sendIILMsgNoDelay(MSG_IIL_SET_FORCE_USE, SENDMSG_QUEUE,
AudioSystem.FOR_RECORD, mForcedUseForComm, eventSource);
// Un-mute ringtone stream volume
mAudioService.setUpdateRingerModeServiceInt();
}
/*package*/ AudioRoutesInfo startWatchingRoutes(IAudioRoutesObserver observer) {
return mDeviceInventory.startWatchingRoutes(observer);
synchronized (mDeviceStateLock) {
return mDeviceInventory.startWatchingRoutes(observer);
}
}
/*package*/ AudioRoutesInfo getCurAudioRoutes() {
return mDeviceInventory.getCurAudioRoutes();
synchronized (mDeviceStateLock) {
return mDeviceInventory.getCurAudioRoutes();
}
}
/*package*/ boolean isAvrcpAbsoluteVolumeSupported() {
synchronized (mA2dpAvrcpLock) {
synchronized (mDeviceStateLock) {
return mBtHelper.isAvrcpAbsoluteVolumeSupported();
}
}
/*package*/ boolean isBluetoothA2dpOn() {
synchronized (mBluetoothA2dpEnabledLock) {
synchronized (mDeviceStateLock) {
return mBluetoothA2dpEnabled;
}
}
@@ -355,14 +389,12 @@ import java.util.ArrayList;
sendMsgNoDelay(MSG_BROADCAST_AUDIO_BECOMING_NOISY, SENDMSG_REPLACE);
}
//###TODO unify with handleSetA2dpSinkConnectionState
/*package*/ void postA2dpSinkConnection(int state,
@NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo, int delay) {
sendILMsg(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE, SENDMSG_QUEUE,
state, btDeviceInfo, delay);
}
//###TODO unify with handleSetA2dpSourceConnectionState
/*package*/ void postA2dpSourceConnection(int state,
@NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo, int delay) {
sendILMsg(MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE, SENDMSG_QUEUE,
@@ -395,7 +427,7 @@ import java.util.ArrayList;
.append(") from u/pid:").append(Binder.getCallingUid()).append("/")
.append(Binder.getCallingPid()).append(" src:").append(source).toString();
synchronized (mBluetoothA2dpEnabledLock) {
synchronized (mDeviceStateLock) {
mBluetoothA2dpEnabled = on;
mBrokerHandler.removeMessages(MSG_IIL_SET_FORCE_BT_A2DP_USE);
onSetForceUse(
@@ -407,25 +439,38 @@ import java.util.ArrayList;
/*package*/ boolean handleDeviceConnection(boolean connect, int device, String address,
String deviceName) {
return mDeviceInventory.handleDeviceConnection(connect, device, address, deviceName);
synchronized (mDeviceStateLock) {
return mDeviceInventory.handleDeviceConnection(connect, device, address, deviceName);
}
}
/*package*/ void handleDisconnectA2dp() {
mDeviceInventory.disconnectA2dp();
synchronized (mDeviceStateLock) {
mDeviceInventory.disconnectA2dp();
}
}
/*package*/ void handleDisconnectA2dpSink() {
mDeviceInventory.disconnectA2dpSink();
synchronized (mDeviceStateLock) {
mDeviceInventory.disconnectA2dpSink();
}
}
/*package*/ void handleDisconnectHearingAid() {
synchronized (mDeviceStateLock) {
mDeviceInventory.disconnectHearingAid();
}
}
/*package*/ void handleSetA2dpSinkConnectionState(@BluetoothProfile.BtProfileState int state,
@NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo) {
final int intState = (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0;
//### DOESN'T HONOR SYNC ON DEVICES -> make a synchronized version?
// might be ok here because called on BT thread? + sync happening in
// checkSendBecomingNoisyIntent
final int delay = mDeviceInventory.checkSendBecomingNoisyIntent(
AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, intState,
AudioSystem.DEVICE_NONE);
final int intState = (state == BluetoothA2dp.STATE_CONNECTED)
? CONNECTION_STATE_CONNECTED : CONNECTION_STATE_DISCONNECTED;
final int delay;
synchronized (mDeviceStateLock) {
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) {
@@ -437,10 +482,6 @@ import java.util.ArrayList;
state, btDeviceInfo, delay);
}
/*package*/ void handleDisconnectHearingAid() {
mDeviceInventory.disconnectHearingAid();
}
/*package*/ void handleSetA2dpSourceConnectionState(@BluetoothProfile.BtProfileState int state,
@NonNull BtHelper.BluetoothA2dpDeviceInfo btDeviceInfo) {
final int intState = (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0;
@@ -468,8 +509,6 @@ import java.util.ArrayList;
sendLMsgNoDelay(MSG_L_A2DP_ACTIVE_DEVICE_CHANGE, SENDMSG_QUEUE, btDeviceInfo);
}
//###
// must be called synchronized on mConnectedDevices
/*package*/ boolean hasScheduledA2dpDockTimeout() {
return mBrokerHandler.hasMessages(MSG_IL_BTA2DP_DOCK_TIMEOUT);
}
@@ -486,19 +525,19 @@ import java.util.ArrayList;
}
/*package*/ void setAvrcpAbsoluteVolumeSupported(boolean supported) {
synchronized (mA2dpAvrcpLock) {
synchronized (mDeviceStateLock) {
mBtHelper.setAvrcpAbsoluteVolumeSupported(supported);
}
}
/*package*/ boolean getBluetoothA2dpEnabled() {
synchronized (mBluetoothA2dpEnabledLock) {
synchronized (mDeviceStateLock) {
return mBluetoothA2dpEnabled;
}
}
/*package*/ int getA2dpCodec(@NonNull BluetoothDevice device) {
synchronized (mA2dpAvrcpLock) {
synchronized (mDeviceStateLock) {
return mBtHelper.getA2dpCodec(device);
}
}
@@ -579,71 +618,97 @@ import java.util.ArrayList;
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_RESTORE_DEVICES:
mDeviceInventory.onRestoreDevices();
synchronized (mBluetoothA2dpEnabledLock) {
synchronized (mDeviceStateLock) {
mDeviceInventory.onRestoreDevices();
mBtHelper.onAudioServerDiedRestoreA2dp();
}
break;
case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE:
mDeviceInventory.onSetWiredDeviceConnectionState(
(AudioDeviceInventory.WiredDeviceConnectionState) msg.obj);
synchronized (mDeviceStateLock) {
mDeviceInventory.onSetWiredDeviceConnectionState(
(AudioDeviceInventory.WiredDeviceConnectionState) msg.obj);
}
break;
case MSG_I_BROADCAST_BT_CONNECTION_STATE:
mBtHelper.onBroadcastScoConnectionState(msg.arg1);
synchronized (mDeviceStateLock) {
mBtHelper.onBroadcastScoConnectionState(msg.arg1);
}
break;
case MSG_IIL_SET_FORCE_USE: // intented fall-through
case MSG_IIL_SET_FORCE_USE: // intended fall-through
case MSG_IIL_SET_FORCE_BT_A2DP_USE:
onSetForceUse(msg.arg1, msg.arg2, (String) msg.obj);
break;
case MSG_REPORT_NEW_ROUTES:
mDeviceInventory.onReportNewRoutes();
synchronized (mDeviceStateLock) {
mDeviceInventory.onReportNewRoutes();
}
break;
case MSG_IL_SET_A2DP_SINK_CONNECTION_STATE:
mDeviceInventory.onSetA2dpSinkConnectionState(
(BtHelper.BluetoothA2dpDeviceInfo) msg.obj, msg.arg1);
synchronized (mDeviceStateLock) {
mDeviceInventory.onSetA2dpSinkConnectionState(
(BtHelper.BluetoothA2dpDeviceInfo) msg.obj, msg.arg1);
}
break;
case MSG_IL_SET_A2DP_SOURCE_CONNECTION_STATE:
mDeviceInventory.onSetA2dpSourceConnectionState(
(BtHelper.BluetoothA2dpDeviceInfo) msg.obj, msg.arg1);
synchronized (mDeviceStateLock) {
mDeviceInventory.onSetA2dpSourceConnectionState(
(BtHelper.BluetoothA2dpDeviceInfo) msg.obj, msg.arg1);
}
break;
case MSG_IL_SET_HEARING_AID_CONNECTION_STATE:
mDeviceInventory.onSetHearingAidConnectionState(
(BluetoothDevice) msg.obj, msg.arg1);
synchronized (mDeviceStateLock) {
mDeviceInventory.onSetHearingAidConnectionState(
(BluetoothDevice) msg.obj, msg.arg1);
}
break;
case MSG_BT_HEADSET_CNCT_FAILED:
mBtHelper.resetBluetoothSco();
synchronized (mDeviceStateLock) {
mBtHelper.resetBluetoothSco();
}
break;
case MSG_IL_BTA2DP_DOCK_TIMEOUT:
// msg.obj == address of BTA2DP device
mDeviceInventory.onMakeA2dpDeviceUnavailableNow((String) msg.obj, msg.arg1);
synchronized (mDeviceStateLock) {
mDeviceInventory.onMakeA2dpDeviceUnavailableNow((String) msg.obj, msg.arg1);
}
break;
case MSG_L_A2DP_DEVICE_CONFIG_CHANGE:
final int a2dpCodec;
final BluetoothDevice btDevice = (BluetoothDevice) msg.obj;
synchronized (mA2dpAvrcpLock) {
synchronized (mDeviceStateLock) {
a2dpCodec = mBtHelper.getA2dpCodec(btDevice);
mDeviceInventory.onBluetoothA2dpDeviceConfigChange(
new BtHelper.BluetoothA2dpDeviceInfo(btDevice, -1, a2dpCodec));
}
mDeviceInventory.onBluetoothA2dpDeviceConfigChange(
new BtHelper.BluetoothA2dpDeviceInfo(btDevice, -1, a2dpCodec));
break;
case MSG_BROADCAST_AUDIO_BECOMING_NOISY:
onSendBecomingNoisyIntent();
break;
case MSG_II_SET_HEARING_AID_VOLUME:
mBtHelper.setHearingAidVolume(msg.arg1, msg.arg2);
synchronized (mDeviceStateLock) {
mBtHelper.setHearingAidVolume(msg.arg1, msg.arg2);
}
break;
case MSG_I_SET_AVRCP_ABSOLUTE_VOLUME:
mBtHelper.setAvrcpAbsoluteVolumeIndex(msg.arg1);
synchronized (mDeviceStateLock) {
mBtHelper.setAvrcpAbsoluteVolumeIndex(msg.arg1);
}
break;
case MSG_I_DISCONNECT_BT_SCO:
mBtHelper.disconnectBluetoothSco(msg.arg1);
synchronized (mDeviceStateLock) {
mBtHelper.disconnectBluetoothSco(msg.arg1);
}
break;
case MSG_TOGGLE_HDMI:
mDeviceInventory.onToggleHdmi();
synchronized (mDeviceStateLock) {
mDeviceInventory.onToggleHdmi();
}
break;
case MSG_L_A2DP_ACTIVE_DEVICE_CHANGE:
mDeviceInventory.onBluetoothA2dpActiveDeviceChange(
(BtHelper.BluetoothA2dpDeviceInfo) msg.obj);
synchronized (mDeviceStateLock) {
mDeviceInventory.onBluetoothA2dpActiveDeviceChange(
(BtHelper.BluetoothA2dpDeviceInfo) msg.obj);
}
break;
default:
Log.wtf(TAG, "Invalid message " + msg.what);

View File

@@ -162,10 +162,7 @@ public final class AudioDeviceInventory {
"A2DP sink connected: device addr=" + address + " state=" + state
+ " vol=" + a2dpVolume));
final int a2dpCodec;
synchronized (mDeviceBroker.mA2dpAvrcpLock) {
a2dpCodec = btInfo.getCodec();
}
final int a2dpCodec = btInfo.getCodec();
synchronized (mConnectedDevices) {
final String key = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
@@ -508,22 +505,20 @@ public final class AudioDeviceInventory {
/*package*/ void disconnectA2dp() {
synchronized (mConnectedDevices) {
synchronized (mDeviceBroker.mA2dpAvrcpLock) {
final ArraySet<String> toRemove = new ArraySet<>();
// Disconnect ALL DEVICE_OUT_BLUETOOTH_A2DP devices
mConnectedDevices.values().forEach(deviceInfo -> {
if (deviceInfo.mDeviceType == AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) {
toRemove.add(deviceInfo.mDeviceAddress);
}
});
if (toRemove.size() > 0) {
final int delay = checkSendBecomingNoisyIntentInt(
AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
0, AudioSystem.DEVICE_NONE);
toRemove.stream().forEach(deviceAddress ->
makeA2dpDeviceUnavailableLater(deviceAddress, delay)
);
final ArraySet<String> toRemove = new ArraySet<>();
// Disconnect ALL DEVICE_OUT_BLUETOOTH_A2DP devices
mConnectedDevices.values().forEach(deviceInfo -> {
if (deviceInfo.mDeviceType == AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) {
toRemove.add(deviceInfo.mDeviceAddress);
}
});
if (toRemove.size() > 0) {
final int delay = checkSendBecomingNoisyIntentInt(
AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
AudioService.CONNECTION_STATE_DISCONNECTED, AudioSystem.DEVICE_NONE);
toRemove.stream().forEach(deviceAddress ->
makeA2dpDeviceUnavailableLater(deviceAddress, delay)
);
}
}
}
@@ -543,22 +538,20 @@ public final class AudioDeviceInventory {
/*package*/ void disconnectHearingAid() {
synchronized (mConnectedDevices) {
synchronized (mDeviceBroker.mHearingAidLock) {
final ArraySet<String> toRemove = new ArraySet<>();
// Disconnect ALL DEVICE_OUT_HEARING_AID devices
mConnectedDevices.values().forEach(deviceInfo -> {
if (deviceInfo.mDeviceType == AudioSystem.DEVICE_OUT_HEARING_AID) {
toRemove.add(deviceInfo.mDeviceAddress);
}
});
if (toRemove.size() > 0) {
final int delay = checkSendBecomingNoisyIntentInt(
AudioSystem.DEVICE_OUT_HEARING_AID, 0, AudioSystem.DEVICE_NONE);
toRemove.stream().forEach(deviceAddress ->
// TODO delay not used?
makeHearingAidDeviceUnavailable(deviceAddress /*, delay*/)
);
final ArraySet<String> toRemove = new ArraySet<>();
// Disconnect ALL DEVICE_OUT_HEARING_AID devices
mConnectedDevices.values().forEach(deviceInfo -> {
if (deviceInfo.mDeviceType == AudioSystem.DEVICE_OUT_HEARING_AID) {
toRemove.add(deviceInfo.mDeviceAddress);
}
});
if (toRemove.size() > 0) {
final int delay = checkSendBecomingNoisyIntentInt(
AudioSystem.DEVICE_OUT_HEARING_AID, 0, AudioSystem.DEVICE_NONE);
toRemove.stream().forEach(deviceAddress ->
// TODO delay not used?
makeHearingAidDeviceUnavailable(deviceAddress /*, delay*/)
);
}
}
}
@@ -566,7 +559,8 @@ public final class AudioDeviceInventory {
// must be called before removing the device from mConnectedDevices
// musicDevice argument is used when not AudioSystem.DEVICE_NONE instead of querying
// from AudioSystem
/*package*/ int checkSendBecomingNoisyIntent(int device, int state, int musicDevice) {
/*package*/ int checkSendBecomingNoisyIntent(int device,
@AudioService.ConnectionState int state, int musicDevice) {
synchronized (mConnectedDevices) {
return checkSendBecomingNoisyIntentInt(device, state, musicDevice);
}
@@ -840,8 +834,9 @@ public final class AudioDeviceInventory {
// musicDevice argument is used when not AudioSystem.DEVICE_NONE instead of querying
// from AudioSystem
@GuardedBy("mConnectedDevices")
private int checkSendBecomingNoisyIntentInt(int device, int state, int musicDevice) {
if (state != 0) {
private int checkSendBecomingNoisyIntentInt(int device,
@AudioService.ConnectionState int state, int musicDevice) {
if (state != AudioService.CONNECTION_STATE_DISCONNECTED) {
return 0;
}
if ((device & mBecomingNoisyIntentDevices) == 0) {

View File

@@ -786,7 +786,6 @@ public class AudioService extends IAudioService.Stub
mPrescaleAbsoluteVolume[i] = preScale[i];
}
}
}
public void systemReady() {
@@ -3821,7 +3820,6 @@ public class AudioService extends IAudioService.Stub
private static void sendMsg(Handler handler, int msg,
int existingMsgPolicy, int arg1, int arg2, Object obj, int delay) {
if (existingMsgPolicy == SENDMSG_REPLACE) {
handler.removeMessages(msg);
} else if (existingMsgPolicy == SENDMSG_NOOP && handler.hasMessages(msg)) {

View File

@@ -16,6 +16,7 @@
package com.android.server.audio;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
@@ -35,8 +36,6 @@ import android.os.UserHandle;
import android.provider.Settings;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
@@ -57,21 +56,40 @@ public class BtHelper {
}
// List of clients having issued a SCO start request
private final ArrayList<ScoClient> mScoClients = new ArrayList<ScoClient>();
private final @NonNull ArrayList<ScoClient> mScoClients = new ArrayList<ScoClient>();
// BluetoothHeadset API to control SCO connection
private BluetoothHeadset mBluetoothHeadset;
private @Nullable BluetoothHeadset mBluetoothHeadset;
// Bluetooth headset device
private BluetoothDevice mBluetoothHeadsetDevice;
private @Nullable BluetoothDevice mBluetoothHeadsetDevice;
private @Nullable BluetoothHearingAid mHearingAid;
// Reference to BluetoothA2dp to query for AbsoluteVolume.
private @Nullable BluetoothA2dp mA2dp;
// If absolute volume is supported in AVRCP device
private boolean mAvrcpAbsVolSupported = false;
// Current connection state indicated by bluetooth headset
private int mScoConnectionState;
// Indicate if SCO audio connection is currently active and if the initiator is
// audio service (internal) or bluetooth headset (external)
private int mScoAudioState;
// Indicates the mode used for SCO audio connection. The mode is virtual call if the request
// originated from an app targeting an API version before JB MR2 and raw audio after that.
private int mScoAudioMode;
// SCO audio state is not active
private static final int SCO_STATE_INACTIVE = 0;
// SCO audio activation request waiting for headset service to connect
private static final int SCO_STATE_ACTIVATE_REQ = 1;
// SCO audio state is active due to an action in BT handsfree (either voice recognition or
// in call audio)
private static final int SCO_STATE_ACTIVE_EXTERNAL = 2;
// SCO audio state is active or starting due to a request from AudioManager API
private static final int SCO_STATE_ACTIVE_INTERNAL = 3;
// SCO audio deactivation request waiting for headset service to connect
@@ -79,39 +97,19 @@ public class BtHelper {
// SCO audio deactivation in progress, waiting for Bluetooth audio intent
private static final int SCO_STATE_DEACTIVATING = 5;
// SCO audio state is active due to an action in BT handsfree (either voice recognition or
// in call audio)
private static final int SCO_STATE_ACTIVE_EXTERNAL = 2;
// Indicates the mode used for SCO audio connection. The mode is virtual call if the request
// originated from an app targeting an API version before JB MR2 and raw audio after that.
private int mScoAudioMode;
// SCO audio mode is undefined
/*package*/ static final int SCO_MODE_UNDEFINED = -1;
/*package*/ static final int SCO_MODE_UNDEFINED = -1;
// SCO audio mode is virtual voice call (BluetoothHeadset.startScoUsingVirtualVoiceCall())
/*package*/ static final int SCO_MODE_VIRTUAL_CALL = 0;
// SCO audio mode is raw audio (BluetoothHeadset.connectAudio())
private static final int SCO_MODE_RAW = 1;
// SCO audio mode is Voice Recognition (BluetoothHeadset.startVoiceRecognition())
private static final int SCO_MODE_VR = 2;
// max valid SCO audio mode values
private static final int SCO_MODE_MAX = 2;
// Current connection state indicated by bluetooth headset
private int mScoConnectionState;
private static final int BT_HEARING_AID_GAIN_MIN = -128;
@GuardedBy("mDeviceBroker.mHearingAidLock")
private BluetoothHearingAid mHearingAid;
// Reference to BluetoothA2dp to query for AbsoluteVolume.
@GuardedBy("mDeviceBroker.mA2dpAvrcpLock")
private BluetoothA2dp mA2dp;
// If absolute volume is supported in AVRCP device
@GuardedBy("mDeviceBroker.mA2dpAvrcpLock")
private boolean mAvrcpAbsVolSupported = false;
//----------------------------------------------------------------------
/*package*/ static class BluetoothA2dpDeviceInfo {
private final @NonNull BluetoothDevice mBtDevice;
@@ -144,7 +142,7 @@ public class BtHelper {
//----------------------------------------------------------------------
// Interface for AudioDeviceBroker
/*package*/ void onSystemReady() {
/*package*/ synchronized void onSystemReady() {
mScoConnectionState = android.media.AudioManager.SCO_AUDIO_STATE_ERROR;
resetBluetoothSco();
getBluetoothHeadset();
@@ -165,45 +163,39 @@ public class BtHelper {
}
}
@GuardedBy("mBluetoothA2dpEnabledLock")
/*package*/ void onAudioServerDiedRestoreA2dp() {
/*package*/ synchronized void onAudioServerDiedRestoreA2dp() {
final int forMed = mDeviceBroker.getBluetoothA2dpEnabled()
? AudioSystem.FORCE_NONE : AudioSystem.FORCE_NO_BT_A2DP;
mDeviceBroker.setForceUse_Async(AudioSystem.FOR_MEDIA, forMed, "onAudioServerDied()");
}
@GuardedBy("mA2dpAvrcpLock")
/*package*/ boolean isAvrcpAbsoluteVolumeSupported() {
/*package*/ synchronized boolean isAvrcpAbsoluteVolumeSupported() {
return (mA2dp != null && mAvrcpAbsVolSupported);
}
@GuardedBy("mA2dpAvrcpLock")
/*package*/ void setAvrcpAbsoluteVolumeSupported(boolean supported) {
/*package*/ synchronized void setAvrcpAbsoluteVolumeSupported(boolean supported) {
mAvrcpAbsVolSupported = supported;
}
/*package*/ void setAvrcpAbsoluteVolumeIndex(int index) {
synchronized (mDeviceBroker.mA2dpAvrcpLock) {
if (mA2dp == null) {
if (AudioService.DEBUG_VOL) {
Log.d(TAG, "setAvrcpAbsoluteVolumeIndex: bailing due to null mA2dp");
return;
}
}
if (!mAvrcpAbsVolSupported) {
/*package*/ synchronized void setAvrcpAbsoluteVolumeIndex(int index) {
if (mA2dp == null) {
if (AudioService.DEBUG_VOL) {
Log.d(TAG, "setAvrcpAbsoluteVolumeIndex: bailing due to null mA2dp");
return;
}
if (AudioService.DEBUG_VOL) {
Log.i(TAG, "setAvrcpAbsoluteVolumeIndex index=" + index);
}
AudioService.sVolumeLogger.log(new AudioServiceEvents.VolumeEvent(
AudioServiceEvents.VolumeEvent.VOL_SET_AVRCP_VOL, index / 10));
mA2dp.setAvrcpAbsoluteVolume(index / 10);
}
if (!mAvrcpAbsVolSupported) {
return;
}
if (AudioService.DEBUG_VOL) {
Log.i(TAG, "setAvrcpAbsoluteVolumeIndex index=" + index);
}
AudioService.sVolumeLogger.log(new AudioServiceEvents.VolumeEvent(
AudioServiceEvents.VolumeEvent.VOL_SET_AVRCP_VOL, index / 10));
mA2dp.setAvrcpAbsoluteVolume(index / 10);
}
@GuardedBy("mA2dpAvrcpLock")
/*package*/ int getA2dpCodec(@NonNull BluetoothDevice device) {
/*package*/ synchronized int getA2dpCodec(@NonNull BluetoothDevice device) {
if (mA2dp == null) {
return AudioSystem.AUDIO_FORMAT_DEFAULT;
}
@@ -218,7 +210,7 @@ public class BtHelper {
return mapBluetoothCodecToAudioFormat(btCodecConfig.getCodecType());
}
/*package*/ void receiveBtEvent(Intent intent) {
/*package*/ synchronized void receiveBtEvent(Intent intent) {
final String action = intent.getAction();
if (action.equals(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED)) {
BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
@@ -226,53 +218,51 @@ public class BtHelper {
} else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
boolean broadcast = false;
int scoAudioState = AudioManager.SCO_AUDIO_STATE_ERROR;
synchronized (mScoClients) {
int btState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
// broadcast intent if the connection was initated by AudioService
if (!mScoClients.isEmpty()
&& (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL
|| mScoAudioState == SCO_STATE_ACTIVATE_REQ
|| mScoAudioState == SCO_STATE_DEACTIVATE_REQ
|| mScoAudioState == SCO_STATE_DEACTIVATING)) {
broadcast = true;
}
switch (btState) {
case BluetoothHeadset.STATE_AUDIO_CONNECTED:
scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTED;
if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL
&& mScoAudioState != SCO_STATE_DEACTIVATE_REQ) {
mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
int btState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
// broadcast intent if the connection was initated by AudioService
if (!mScoClients.isEmpty()
&& (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL
|| mScoAudioState == SCO_STATE_ACTIVATE_REQ
|| mScoAudioState == SCO_STATE_DEACTIVATE_REQ
|| mScoAudioState == SCO_STATE_DEACTIVATING)) {
broadcast = true;
}
switch (btState) {
case BluetoothHeadset.STATE_AUDIO_CONNECTED:
scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTED;
if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL
&& mScoAudioState != SCO_STATE_DEACTIVATE_REQ) {
mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
}
mDeviceBroker.setBluetoothScoOn(true, "BtHelper.receiveBtEvent");
break;
case BluetoothHeadset.STATE_AUDIO_DISCONNECTED:
mDeviceBroker.setBluetoothScoOn(false, "BtHelper.receiveBtEvent");
scoAudioState = AudioManager.SCO_AUDIO_STATE_DISCONNECTED;
// startBluetoothSco called after stopBluetoothSco
if (mScoAudioState == SCO_STATE_ACTIVATE_REQ) {
if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null
&& connectBluetoothScoAudioHelper(mBluetoothHeadset,
mBluetoothHeadsetDevice, mScoAudioMode)) {
mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
broadcast = false;
break;
}
mDeviceBroker.setBluetoothScoOn(true, "BtHelper.receiveBtEvent");
break;
case BluetoothHeadset.STATE_AUDIO_DISCONNECTED:
mDeviceBroker.setBluetoothScoOn(false, "BtHelper.receiveBtEvent");
scoAudioState = AudioManager.SCO_AUDIO_STATE_DISCONNECTED;
// startBluetoothSco called after stopBluetoothSco
if (mScoAudioState == SCO_STATE_ACTIVATE_REQ) {
if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null
&& connectBluetoothScoAudioHelper(mBluetoothHeadset,
mBluetoothHeadsetDevice, mScoAudioMode)) {
mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
broadcast = false;
break;
}
}
// Tear down SCO if disconnected from external
clearAllScoClients(0, mScoAudioState == SCO_STATE_ACTIVE_INTERNAL);
mScoAudioState = SCO_STATE_INACTIVE;
break;
case BluetoothHeadset.STATE_AUDIO_CONNECTING:
if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL
&& mScoAudioState != SCO_STATE_DEACTIVATE_REQ) {
mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
}
break;
default:
// do not broadcast CONNECTING or invalid state
broadcast = false;
break;
}
}
// Tear down SCO if disconnected from external
clearAllScoClients(0, mScoAudioState == SCO_STATE_ACTIVE_INTERNAL);
mScoAudioState = SCO_STATE_INACTIVE;
break;
case BluetoothHeadset.STATE_AUDIO_CONNECTING:
if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL
&& mScoAudioState != SCO_STATE_DEACTIVATE_REQ) {
mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
}
break;
default:
// do not broadcast CONNECTING or invalid state
broadcast = false;
break;
}
if (broadcast) {
broadcastScoConnectionState(scoAudioState);
@@ -289,15 +279,13 @@ public class BtHelper {
*
* @return false if SCO isn't connected
*/
/*package*/ boolean isBluetoothScoOn() {
synchronized (mScoClients) {
if ((mBluetoothHeadset != null)
&& (mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice)
!= BluetoothHeadset.STATE_AUDIO_CONNECTED)) {
Log.w(TAG, "isBluetoothScoOn(true) returning false because "
+ mBluetoothHeadsetDevice + " is not in audio connected mode");
return false;
}
/*package*/ synchronized boolean isBluetoothScoOn() {
if ((mBluetoothHeadset != null)
&& (mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice)
!= BluetoothHeadset.STATE_AUDIO_CONNECTED)) {
Log.w(TAG, "isBluetoothScoOn(true) returning false because "
+ mBluetoothHeadsetDevice + " is not in audio connected mode");
return false;
}
return true;
}
@@ -308,17 +296,15 @@ public class BtHelper {
*
* @param exceptPid pid whose SCO connections through {@link AudioManager} should be kept
*/
/*package*/ void disconnectBluetoothSco(int exceptPid) {
synchronized (mScoClients) {
checkScoAudioState();
if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL) {
return;
}
clearAllScoClients(exceptPid, true);
/*package*/ synchronized void disconnectBluetoothSco(int exceptPid) {
checkScoAudioState();
if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL) {
return;
}
clearAllScoClients(exceptPid, true);
}
/*package*/ void startBluetoothScoForClient(IBinder cb, int scoAudioMode,
/*package*/ synchronized void startBluetoothScoForClient(IBinder cb, int scoAudioMode,
@NonNull String eventSource) {
ScoClient client = getScoClient(cb, true);
// The calling identity must be cleared before calling ScoClient.incCount().
@@ -337,7 +323,8 @@ public class BtHelper {
Binder.restoreCallingIdentity(ident);
}
/*package*/ void stopBluetoothScoForClient(IBinder cb, @NonNull String eventSource) {
/*package*/ synchronized void stopBluetoothScoForClient(IBinder cb,
@NonNull String eventSource) {
ScoClient client = getScoClient(cb, false);
// The calling identity must be cleared before calling ScoClient.decCount().
// decCount() calls requestScoState() which in turn can call BluetoothHeadset APIs
@@ -352,36 +339,29 @@ public class BtHelper {
}
/*package*/ void setHearingAidVolume(int index, int streamType) {
synchronized (mDeviceBroker.mHearingAidLock) {
if (mHearingAid == null) {
if (AudioService.DEBUG_VOL) {
Log.i(TAG, "setHearingAidVolume: null mHearingAid");
}
return;
}
//hearing aid expect volume value in range -128dB to 0dB
int gainDB = (int) AudioSystem.getStreamVolumeDB(streamType, index / 10,
AudioSystem.DEVICE_OUT_HEARING_AID);
if (gainDB < BT_HEARING_AID_GAIN_MIN) {
gainDB = BT_HEARING_AID_GAIN_MIN;
}
/*package*/ synchronized void setHearingAidVolume(int index, int streamType) {
if (mHearingAid == null) {
if (AudioService.DEBUG_VOL) {
Log.i(TAG, "setHearingAidVolume: calling mHearingAid.setVolume idx="
+ index + " gain=" + gainDB);
Log.i(TAG, "setHearingAidVolume: null mHearingAid");
}
AudioService.sVolumeLogger.log(new AudioServiceEvents.VolumeEvent(
AudioServiceEvents.VolumeEvent.VOL_SET_HEARING_AID_VOL, index, gainDB));
mHearingAid.setVolume(gainDB);
return;
}
//hearing aid expect volume value in range -128dB to 0dB
int gainDB = (int) AudioSystem.getStreamVolumeDB(streamType, index / 10,
AudioSystem.DEVICE_OUT_HEARING_AID);
if (gainDB < BT_HEARING_AID_GAIN_MIN) {
gainDB = BT_HEARING_AID_GAIN_MIN;
}
if (AudioService.DEBUG_VOL) {
Log.i(TAG, "setHearingAidVolume: calling mHearingAid.setVolume idx="
+ index + " gain=" + gainDB);
}
AudioService.sVolumeLogger.log(new AudioServiceEvents.VolumeEvent(
AudioServiceEvents.VolumeEvent.VOL_SET_HEARING_AID_VOL, index, gainDB));
mHearingAid.setVolume(gainDB);
}
//----------------------------------------------------------------------
private void broadcastScoConnectionState(int state) {
mDeviceBroker.broadcastScoConnectionState(state);
}
/*package*/ void onBroadcastScoConnectionState(int state) {
/*package*/ synchronized void onBroadcastScoConnectionState(int state) {
if (state == mScoConnectionState) {
return;
}
@@ -393,6 +373,26 @@ public class BtHelper {
mScoConnectionState = state;
}
/*package*/ synchronized void disconnectAllBluetoothProfiles() {
mDeviceBroker.handleDisconnectA2dp();
mDeviceBroker.handleDisconnectA2dpSink();
disconnectHeadset();
mDeviceBroker.handleDisconnectHearingAid();
}
/*package*/ synchronized void resetBluetoothSco() {
clearAllScoClients(0, false);
mScoAudioState = SCO_STATE_INACTIVE;
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
AudioSystem.setParameters("A2dpSuspended=false");
mDeviceBroker.setBluetoothScoOn(false, "resetBluetoothSco");
}
//----------------------------------------------------------------------
private void broadcastScoConnectionState(int state) {
mDeviceBroker.broadcastScoConnectionState(state);
}
private boolean handleBtScoActiveDeviceChange(BluetoothDevice btDevice, boolean isActive) {
if (btDevice == null) {
return true;
@@ -437,25 +437,23 @@ public class BtHelper {
}
private void setBtScoActiveDevice(BluetoothDevice btDevice) {
synchronized (mScoClients) {
Log.i(TAG, "setBtScoActiveDevice: " + mBluetoothHeadsetDevice + " -> " + btDevice);
final BluetoothDevice previousActiveDevice = mBluetoothHeadsetDevice;
if (Objects.equals(btDevice, previousActiveDevice)) {
return;
}
if (!handleBtScoActiveDeviceChange(previousActiveDevice, false)) {
Log.w(TAG, "setBtScoActiveDevice() failed to remove previous device "
+ previousActiveDevice);
}
if (!handleBtScoActiveDeviceChange(btDevice, true)) {
Log.e(TAG, "setBtScoActiveDevice() failed to add new device " + btDevice);
// set mBluetoothHeadsetDevice to null when failing to add new device
btDevice = null;
}
mBluetoothHeadsetDevice = btDevice;
if (mBluetoothHeadsetDevice == null) {
resetBluetoothSco();
}
Log.i(TAG, "setBtScoActiveDevice: " + mBluetoothHeadsetDevice + " -> " + btDevice);
final BluetoothDevice previousActiveDevice = mBluetoothHeadsetDevice;
if (Objects.equals(btDevice, previousActiveDevice)) {
return;
}
if (!handleBtScoActiveDeviceChange(previousActiveDevice, false)) {
Log.w(TAG, "setBtScoActiveDevice() failed to remove previous device "
+ previousActiveDevice);
}
if (!handleBtScoActiveDeviceChange(btDevice, true)) {
Log.e(TAG, "setBtScoActiveDevice() failed to add new device " + btDevice);
// set mBluetoothHeadsetDevice to null when failing to add new device
btDevice = null;
}
mBluetoothHeadsetDevice = btDevice;
if (mBluetoothHeadsetDevice == null) {
resetBluetoothSco();
}
}
@@ -466,7 +464,7 @@ public class BtHelper {
List<BluetoothDevice> deviceList;
switch(profile) {
case BluetoothProfile.A2DP:
synchronized (mDeviceBroker.mA2dpAvrcpLock) {
synchronized (BtHelper.this) {
mA2dp = (BluetoothA2dp) proxy;
deviceList = mA2dp.getConnectedDevices();
if (deviceList.size() > 0) {
@@ -495,7 +493,7 @@ public class BtHelper {
break;
case BluetoothProfile.HEADSET:
synchronized (mScoClients) {
synchronized (BtHelper.this) {
// Discard timeout message
mDeviceBroker.handleCancelFailureToConnectToBtHeadsetService();
mBluetoothHeadset = (BluetoothHeadset) proxy;
@@ -536,17 +534,19 @@ public class BtHelper {
break;
case BluetoothProfile.HEARING_AID:
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");
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");
}
}
break;
@@ -579,18 +579,9 @@ public class BtHelper {
}
};
void disconnectAllBluetoothProfiles() {
mDeviceBroker.handleDisconnectA2dp();
mDeviceBroker.handleDisconnectA2dpSink();
disconnectHeadset();
mDeviceBroker.handleDisconnectHearingAid();
}
private void disconnectHeadset() {
synchronized (mScoClients) {
setBtScoActiveDevice(null);
mBluetoothHeadset = null;
}
setBtScoActiveDevice(null);
mBluetoothHeadset = null;
}
//----------------------------------------------------------------------
@@ -605,8 +596,12 @@ public class BtHelper {
mStartcount = 0;
}
@Override
public void binderDied() {
synchronized (mScoClients) {
// this is the only place the implementation of ScoClient needs to be synchronized
// on the instance, as all other methods are directly or indirectly called from
// package-private methods, which are synchronized
synchronized (BtHelper.this) {
Log.w(TAG, "SCO client died");
int index = mScoClients.indexOf(this);
if (index < 0) {
@@ -618,77 +613,69 @@ public class BtHelper {
}
}
public void incCount(int scoAudioMode) {
synchronized (mScoClients) {
requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, scoAudioMode);
if (mStartcount == 0) {
try {
mCb.linkToDeath(this, 0);
} catch (RemoteException e) {
// client has already died!
Log.w(TAG, "ScoClient incCount() could not link to "
+ mCb + " binder death");
}
}
mStartcount++;
}
}
public void decCount() {
synchronized (mScoClients) {
if (mStartcount == 0) {
Log.w(TAG, "ScoClient.decCount() already 0");
} else {
mStartcount--;
if (mStartcount == 0) {
try {
mCb.unlinkToDeath(this, 0);
} catch (NoSuchElementException e) {
Log.w(TAG, "decCount() going to 0 but not registered to binder");
}
}
requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, 0);
void incCount(int scoAudioMode) {
requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, scoAudioMode);
if (mStartcount == 0) {
try {
mCb.linkToDeath(this, 0);
} catch (RemoteException e) {
// client has already died!
Log.w(TAG, "ScoClient incCount() could not link to "
+ mCb + " binder death");
}
}
mStartcount++;
}
public void clearCount(boolean stopSco) {
synchronized (mScoClients) {
if (mStartcount != 0) {
void decCount() {
if (mStartcount == 0) {
Log.w(TAG, "ScoClient.decCount() already 0");
} else {
mStartcount--;
if (mStartcount == 0) {
try {
mCb.unlinkToDeath(this, 0);
} catch (NoSuchElementException e) {
Log.w(TAG, "clearCount() mStartcount: "
+ mStartcount + " != 0 but not registered to binder");
Log.w(TAG, "decCount() going to 0 but not registered to binder");
}
}
mStartcount = 0;
if (stopSco) {
requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, 0);
}
requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, 0);
}
}
public int getCount() {
void clearCount(boolean stopSco) {
if (mStartcount != 0) {
try {
mCb.unlinkToDeath(this, 0);
} catch (NoSuchElementException e) {
Log.w(TAG, "clearCount() mStartcount: "
+ mStartcount + " != 0 but not registered to binder");
}
}
mStartcount = 0;
if (stopSco) {
requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, 0);
}
}
int getCount() {
return mStartcount;
}
public IBinder getBinder() {
IBinder getBinder() {
return mCb;
}
public int getPid() {
int getPid() {
return mCreatorPid;
}
public int totalCount() {
synchronized (mScoClients) {
int count = 0;
for (ScoClient mScoClient : mScoClients) {
count += mScoClient.getCount();
}
return count;
private int totalCount() {
int count = 0;
for (ScoClient mScoClient : mScoClients) {
count += mScoClient.getCount();
}
return count;
}
private void requestScoState(int state, int scoAudioMode) {
@@ -705,6 +692,7 @@ public class BtHelper {
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING);
// Accept SCO audio activation only in NORMAL audio mode or if the mode is
// currently controlled by the same client process.
// TODO do not sync that way, see b/123769055
synchronized (mDeviceBroker.mSetModeLock) {
int modeOwnerPid = mDeviceBroker.getSetModeDeathHandlers().isEmpty()
? 0 : mDeviceBroker.getSetModeDeathHandlers().get(0).getPid();
@@ -857,61 +845,44 @@ public class BtHelper {
}
}
/*package*/ void resetBluetoothSco() {
synchronized (mScoClients) {
clearAllScoClients(0, false);
mScoAudioState = SCO_STATE_INACTIVE;
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
}
AudioSystem.setParameters("A2dpSuspended=false");
mDeviceBroker.setBluetoothScoOn(false, "resetBluetoothSco");
}
private void checkScoAudioState() {
synchronized (mScoClients) {
if (mBluetoothHeadset != null
&& mBluetoothHeadsetDevice != null
&& mScoAudioState == SCO_STATE_INACTIVE
&& mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice)
!= BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
}
if (mBluetoothHeadset != null
&& mBluetoothHeadsetDevice != null
&& mScoAudioState == SCO_STATE_INACTIVE
&& mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice)
!= BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
}
}
private ScoClient getScoClient(IBinder cb, boolean create) {
synchronized (mScoClients) {
for (ScoClient existingClient : mScoClients) {
if (existingClient.getBinder() == cb) {
return existingClient;
}
for (ScoClient existingClient : mScoClients) {
if (existingClient.getBinder() == cb) {
return existingClient;
}
if (create) {
ScoClient newClient = new ScoClient(cb);
mScoClients.add(newClient);
return newClient;
}
return null;
}
if (create) {
ScoClient newClient = new ScoClient(cb);
mScoClients.add(newClient);
return newClient;
}
return null;
}
private void clearAllScoClients(int exceptPid, boolean stopSco) {
synchronized (mScoClients) {
ScoClient savedClient = null;
for (ScoClient cl : mScoClients) {
if (cl.getPid() != exceptPid) {
cl.clearCount(stopSco);
} else {
savedClient = cl;
}
}
mScoClients.clear();
if (savedClient != null) {
mScoClients.add(savedClient);
ScoClient savedClient = null;
for (ScoClient cl : mScoClients) {
if (cl.getPid() != exceptPid) {
cl.clearCount(stopSco);
} else {
savedClient = cl;
}
}
mScoClients.clear();
if (savedClient != null) {
mScoClients.add(savedClient);
}
}
private boolean getBluetoothHeadset() {