Merge "AudioService: fix audio mode lock" into qt-dev

This commit is contained in:
Jean-Michel Trivi
2019-04-17 14:26:44 +00:00
committed by Android (Google) Code Review
3 changed files with 159 additions and 87 deletions

View File

@@ -114,8 +114,10 @@ import java.util.ArrayList;
// All post* methods are asynchronous // All post* methods are asynchronous
/*package*/ void onSystemReady() { /*package*/ void onSystemReady() {
synchronized (mDeviceStateLock) { synchronized (mSetModeLock) {
mBtHelper.onSystemReady(); synchronized (mDeviceStateLock) {
mBtHelper.onSystemReady();
}
} }
} }
@@ -151,8 +153,10 @@ import java.util.ArrayList;
* @param intent * @param intent
*/ */
/*package*/ void receiveBtEvent(@NonNull Intent intent) { /*package*/ void receiveBtEvent(@NonNull Intent intent) {
synchronized (mDeviceStateLock) { synchronized (mSetModeLock) {
mBtHelper.receiveBtEvent(intent); synchronized (mDeviceStateLock) {
mBtHelper.receiveBtEvent(intent);
}
} }
} }
@@ -350,13 +354,19 @@ import java.util.ArrayList;
sendLMsgNoDelay(MSG_L_A2DP_DEVICE_CONFIG_CHANGE, SENDMSG_QUEUE, device); sendLMsgNoDelay(MSG_L_A2DP_DEVICE_CONFIG_CHANGE, SENDMSG_QUEUE, device);
} }
@GuardedBy("mSetModeLock")
/*package*/ void startBluetoothScoForClient_Sync(IBinder cb, int scoAudioMode, /*package*/ void startBluetoothScoForClient_Sync(IBinder cb, int scoAudioMode,
@NonNull String eventSource) { @NonNull String eventSource) {
mBtHelper.startBluetoothScoForClient(cb, scoAudioMode, eventSource); synchronized (mDeviceStateLock) {
mBtHelper.startBluetoothScoForClient(cb, scoAudioMode, eventSource);
}
} }
@GuardedBy("mSetModeLock")
/*package*/ void stopBluetoothScoForClient_Sync(IBinder cb, @NonNull String eventSource) { /*package*/ void stopBluetoothScoForClient_Sync(IBinder cb, @NonNull String eventSource) {
mBtHelper.stopBluetoothScoForClient(cb, eventSource); synchronized (mDeviceStateLock) {
mBtHelper.stopBluetoothScoForClient(cb, eventSource);
}
} }
//--------------------------------------------------------------------- //---------------------------------------------------------------------
@@ -479,6 +489,10 @@ import java.util.ArrayList;
hearingAidProfile); hearingAidProfile);
} }
/*package*/ void postScoClientDied(Object obj) {
sendLMsgNoDelay(MSG_L_SCOCLIENT_DIED, SENDMSG_QUEUE, obj);
}
//--------------------------------------------------------------------- //---------------------------------------------------------------------
// Method forwarding between the helper classes (BtHelper, AudioDeviceInventory) // Method forwarding between the helper classes (BtHelper, AudioDeviceInventory)
// only call from a "handle"* method or "on"* method // only call from a "handle"* method or "on"* method
@@ -708,8 +722,10 @@ import java.util.ArrayList;
} }
break; break;
case MSG_BT_HEADSET_CNCT_FAILED: case MSG_BT_HEADSET_CNCT_FAILED:
synchronized (mDeviceStateLock) { synchronized (mSetModeLock) {
mBtHelper.resetBluetoothSco(); synchronized (mDeviceStateLock) {
mBtHelper.resetBluetoothSco();
}
} }
break; break;
case MSG_IL_BTA2DP_DOCK_TIMEOUT: case MSG_IL_BTA2DP_DOCK_TIMEOUT:
@@ -742,8 +758,17 @@ import java.util.ArrayList;
} }
break; break;
case MSG_I_DISCONNECT_BT_SCO: case MSG_I_DISCONNECT_BT_SCO:
synchronized (mDeviceStateLock) { synchronized (mSetModeLock) {
mBtHelper.disconnectBluetoothSco(msg.arg1); synchronized (mDeviceStateLock) {
mBtHelper.disconnectBluetoothSco(msg.arg1);
}
}
break;
case MSG_L_SCOCLIENT_DIED:
synchronized (mSetModeLock) {
synchronized (mDeviceStateLock) {
mBtHelper.scoClientDied(msg.arg1);
}
} }
break; break;
case MSG_TOGGLE_HDMI: case MSG_TOGGLE_HDMI:
@@ -774,8 +799,10 @@ import java.util.ArrayList;
} }
break; break;
case MSG_DISCONNECT_BT_HEADSET: case MSG_DISCONNECT_BT_HEADSET:
synchronized (mDeviceStateLock) { synchronized (mSetModeLock) {
mBtHelper.disconnectHeadset(); synchronized (mDeviceStateLock) {
mBtHelper.disconnectHeadset();
}
} }
break; break;
case MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP: case MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP:
@@ -794,8 +821,10 @@ import java.util.ArrayList;
} }
break; break;
case MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEADSET: case MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEADSET:
synchronized (mDeviceStateLock) { synchronized (mSetModeLock) {
mBtHelper.onHeadsetProfileConnected((BluetoothHeadset) msg.obj); synchronized (mDeviceStateLock) {
mBtHelper.onHeadsetProfileConnected((BluetoothHeadset) msg.obj);
}
} }
break; break;
case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT: { case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT: {
@@ -892,6 +921,8 @@ import java.util.ArrayList;
private static final int MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT = 28; private static final int MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT = 28;
// process external command to (dis)connect or change active A2DP device // process external command to (dis)connect or change active A2DP device
private static final int MSG_L_A2DP_ACTIVE_DEVICE_CHANGE_EXT = 29; private static final int MSG_L_A2DP_ACTIVE_DEVICE_CHANGE_EXT = 29;
// a ScoClient died in BtHelper
private static final int MSG_L_SCOCLIENT_DIED = 30;
private static boolean isMessageHandledUnderWakelock(int msgId) { private static boolean isMessageHandledUnderWakelock(int msgId) {

View File

@@ -3484,7 +3484,9 @@ public class AudioService extends IAudioService.Stub
!mSystemReady) { !mSystemReady) {
return; return;
} }
mDeviceBroker.startBluetoothScoForClient_Sync(cb, scoAudioMode, eventSource); synchronized (mDeviceBroker.mSetModeLock) {
mDeviceBroker.startBluetoothScoForClient_Sync(cb, scoAudioMode, eventSource);
}
} }
/** @see AudioManager#stopBluetoothSco() */ /** @see AudioManager#stopBluetoothSco() */
@@ -3496,7 +3498,9 @@ public class AudioService extends IAudioService.Stub
final String eventSource = new StringBuilder("stopBluetoothSco()") final String eventSource = new StringBuilder("stopBluetoothSco()")
.append(") from u/pid:").append(Binder.getCallingUid()).append("/") .append(") from u/pid:").append(Binder.getCallingUid()).append("/")
.append(Binder.getCallingPid()).toString(); .append(Binder.getCallingPid()).toString();
mDeviceBroker.stopBluetoothScoForClient_Sync(cb, eventSource); synchronized (mDeviceBroker.mSetModeLock) {
mDeviceBroker.stopBluetoothScoForClient_Sync(cb, eventSource);
}
} }

View File

@@ -36,6 +36,8 @@ import android.os.UserHandle;
import android.provider.Settings; import android.provider.Settings;
import android.util.Log; import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
@@ -163,6 +165,8 @@ public class BtHelper {
//---------------------------------------------------------------------- //----------------------------------------------------------------------
// Interface for AudioDeviceBroker // Interface for AudioDeviceBroker
// @GuardedBy("AudioDeviceBroker.mSetModeLock")
@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void onSystemReady() { /*package*/ synchronized void onSystemReady() {
mScoConnectionState = android.media.AudioManager.SCO_AUDIO_STATE_ERROR; mScoConnectionState = android.media.AudioManager.SCO_AUDIO_STATE_ERROR;
resetBluetoothSco(); resetBluetoothSco();
@@ -231,6 +235,8 @@ public class BtHelper {
return mapBluetoothCodecToAudioFormat(btCodecConfig.getCodecType()); return mapBluetoothCodecToAudioFormat(btCodecConfig.getCodecType());
} }
// @GuardedBy("AudioDeviceBroker.mSetModeLock")
@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void receiveBtEvent(Intent intent) { /*package*/ synchronized void receiveBtEvent(Intent intent) {
final String action = intent.getAction(); final String action = intent.getAction();
if (action.equals(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED)) { if (action.equals(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED)) {
@@ -317,6 +323,8 @@ public class BtHelper {
* *
* @param exceptPid pid whose SCO connections through {@link AudioManager} should be kept * @param exceptPid pid whose SCO connections through {@link AudioManager} should be kept
*/ */
// @GuardedBy("AudioDeviceBroker.mSetModeLock")
@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void disconnectBluetoothSco(int exceptPid) { /*package*/ synchronized void disconnectBluetoothSco(int exceptPid) {
checkScoAudioState(); checkScoAudioState();
if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL) { if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL) {
@@ -325,6 +333,8 @@ public class BtHelper {
clearAllScoClients(exceptPid, true); clearAllScoClients(exceptPid, true);
} }
// @GuardedBy("AudioDeviceBroker.mSetModeLock")
@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void startBluetoothScoForClient(IBinder cb, int scoAudioMode, /*package*/ synchronized void startBluetoothScoForClient(IBinder cb, int scoAudioMode,
@NonNull String eventSource) { @NonNull String eventSource) {
ScoClient client = getScoClient(cb, true); ScoClient client = getScoClient(cb, true);
@@ -344,6 +354,8 @@ public class BtHelper {
Binder.restoreCallingIdentity(ident); Binder.restoreCallingIdentity(ident);
} }
// @GuardedBy("AudioDeviceBroker.mSetModeLock")
@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void stopBluetoothScoForClient(IBinder cb, /*package*/ synchronized void stopBluetoothScoForClient(IBinder cb,
@NonNull String eventSource) { @NonNull String eventSource) {
ScoClient client = getScoClient(cb, false); ScoClient client = getScoClient(cb, false);
@@ -401,6 +413,8 @@ public class BtHelper {
mDeviceBroker.postDisconnectHearingAid(); mDeviceBroker.postDisconnectHearingAid();
} }
// @GuardedBy("AudioDeviceBroker.mSetModeLock")
@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void resetBluetoothSco() { /*package*/ synchronized void resetBluetoothSco() {
clearAllScoClients(0, false); clearAllScoClients(0, false);
mScoAudioState = SCO_STATE_INACTIVE; mScoAudioState = SCO_STATE_INACTIVE;
@@ -409,6 +423,8 @@ public class BtHelper {
mDeviceBroker.setBluetoothScoOn(false, "resetBluetoothSco"); mDeviceBroker.setBluetoothScoOn(false, "resetBluetoothSco");
} }
// @GuardedBy("AudioDeviceBroker.mSetModeLock")
@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void disconnectHeadset() { /*package*/ synchronized void disconnectHeadset() {
setBtScoActiveDevice(null); setBtScoActiveDevice(null);
mBluetoothHeadset = null; mBluetoothHeadset = null;
@@ -454,6 +470,8 @@ public class BtHelper {
/*eventSource*/ "mBluetoothProfileServiceListener"); /*eventSource*/ "mBluetoothProfileServiceListener");
} }
// @GuardedBy("AudioDeviceBroker.mSetModeLock")
@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void onHeadsetProfileConnected(BluetoothHeadset headset) { /*package*/ synchronized void onHeadsetProfileConnected(BluetoothHeadset headset) {
// Discard timeout message // Discard timeout message
mDeviceBroker.handleCancelFailureToConnectToBtHeadsetService(); mDeviceBroker.handleCancelFailureToConnectToBtHeadsetService();
@@ -540,6 +558,9 @@ public class BtHelper {
return result; return result;
} }
// @GuardedBy("AudioDeviceBroker.mSetModeLock")
//@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
@GuardedBy("BtHelper.this")
private void setBtScoActiveDevice(BluetoothDevice btDevice) { private void setBtScoActiveDevice(BluetoothDevice btDevice) {
Log.i(TAG, "setBtScoActiveDevice: " + mBluetoothHeadsetDevice + " -> " + btDevice); Log.i(TAG, "setBtScoActiveDevice: " + mBluetoothHeadsetDevice + " -> " + btDevice);
final BluetoothDevice previousActiveDevice = mBluetoothHeadsetDevice; final BluetoothDevice previousActiveDevice = mBluetoothHeadsetDevice;
@@ -621,6 +642,20 @@ public class BtHelper {
}; };
//---------------------------------------------------------------------- //----------------------------------------------------------------------
// @GuardedBy("AudioDeviceBroker.mSetModeLock")
@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
/*package*/ synchronized void scoClientDied(Object obj) {
final ScoClient client = (ScoClient) obj;
Log.w(TAG, "SCO client died");
int index = mScoClients.indexOf(client);
if (index < 0) {
Log.w(TAG, "unregistered SCO client died");
} else {
client.clearCount(true);
mScoClients.remove(client);
}
}
private class ScoClient implements IBinder.DeathRecipient { private class ScoClient implements IBinder.DeathRecipient {
private IBinder mCb; // To be notified of client's death private IBinder mCb; // To be notified of client's death
private int mCreatorPid; private int mCreatorPid;
@@ -634,21 +669,14 @@ public class BtHelper {
@Override @Override
public void binderDied() { public void binderDied() {
// this is the only place the implementation of ScoClient needs to be synchronized // process this from DeviceBroker's message queue to take the right locks since
// on the instance, as all other methods are directly or indirectly called from // this event can impact SCO mode and requires querying audio mode stack
// package-private methods, which are synchronized mDeviceBroker.postScoClientDied(this);
synchronized (BtHelper.this) {
Log.w(TAG, "SCO client died");
int index = mScoClients.indexOf(this);
if (index < 0) {
Log.w(TAG, "unregistered SCO client died");
} else {
clearCount(true);
mScoClients.remove(this);
}
}
} }
// @GuardedBy("AudioDeviceBroker.mSetModeLock")
// @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
@GuardedBy("BtHelper.this")
void incCount(int scoAudioMode) { void incCount(int scoAudioMode) {
requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, scoAudioMode); requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, scoAudioMode);
if (mStartcount == 0) { if (mStartcount == 0) {
@@ -663,6 +691,9 @@ public class BtHelper {
mStartcount++; mStartcount++;
} }
// @GuardedBy("AudioDeviceBroker.mSetModeLock")
// @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
@GuardedBy("BtHelper.this")
void decCount() { void decCount() {
if (mStartcount == 0) { if (mStartcount == 0) {
Log.w(TAG, "ScoClient.decCount() already 0"); Log.w(TAG, "ScoClient.decCount() already 0");
@@ -679,6 +710,9 @@ public class BtHelper {
} }
} }
// @GuardedBy("AudioDeviceBroker.mSetModeLock")
// @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
@GuardedBy("BtHelper.this")
void clearCount(boolean stopSco) { void clearCount(boolean stopSco) {
if (mStartcount != 0) { if (mStartcount != 0) {
try { try {
@@ -714,6 +748,9 @@ public class BtHelper {
return count; return count;
} }
// @GuardedBy("AudioDeviceBroker.mSetModeLock")
//@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
@GuardedBy("BtHelper.this")
private void requestScoState(int state, int scoAudioMode) { private void requestScoState(int state, int scoAudioMode) {
checkScoAudioState(); checkScoAudioState();
int clientCount = totalCount(); int clientCount = totalCount();
@@ -728,74 +765,71 @@ public class BtHelper {
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING); broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING);
// Accept SCO audio activation only in NORMAL audio mode or if the mode is // Accept SCO audio activation only in NORMAL audio mode or if the mode is
// currently controlled by the same client process. // currently controlled by the same client process.
// TODO do not sync that way, see b/123769055 int modeOwnerPid = mDeviceBroker.getSetModeDeathHandlers().isEmpty()
synchronized (mDeviceBroker.mSetModeLock) { ? 0 : mDeviceBroker.getSetModeDeathHandlers().get(0).getPid();
int modeOwnerPid = mDeviceBroker.getSetModeDeathHandlers().isEmpty() if (modeOwnerPid != 0 && (modeOwnerPid != mCreatorPid)) {
? 0 : mDeviceBroker.getSetModeDeathHandlers().get(0).getPid(); Log.w(TAG, "requestScoState: audio mode is not NORMAL and modeOwnerPid "
if (modeOwnerPid != 0 && (modeOwnerPid != mCreatorPid)) { + modeOwnerPid + " != creatorPid " + mCreatorPid);
Log.w(TAG, "requestScoState: audio mode is not NORMAL and modeOwnerPid " broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+ modeOwnerPid + " != creatorPid " + mCreatorPid); return;
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); }
return; switch (mScoAudioState) {
} case SCO_STATE_INACTIVE:
switch (mScoAudioState) { mScoAudioMode = scoAudioMode;
case SCO_STATE_INACTIVE: if (scoAudioMode == SCO_MODE_UNDEFINED) {
mScoAudioMode = scoAudioMode; mScoAudioMode = SCO_MODE_VIRTUAL_CALL;
if (scoAudioMode == SCO_MODE_UNDEFINED) { if (mBluetoothHeadsetDevice != null) {
mScoAudioMode = SCO_MODE_VIRTUAL_CALL; mScoAudioMode = Settings.Global.getInt(
if (mBluetoothHeadsetDevice != null) { mDeviceBroker.getContentResolver(),
mScoAudioMode = Settings.Global.getInt( "bluetooth_sco_channel_"
mDeviceBroker.getContentResolver(), + mBluetoothHeadsetDevice.getAddress(),
"bluetooth_sco_channel_" SCO_MODE_VIRTUAL_CALL);
+ mBluetoothHeadsetDevice.getAddress(), if (mScoAudioMode > SCO_MODE_MAX || mScoAudioMode < 0) {
SCO_MODE_VIRTUAL_CALL); mScoAudioMode = SCO_MODE_VIRTUAL_CALL;
if (mScoAudioMode > SCO_MODE_MAX || mScoAudioMode < 0) {
mScoAudioMode = SCO_MODE_VIRTUAL_CALL;
}
} }
} }
if (mBluetoothHeadset == null) { }
if (getBluetoothHeadset()) { if (mBluetoothHeadset == null) {
mScoAudioState = SCO_STATE_ACTIVATE_REQ; if (getBluetoothHeadset()) {
} else { mScoAudioState = SCO_STATE_ACTIVATE_REQ;
Log.w(TAG, "requestScoState: getBluetoothHeadset failed during"
+ " connection, mScoAudioMode=" + mScoAudioMode);
broadcastScoConnectionState(
AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
}
break;
}
if (mBluetoothHeadsetDevice == null) {
Log.w(TAG, "requestScoState: no active device while connecting,"
+ " mScoAudioMode=" + mScoAudioMode);
broadcastScoConnectionState(
AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
break;
}
if (connectBluetoothScoAudioHelper(mBluetoothHeadset,
mBluetoothHeadsetDevice, mScoAudioMode)) {
mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
} else { } else {
Log.w(TAG, "requestScoState: connect to " + mBluetoothHeadsetDevice Log.w(TAG, "requestScoState: getBluetoothHeadset failed during"
+ " failed, mScoAudioMode=" + mScoAudioMode); + " connection, mScoAudioMode=" + mScoAudioMode);
broadcastScoConnectionState( broadcastScoConnectionState(
AudioManager.SCO_AUDIO_STATE_DISCONNECTED); AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
} }
break; break;
case SCO_STATE_DEACTIVATING: }
mScoAudioState = SCO_STATE_ACTIVATE_REQ; if (mBluetoothHeadsetDevice == null) {
Log.w(TAG, "requestScoState: no active device while connecting,"
+ " mScoAudioMode=" + mScoAudioMode);
broadcastScoConnectionState(
AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
break; break;
case SCO_STATE_DEACTIVATE_REQ: }
if (connectBluetoothScoAudioHelper(mBluetoothHeadset,
mBluetoothHeadsetDevice, mScoAudioMode)) {
mScoAudioState = SCO_STATE_ACTIVE_INTERNAL; mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED); } else {
break; Log.w(TAG, "requestScoState: connect to " + mBluetoothHeadsetDevice
default: + " failed, mScoAudioMode=" + mScoAudioMode);
Log.w(TAG, "requestScoState: failed to connect in state " broadcastScoConnectionState(
+ mScoAudioState + ", scoAudioMode=" + scoAudioMode); AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); }
break; break;
case SCO_STATE_DEACTIVATING:
mScoAudioState = SCO_STATE_ACTIVATE_REQ;
break;
case SCO_STATE_DEACTIVATE_REQ:
mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED);
break;
default:
Log.w(TAG, "requestScoState: failed to connect in state "
+ mScoAudioState + ", scoAudioMode=" + scoAudioMode);
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
break;
}
} }
} else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { } else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
switch (mScoAudioState) { switch (mScoAudioState) {
@@ -906,6 +940,9 @@ public class BtHelper {
return null; return null;
} }
// @GuardedBy("AudioDeviceBroker.mSetModeLock")
//@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
@GuardedBy("BtHelper.this")
private void clearAllScoClients(int exceptPid, boolean stopSco) { private void clearAllScoClients(int exceptPid, boolean stopSco) {
ScoClient savedClient = null; ScoClient savedClient = null;
for (ScoClient cl : mScoClients) { for (ScoClient cl : mScoClients) {