Merge "Bluetooth SCO audio API improvements."
diff --git a/api/current.txt b/api/current.txt
index 8c61922..6854965 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -9687,7 +9687,8 @@
method public void unloadSoundEffects();
method public void unregisterMediaButtonEventReceiver(android.content.ComponentName);
field public static final java.lang.String ACTION_AUDIO_BECOMING_NOISY = "android.media.AUDIO_BECOMING_NOISY";
- field public static final java.lang.String ACTION_SCO_AUDIO_STATE_CHANGED = "android.media.SCO_AUDIO_STATE_CHANGED";
+ field public static final deprecated java.lang.String ACTION_SCO_AUDIO_STATE_CHANGED = "android.media.SCO_AUDIO_STATE_CHANGED";
+ field public static final java.lang.String ACTION_SCO_AUDIO_STATE_UPDATED = "android.media.ACTION_SCO_AUDIO_STATE_UPDATED";
field public static final int ADJUST_LOWER = -1; // 0xffffffff
field public static final int ADJUST_RAISE = 1; // 0x1
field public static final int ADJUST_SAME = 0; // 0x0
@@ -9700,6 +9701,7 @@
field public static final int AUDIOFOCUS_REQUEST_FAILED = 0; // 0x0
field public static final int AUDIOFOCUS_REQUEST_GRANTED = 1; // 0x1
field public static final java.lang.String EXTRA_RINGER_MODE = "android.media.EXTRA_RINGER_MODE";
+ field public static final java.lang.String EXTRA_SCO_AUDIO_PREVIOUS_STATE = "android.media.extra.SCO_AUDIO_PREVIOUS_STATE";
field public static final java.lang.String EXTRA_SCO_AUDIO_STATE = "android.media.extra.SCO_AUDIO_STATE";
field public static final java.lang.String EXTRA_VIBRATE_SETTING = "android.media.EXTRA_VIBRATE_SETTING";
field public static final java.lang.String EXTRA_VIBRATE_TYPE = "android.media.EXTRA_VIBRATE_TYPE";
@@ -9736,6 +9738,7 @@
field public static final deprecated int ROUTE_HEADSET = 8; // 0x8
field public static final deprecated int ROUTE_SPEAKER = 2; // 0x2
field public static final int SCO_AUDIO_STATE_CONNECTED = 1; // 0x1
+ field public static final int SCO_AUDIO_STATE_CONNECTING = 2; // 0x2
field public static final int SCO_AUDIO_STATE_DISCONNECTED = 0; // 0x0
field public static final int SCO_AUDIO_STATE_ERROR = -1; // 0xffffffff
field public static final int STREAM_ALARM = 4; // 0x4
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index e1daede..253010c 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -828,29 +828,64 @@
* or {@link #SCO_AUDIO_STATE_CONNECTED}
*
* @see #startBluetoothSco()
+ * @deprecated Use {@link #ACTION_SCO_AUDIO_STATE_UPDATED} instead
*/
+ @Deprecated
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_SCO_AUDIO_STATE_CHANGED =
"android.media.SCO_AUDIO_STATE_CHANGED";
+
+ /**
+ * Sticky broadcast intent action indicating that the bluetoooth SCO audio
+ * connection state has been updated.
+ * <p>This intent has two extras:
+ * <ul>
+ * <li> {@link #EXTRA_SCO_AUDIO_STATE} - The new SCO audio state. </li>
+ * <li> {@link #EXTRA_SCO_AUDIO_PREVIOUS_STATE}- The previous SCO audio state. </li>
+ * </ul>
+ * <p> EXTRA_SCO_AUDIO_STATE or EXTRA_SCO_AUDIO_PREVIOUS_STATE can be any of:
+ * <ul>
+ * <li> {@link #SCO_AUDIO_STATE_DISCONNECTED}, </li>
+ * <li> {@link #SCO_AUDIO_STATE_CONNECTING} or </li>
+ * <li> {@link #SCO_AUDIO_STATE_CONNECTED}, </li>
+ * </ul>
+ * @see #startBluetoothSco()
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_SCO_AUDIO_STATE_UPDATED =
+ "android.media.ACTION_SCO_AUDIO_STATE_UPDATED";
+
/**
- * Extra for intent {@link #ACTION_SCO_AUDIO_STATE_CHANGED} containing the new
- * bluetooth SCO connection state.
+ * Extra for intent {@link #ACTION_SCO_AUDIO_STATE_CHANGED} or
+ * {@link #ACTION_SCO_AUDIO_STATE_UPDATED} containing the new bluetooth SCO connection state.
*/
public static final String EXTRA_SCO_AUDIO_STATE =
"android.media.extra.SCO_AUDIO_STATE";
/**
- * Value for extra {@link #EXTRA_SCO_AUDIO_STATE} indicating that the
- * SCO audio channel is not established
+ * Extra for intent {@link #ACTION_SCO_AUDIO_STATE_UPDATED} containing the previous
+ * bluetooth SCO connection state.
+ */
+ public static final String EXTRA_SCO_AUDIO_PREVIOUS_STATE =
+ "android.media.extra.SCO_AUDIO_PREVIOUS_STATE";
+
+ /**
+ * Value for extra EXTRA_SCO_AUDIO_STATE or EXTRA_SCO_AUDIO_PREVIOUS_STATE
+ * indicating that the SCO audio channel is not established
*/
public static final int SCO_AUDIO_STATE_DISCONNECTED = 0;
/**
- * Value for extra {@link #EXTRA_SCO_AUDIO_STATE} indicating that the
- * SCO audio channel is established
+ * Value for extra {@link #EXTRA_SCO_AUDIO_STATE} or {@link #EXTRA_SCO_AUDIO_PREVIOUS_STATE}
+ * indicating that the SCO audio channel is established
*/
public static final int SCO_AUDIO_STATE_CONNECTED = 1;
/**
- * Value for extra {@link #EXTRA_SCO_AUDIO_STATE} indicating that
+ * Value for extra EXTRA_SCO_AUDIO_STATE or EXTRA_SCO_AUDIO_PREVIOUS_STATE
+ * indicating that the SCO audio channel is being established
+ */
+ public static final int SCO_AUDIO_STATE_CONNECTING = 2;
+ /**
+ * Value for extra EXTRA_SCO_AUDIO_STATE indicating that
* there was an error trying to obtain the state
*/
public static final int SCO_AUDIO_STATE_ERROR = -1;
@@ -878,29 +913,37 @@
* to/from a bluetooth SCO headset while the phone is not in call.
* <p>As the SCO connection establishment can take several seconds,
* applications should not rely on the connection to be available when the method
- * returns but instead register to receive the intent {@link #ACTION_SCO_AUDIO_STATE_CHANGED}
+ * returns but instead register to receive the intent {@link #ACTION_SCO_AUDIO_STATE_UPDATED}
* and wait for the state to be {@link #SCO_AUDIO_STATE_CONNECTED}.
- * <p>As the connection is not guaranteed to succeed, applications must wait for this intent with
- * a timeout.
- * <p>When finished with the SCO connection or if the establishment times out,
- * the application must call {@link #stopBluetoothSco()} to clear the request and turn
- * down the bluetooth connection.
+ * <p>As the ACTION_SCO_AUDIO_STATE_UPDATED intent is sticky, the application can check the SCO
+ * audio state before calling startBluetoothSco() by reading the intent returned by the receiver
+ * registration. If the state is already CONNECTED, no state change will be received via the
+ * intent after calling startBluetoothSco(). It is however useful to call startBluetoothSco()
+ * so that the connection stays active in case the current initiator stops the connection.
+ * <p>Unless the connection is already active as described above, the state will always
+ * transition from DISCONNECTED to CONNECTING and then either to CONNECTED if the connection
+ * succeeds or back to DISCONNECTED if the connection fails (e.g no headset is connected).
+ * <p>When finished with the SCO connection or if the establishment fails, the application must
+ * call {@link #stopBluetoothSco()} to clear the request and turn down the bluetooth connection.
* <p>Even if a SCO connection is established, the following restrictions apply on audio
* output streams so that they can be routed to SCO headset:
- * - the stream type must be {@link #STREAM_VOICE_CALL}
- * - the format must be mono
- * - the sampling must be 16kHz or 8kHz
+ * <ul>
+ * <li> the stream type must be {@link #STREAM_VOICE_CALL} </li>
+ * <li> the format must be mono </li>
+ * <li> the sampling must be 16kHz or 8kHz </li>
+ * </ul>
* <p>The following restrictions apply on input streams:
- * - the format must be mono
- * - the sampling must be 8kHz
- *
+ * <ul>
+ * <li> the format must be mono </li>
+ * <li> the sampling must be 8kHz </li>
+ * </ul>
* <p>Note that the phone application always has the priority on the usage of the SCO
* connection for telephony. If this method is called while the phone is in call
* it will be ignored. Similarly, if a call is received or sent while an application
* is using the SCO connection, the connection will be lost for the application and NOT
* returned automatically when the call ends.
* @see #stopBluetoothSco()
- * @see #ACTION_SCO_AUDIO_STATE_CHANGED
+ * @see #ACTION_SCO_AUDIO_STATE_UPDATED
*/
public void startBluetoothSco(){
IAudioService service = getService();
@@ -917,7 +960,7 @@
* {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}.
* <p>This method must be called by applications having requested the use of
* bluetooth SCO audio with {@link #startBluetoothSco()}
- * when finished with the SCO connection or if the establishment times out.
+ * when finished with the SCO connection or if connection fails.
* @see #startBluetoothSco()
*/
public void stopBluetoothSco(){
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 504cfde..4e77fcb 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -112,9 +112,12 @@
private static final int MSG_LOAD_SOUND_EFFECTS = 9;
private static final int MSG_SET_FORCE_USE = 10;
private static final int MSG_PERSIST_MEDIABUTTONRECEIVER = 11;
-
+ private static final int MSG_BT_HEADSET_CNCT_FAILED = 12;
private static final int BTA2DP_DOCK_TIMEOUT_MILLIS = 8000;
+ // Timeout for connection to bluetooth headset service
+ private static final int BT_HEADSET_CNCT_TIMEOUT_MS = 3000;
+
/** @see AudioSystemThread */
private AudioSystemThread mAudioSystemThread;
@@ -273,11 +276,22 @@
private int mScoAudioState;
// 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 or starting due to a local request to start a virtual call
- private static final int SCO_STATE_ACTIVE_INTERNAL = 1;
+ private static final int SCO_STATE_ACTIVE_INTERNAL = 3;
+ // SCO audio deactivation request waiting for headset service to connect
+ private static final int SCO_STATE_DEACTIVATE_REQ = 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;
+ // Deactivation request for all SCO connections (initiated by audio mode change)
+ // waiting for headset service to connect
+ private static final int SCO_STATE_DEACTIVATE_EXT_REQ = 4;
+
+ // Current connection state indicated by bluetooth headset
+ private int mScoConnectionState;
// true if boot sequence has been completed
private boolean mBootCompleted;
@@ -335,13 +349,6 @@
AudioSystem.setErrorCallback(mAudioSystemCallback);
- mBluetoothHeadsetDevice = null;
- BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
- if (adapter != null) {
- adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
- BluetoothProfile.HEADSET);
- }
-
// Register for device connection intent broadcasts.
IntentFilter intentFilter =
new IntentFilter(Intent.ACTION_HEADSET_PLUG);
@@ -768,17 +775,7 @@
if (AudioSystem.setPhoneState(mode) == AudioSystem.AUDIO_STATUS_OK) {
AudioService.this.mMode = mode;
if (mode != AudioSystem.MODE_NORMAL) {
- synchronized(mScoClients) {
- checkScoAudioState();
- if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL) {
- mBluetoothHeadset.stopVoiceRecognition(
- mBluetoothHeadsetDevice);
- mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
- mBluetoothHeadsetDevice);
- } else {
- clearAllScoClients(mCb, true);
- }
- }
+ disconnectBluetoothSco(mCb);
}
}
}
@@ -856,16 +853,7 @@
// when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
// SCO connections not started by the application changing the mode
if (mode != AudioSystem.MODE_NORMAL) {
- synchronized(mScoClients) {
- checkScoAudioState();
- if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL) {
- mBluetoothHeadset.stopVoiceRecognition(mBluetoothHeadsetDevice);
- mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
- mBluetoothHeadsetDevice);
- } else {
- clearAllScoClients(cb, true);
- }
- }
+ disconnectBluetoothSco(cb);
}
}
}
@@ -1213,7 +1201,8 @@
/** @see AudioManager#startBluetoothSco() */
public void startBluetoothSco(IBinder cb){
- if (!checkAudioSettingsPermission("startBluetoothSco()")) {
+ if (!checkAudioSettingsPermission("startBluetoothSco()") ||
+ !mBootCompleted) {
return;
}
ScoClient client = getScoClient(cb, true);
@@ -1222,7 +1211,8 @@
/** @see AudioManager#stopBluetoothSco() */
public void stopBluetoothSco(IBinder cb){
- if (!checkAudioSettingsPermission("stopBluetoothSco()")) {
+ if (!checkAudioSettingsPermission("stopBluetoothSco()") ||
+ !mBootCompleted) {
return;
}
ScoClient client = getScoClient(cb, false);
@@ -1322,25 +1312,57 @@
}
private void requestScoState(int state) {
- if (mBluetoothHeadset == null) {
- return;
- }
-
checkScoAudioState();
-
- if (totalCount() == 0 &&
- mBluetoothHeadsetDevice != null) {
- // Accept SCO audio activation only in NORMAL audio mode or if the mode is
- // currently controlled by the same client.
- if ((AudioService.this.mMode == AudioSystem.MODE_NORMAL ||
- mSetModeDeathHandlers.get(0).getBinder() == mCb) &&
- state == BluetoothHeadset.STATE_AUDIO_CONNECTED &&
- mScoAudioState == SCO_STATE_INACTIVE) {
- mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
- mBluetoothHeadset.startScoUsingVirtualVoiceCall(mBluetoothHeadsetDevice);
+ if (totalCount() == 0) {
+ if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
+ // Make sure that the state transitions to CONNECTING even if we cannot initiate
+ // the connection.
+ 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.
+ if ((AudioService.this.mMode == AudioSystem.MODE_NORMAL ||
+ mSetModeDeathHandlers.get(0).getBinder() == mCb) &&
+ mBluetoothHeadsetDevice != null &&
+ (mScoAudioState == SCO_STATE_INACTIVE ||
+ mScoAudioState == SCO_STATE_DEACTIVATE_REQ)) {
+ if (mScoAudioState == SCO_STATE_INACTIVE) {
+ if (mBluetoothHeadset != null) {
+ if (mBluetoothHeadset.startScoUsingVirtualVoiceCall(
+ mBluetoothHeadsetDevice)) {
+ mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
+ } else {
+ broadcastScoConnectionState(
+ AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+ }
+ } else if (getBluetoothHeadset()) {
+ mScoAudioState = SCO_STATE_ACTIVATE_REQ;
+ }
+ } else {
+ mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
+ broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED);
+ }
+ } else {
+ broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+ }
} else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED &&
- mScoAudioState == SCO_STATE_ACTIVE_INTERNAL){
- mBluetoothHeadset.stopScoUsingVirtualVoiceCall(mBluetoothHeadsetDevice);
+ mBluetoothHeadsetDevice != null &&
+ (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL ||
+ mScoAudioState == SCO_STATE_ACTIVATE_REQ)) {
+ if (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL) {
+ if (mBluetoothHeadset != null) {
+ if (!mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
+ mBluetoothHeadsetDevice)) {
+ mScoAudioState = SCO_STATE_INACTIVE;
+ broadcastScoConnectionState(
+ AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+ }
+ } else if (getBluetoothHeadset()) {
+ mScoAudioState = SCO_STATE_DEACTIVATE_REQ;
+ }
+ } else {
+ mScoAudioState = SCO_STATE_INACTIVE;
+ broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+ }
}
}
}
@@ -1348,7 +1370,7 @@
private void checkScoAudioState() {
if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null &&
- mScoAudioState != SCO_STATE_ACTIVE_INTERNAL &&
+ mScoAudioState == SCO_STATE_INACTIVE &&
mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice)
!= BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
@@ -1391,10 +1413,72 @@
}
}
+ private boolean getBluetoothHeadset() {
+ boolean result = false;
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ if (adapter != null) {
+ result = adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
+ BluetoothProfile.HEADSET);
+ }
+ // If we could not get a bluetooth headset proxy, send a failure message
+ // without delay to reset the SCO audio state and clear SCO clients.
+ // If we could get a proxy, send a delayed failure message that will reset our state
+ // in case we don't receive onServiceConnected().
+ sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED, 0,
+ SENDMSG_REPLACE, 0, 0, null, result ? BT_HEADSET_CNCT_TIMEOUT_MS : 0);
+ return result;
+ }
+
+ private void disconnectBluetoothSco(IBinder exceptBinder) {
+ synchronized(mScoClients) {
+ checkScoAudioState();
+ if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL ||
+ mScoAudioState == SCO_STATE_DEACTIVATE_EXT_REQ) {
+ if (mBluetoothHeadsetDevice != null) {
+ if (mBluetoothHeadset != null) {
+ if (!mBluetoothHeadset.stopVoiceRecognition(
+ mBluetoothHeadsetDevice) ||
+ !mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
+ mBluetoothHeadsetDevice)) {
+ sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED, 0,
+ SENDMSG_REPLACE, 0, 0, null, 0);
+ }
+ } else if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL &&
+ getBluetoothHeadset()) {
+ mScoAudioState = SCO_STATE_DEACTIVATE_EXT_REQ;
+ }
+ }
+ } else {
+ clearAllScoClients(exceptBinder, true);
+ }
+ }
+ }
+
+ private void resetBluetoothSco() {
+ synchronized(mScoClients) {
+ clearAllScoClients(null, false);
+ mScoAudioState = SCO_STATE_INACTIVE;
+ broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+ }
+ }
+
+ private void broadcastScoConnectionState(int state) {
+ if (state != mScoConnectionState) {
+ Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED);
+ newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, state);
+ newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_PREVIOUS_STATE,
+ mScoConnectionState);
+ mContext.sendStickyBroadcast(newIntent);
+ mScoConnectionState = state;
+ }
+ }
+
private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
new BluetoothProfile.ServiceListener() {
public void onServiceConnected(int profile, BluetoothProfile proxy) {
synchronized (mScoClients) {
+ // Discard timeout message
+ mAudioHandler.removeMessages(MSG_BT_HEADSET_CNCT_FAILED);
mBluetoothHeadset = (BluetoothHeadset) proxy;
List<BluetoothDevice> deviceList = mBluetoothHeadset.getConnectedDevices();
if (deviceList.size() > 0) {
@@ -1402,19 +1486,41 @@
} else {
mBluetoothHeadsetDevice = null;
}
+ // Refresh SCO audio state
+ checkScoAudioState();
+ // Continue pending action if any
+ if (mScoAudioState == SCO_STATE_ACTIVATE_REQ ||
+ mScoAudioState == SCO_STATE_DEACTIVATE_REQ ||
+ mScoAudioState == SCO_STATE_DEACTIVATE_EXT_REQ) {
+ boolean status = false;
+ if (mBluetoothHeadsetDevice != null) {
+ switch (mScoAudioState) {
+ case SCO_STATE_ACTIVATE_REQ:
+ mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
+ status = mBluetoothHeadset.startScoUsingVirtualVoiceCall(
+ mBluetoothHeadsetDevice);
+ break;
+ case SCO_STATE_DEACTIVATE_REQ:
+ status = mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
+ mBluetoothHeadsetDevice);
+ break;
+ case SCO_STATE_DEACTIVATE_EXT_REQ:
+ status = mBluetoothHeadset.stopVoiceRecognition(
+ mBluetoothHeadsetDevice) &&
+ mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
+ mBluetoothHeadsetDevice);
+ }
+ }
+ if (!status) {
+ sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED, 0,
+ SENDMSG_REPLACE, 0, 0, null, 0);
+ }
+ }
}
}
public void onServiceDisconnected(int profile) {
synchronized (mScoClients) {
- if (mBluetoothHeadset != null) {
- List<BluetoothDevice> devices = mBluetoothHeadset.getConnectedDevices();
- if (devices.size() == 0) {
- mBluetoothHeadsetDevice = null;
- clearAllScoClients(null, false);
- mScoAudioState = SCO_STATE_INACTIVE;
- }
- mBluetoothHeadset = null;
- }
+ mBluetoothHeadset = null;
}
}
};
@@ -2041,6 +2147,10 @@
case MSG_PERSIST_MEDIABUTTONRECEIVER:
persistMediaButtonReceiver( (ComponentName) msg.obj );
break;
+
+ case MSG_BT_HEADSET_CNCT_FAILED:
+ resetBluetoothSco();
+ break;
}
}
}
@@ -2241,8 +2351,7 @@
address);
mConnectedDevices.remove(device);
mBluetoothHeadsetDevice = null;
- clearAllScoClients(null, false);
- mScoAudioState = SCO_STATE_INACTIVE;
+ resetBluetoothSco();
} else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
AudioSystem.setDeviceConnectionState(device,
AudioSystem.DEVICE_STATE_AVAILABLE,
@@ -2332,26 +2441,34 @@
}
} else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
boolean broadcast = false;
- int audioState = AudioManager.SCO_AUDIO_STATE_ERROR;
+ int state = AudioManager.SCO_AUDIO_STATE_ERROR;
synchronized (mScoClients) {
int btState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
- if (!mScoClients.isEmpty() && mScoAudioState == SCO_STATE_ACTIVE_INTERNAL) {
+ // 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)) {
broadcast = true;
}
switch (btState) {
case BluetoothHeadset.STATE_AUDIO_CONNECTED:
- audioState = AudioManager.SCO_AUDIO_STATE_CONNECTED;
- if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL) {
+ state = AudioManager.SCO_AUDIO_STATE_CONNECTED;
+ if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL &&
+ mScoAudioState != SCO_STATE_DEACTIVATE_REQ &&
+ mScoAudioState != SCO_STATE_DEACTIVATE_EXT_REQ) {
mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
}
break;
case BluetoothHeadset.STATE_AUDIO_DISCONNECTED:
- audioState = AudioManager.SCO_AUDIO_STATE_DISCONNECTED;
+ state = AudioManager.SCO_AUDIO_STATE_DISCONNECTED;
mScoAudioState = SCO_STATE_INACTIVE;
clearAllScoClients(null, false);
break;
case BluetoothHeadset.STATE_AUDIO_CONNECTING:
- if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL) {
+ if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL &&
+ mScoAudioState != SCO_STATE_DEACTIVATE_REQ &&
+ mScoAudioState != SCO_STATE_DEACTIVATE_EXT_REQ) {
mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
}
default:
@@ -2361,14 +2478,23 @@
}
}
if (broadcast) {
+ broadcastScoConnectionState(state);
+ //FIXME: this is to maintain compatibility with deprecated intent
+ // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate.
Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
- newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, audioState);
+ newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, state);
mContext.sendStickyBroadcast(newIntent);
}
} else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
mBootCompleted = true;
sendMsg(mAudioHandler, MSG_LOAD_SOUND_EFFECTS, SHARED_MSG, SENDMSG_NOOP,
0, 0, null, 0);
+
+ mScoConnectionState = AudioManager.SCO_AUDIO_STATE_ERROR;
+ resetBluetoothSco();
+ getBluetoothHeadset();
+ //FIXME: this is to maintain compatibility with deprecated intent
+ // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate.
Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE,
AudioManager.SCO_AUDIO_STATE_DISCONNECTED);