fix issue 1713090: After a Bluetooth call, MusicPlayer starts playing on speaker rather than wired external audio.
Temporary fix until audio routing is refactored in Eclair release:
- centralized and synchronized all audio routing control in AudioService.setRouting()
- deprecated AudioManager.setRouting() and AudioManager.getRouting() methods
diff --git a/api/current.xml b/api/current.xml
index 2bab739..2f93971 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -63668,7 +63668,7 @@
synchronized="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="mode" type="int">
@@ -63879,7 +63879,7 @@
synchronized="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="mode" type="int">
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index f509fb5..e43c9c4 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -642,7 +642,9 @@
* <var>false</var> to turn it off
*/
public void setSpeakerphoneOn(boolean on){
- setRouting(MODE_IN_CALL, on ? ROUTE_SPEAKER : ROUTE_EARPIECE, ROUTE_ALL);
+ // Temporary fix for issue #1713090 until audio routing is refactored in eclair release.
+ // MODE_INVALID indicates to AudioService that setRouting() was initiated by AudioManager
+ setRoutingP(MODE_INVALID, on ? ROUTE_SPEAKER: 0, ROUTE_SPEAKER);
}
/**
@@ -651,7 +653,7 @@
* @return true if speakerphone is on, false if it's off
*/
public boolean isSpeakerphoneOn() {
- return (getRouting(MODE_IN_CALL) & ROUTE_SPEAKER) == 0 ? false : true;
+ return (getRoutingP(MODE_IN_CALL) & ROUTE_SPEAKER) == 0 ? false : true;
}
/**
@@ -661,14 +663,9 @@
* headset; <var>false</var> to route audio to/from phone earpiece
*/
public void setBluetoothScoOn(boolean on){
- // Don't disable A2DP when turning off SCO.
- // A2DP does not affect in-call routing.
- setRouting(MODE_RINGTONE,
- on ? ROUTE_BLUETOOTH_SCO: ROUTE_SPEAKER, ROUTE_ALL & ~ROUTE_BLUETOOTH_A2DP);
- setRouting(MODE_NORMAL,
- on ? ROUTE_BLUETOOTH_SCO: ROUTE_SPEAKER, ROUTE_ALL & ~ROUTE_BLUETOOTH_A2DP);
- setRouting(MODE_IN_CALL,
- on ? ROUTE_BLUETOOTH_SCO: ROUTE_EARPIECE, ROUTE_ALL);
+ // Temporary fix for issue #1713090 until audio routing is refactored in eclair release.
+ // MODE_INVALID indicates to AudioService that setRouting() was initiated by AudioManager
+ setRoutingP(MODE_INVALID, on ? ROUTE_BLUETOOTH_SCO: 0, ROUTE_BLUETOOTH_SCO);
}
/**
@@ -678,7 +675,7 @@
* false if otherwise
*/
public boolean isBluetoothScoOn() {
- return (getRouting(MODE_IN_CALL) & ROUTE_BLUETOOTH_SCO) == 0 ? false : true;
+ return (getRoutingP(MODE_IN_CALL) & ROUTE_BLUETOOTH_SCO) == 0 ? false : true;
}
/**
@@ -688,12 +685,9 @@
* headset; <var>false</var> disable A2DP audio
*/
public void setBluetoothA2dpOn(boolean on){
- // the audio flinger chooses A2DP as a higher priority,
- // so there is no need to disable other routes.
- setRouting(MODE_RINGTONE,
- on ? ROUTE_BLUETOOTH_A2DP: 0, ROUTE_BLUETOOTH_A2DP);
- setRouting(MODE_NORMAL,
- on ? ROUTE_BLUETOOTH_A2DP: 0, ROUTE_BLUETOOTH_A2DP);
+ // Temporary fix for issue #1713090 until audio routing is refactored in eclair release.
+ // MODE_INVALID indicates to AudioService that setRouting() was initiated by AudioManager
+ setRoutingP(MODE_INVALID, on ? ROUTE_BLUETOOTH_A2DP: 0, ROUTE_BLUETOOTH_A2DP);
}
/**
@@ -703,7 +697,7 @@
* false if otherwise
*/
public boolean isBluetoothA2dpOn() {
- return (getRouting(MODE_NORMAL) & ROUTE_BLUETOOTH_A2DP) == 0 ? false : true;
+ return (getRoutingP(MODE_NORMAL) & ROUTE_BLUETOOTH_A2DP) == 0 ? false : true;
}
/**
@@ -714,14 +708,9 @@
* @hide
*/
public void setWiredHeadsetOn(boolean on){
- // A2DP has higher priority than wired headset, so headset connect/disconnect events
- // should not affect A2DP routing
- setRouting(MODE_NORMAL,
- on ? ROUTE_HEADSET : ROUTE_SPEAKER, ROUTE_ALL & ~ROUTE_BLUETOOTH_A2DP);
- setRouting(MODE_RINGTONE,
- on ? ROUTE_HEADSET | ROUTE_SPEAKER : ROUTE_SPEAKER, ROUTE_ALL & ~ROUTE_BLUETOOTH_A2DP);
- setRouting(MODE_IN_CALL,
- on ? ROUTE_HEADSET : ROUTE_EARPIECE, ROUTE_ALL);
+ // Temporary fix for issue #1713090 until audio routing is refactored in eclair release.
+ // MODE_INVALID indicates to AudioService that setRouting() was initiated by AudioManager
+ setRoutingP(MODE_INVALID, on ? ROUTE_HEADSET: 0, ROUTE_HEADSET);
}
/**
@@ -732,7 +721,7 @@
* @hide
*/
public boolean isWiredHeadsetOn() {
- return (getRouting(MODE_NORMAL) & ROUTE_HEADSET) == 0 ? false : true;
+ return (getRoutingP(MODE_NORMAL) & ROUTE_HEADSET) == 0 ? false : true;
}
/**
@@ -860,7 +849,11 @@
* more of ROUTE_xxx types. Set bits indicate that route should be on
* @param mask bit vector of routes to change, created from one or more of
* ROUTE_xxx types. Unset bits indicate the route should be left unchanged
+ *
+ * @deprecated Do not set audio routing directly, use setSpeakerphoneOn(),
+ * setBluetoothScoOn(), setBluetoothA2dpOn() and setWiredHeadsetOn() methods instead.
*/
+
public void setRouting(int mode, int routes, int mask) {
IAudioService service = getService();
try {
@@ -876,7 +869,10 @@
* @param mode audio mode to get route (e.g., MODE_RINGTONE)
* @return an audio route bit vector that can be compared with ROUTE_xxx
* bits
+ * @deprecated Do not query audio routing directly, use isSpeakerphoneOn(),
+ * isBluetoothScoOn(), isBluetoothA2dpOn() and isWiredHeadsetOn() methods instead.
*/
+ @Deprecated
public int getRouting(int mode) {
IAudioService service = getService();
try {
@@ -1076,4 +1072,31 @@
* {@hide}
*/
private IBinder mICallBack = new Binder();
+
+ /**
+ * {@hide}
+ */
+ private void setRoutingP(int mode, int routes, int mask) {
+ IAudioService service = getService();
+ try {
+ service.setRouting(mode, routes, mask);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Dead object in setRouting", e);
+ }
+ }
+
+
+ /**
+ * {@hide}
+ */
+ private int getRoutingP(int mode) {
+ IAudioService service = getService();
+ try {
+ return service.getRouting(mode);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Dead object in getRouting", e);
+ return -1;
+ }
+ }
+
}
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 2e3e460..881de4d 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -100,6 +100,10 @@
private int[] mRoutes = new int[AudioSystem.NUM_MODES];
private Object mSettingsLock = new Object();
private boolean mMediaServerOk;
+ private boolean mSpeakerIsOn;
+ private boolean mBluetoothScoIsConnected;
+ private boolean mHeadsetIsConnected;
+ private boolean mBluetoothA2dpIsConnected;
private SoundPool mSoundPool;
private Object mSoundEffectsLock = new Object();
@@ -189,6 +193,10 @@
mMediaServerOk = true;
AudioSystem.setErrorCallback(mAudioSystemCallback);
loadSoundEffects();
+ mSpeakerIsOn = false;
+ mBluetoothScoIsConnected = false;
+ mHeadsetIsConnected = false;
+ mBluetoothA2dpIsConnected = false;
}
private void createAudioSystemThread() {
@@ -606,8 +614,9 @@
}
synchronized (mSettingsLock) {
if (mode != mMode) {
- AudioSystem.setMode(mode);
- mMode = mode;
+ if (AudioSystem.setMode(mode) == AudioSystem.AUDIO_STATUS_OK) {
+ mMode = mode;
+ }
}
int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE);
int index = mStreamStates[streamType].mIndex;
@@ -623,18 +632,167 @@
/** @see AudioManager#setRouting(int, int, int) */
public void setRouting(int mode, int routes, int mask) {
+ int incallMask = 0;
+ int ringtoneMask = 0;
+ int normalMask = 0;
+
if (!checkAudioSettingsPermission("setRouting()")) {
return;
}
synchronized (mSettingsLock) {
- if ((mRoutes[mode] & mask) != (routes & mask)) {
- AudioSystem.setRouting(mode, routes, mask);
- mRoutes[mode] = (mRoutes[mode] & ~mask) | (routes & mask);
+ // Temporary fix for issue #1713090 until audio routing is refactored in eclair release.
+ // mode AudioSystem.MODE_INVALID is used only by the following AudioManager methods:
+ // setWiredHeadsetOn(), setBluetoothA2dpOn(), setBluetoothScoOn() and setSpeakerphoneOn().
+ // If applications are using AudioManager.setRouting() that is now deprecated, the routing
+ // command will be ignored.
+ if (mode == AudioSystem.MODE_INVALID) {
+ switch (mask) {
+ case AudioSystem.ROUTE_SPEAKER:
+ // handle setSpeakerphoneOn()
+ if (routes != 0 && !mSpeakerIsOn) {
+ mSpeakerIsOn = true;
+ mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_SPEAKER;
+ incallMask = AudioSystem.ROUTE_ALL;
+ } else if (mSpeakerIsOn) {
+ mSpeakerIsOn = false;
+ if (mBluetoothScoIsConnected) {
+ mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_BLUETOOTH_SCO;
+ } else if (mHeadsetIsConnected) {
+ mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_HEADSET;
+ } else {
+ mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_EARPIECE;
+ }
+ incallMask = AudioSystem.ROUTE_ALL;
+ }
+ break;
+
+ case AudioSystem.ROUTE_BLUETOOTH_SCO:
+ // handle setBluetoothScoOn()
+ if (routes != 0 && !mBluetoothScoIsConnected) {
+ mBluetoothScoIsConnected = true;
+ mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_BLUETOOTH_SCO;
+ mRoutes[AudioSystem.MODE_RINGTONE] = (mRoutes[AudioSystem.MODE_RINGTONE] & AudioSystem.ROUTE_BLUETOOTH_A2DP) |
+ AudioSystem.ROUTE_BLUETOOTH_SCO;
+ mRoutes[AudioSystem.MODE_NORMAL] = (mRoutes[AudioSystem.MODE_NORMAL] & AudioSystem.ROUTE_BLUETOOTH_A2DP) |
+ AudioSystem.ROUTE_BLUETOOTH_SCO;
+ incallMask = AudioSystem.ROUTE_ALL;
+ // A2DP has higher priority than SCO headset, so headset connect/disconnect events
+ // should not affect A2DP routing
+ ringtoneMask = AudioSystem.ROUTE_ALL & ~AudioSystem.ROUTE_BLUETOOTH_A2DP;
+ normalMask = AudioSystem.ROUTE_ALL & ~AudioSystem.ROUTE_BLUETOOTH_A2DP;
+ } else if (mBluetoothScoIsConnected) {
+ mBluetoothScoIsConnected = false;
+ if (mHeadsetIsConnected) {
+ mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_HEADSET;
+ mRoutes[AudioSystem.MODE_RINGTONE] = (mRoutes[AudioSystem.MODE_RINGTONE] & AudioSystem.ROUTE_BLUETOOTH_A2DP) |
+ (AudioSystem.ROUTE_HEADSET|AudioSystem.ROUTE_SPEAKER);
+ mRoutes[AudioSystem.MODE_NORMAL] = (mRoutes[AudioSystem.MODE_NORMAL] & AudioSystem.ROUTE_BLUETOOTH_A2DP) |
+ AudioSystem.ROUTE_HEADSET;
+ } else {
+ if (mSpeakerIsOn) {
+ mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_SPEAKER;
+ } else {
+ mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_EARPIECE;
+ }
+ mRoutes[AudioSystem.MODE_RINGTONE] = (mRoutes[AudioSystem.MODE_RINGTONE] & AudioSystem.ROUTE_BLUETOOTH_A2DP) |
+ AudioSystem.ROUTE_SPEAKER;
+ mRoutes[AudioSystem.MODE_NORMAL] = (mRoutes[AudioSystem.MODE_NORMAL] & AudioSystem.ROUTE_BLUETOOTH_A2DP) |
+ AudioSystem.ROUTE_SPEAKER;
+ }
+ incallMask = AudioSystem.ROUTE_ALL;
+ // A2DP has higher priority than SCO headset, so headset connect/disconnect events
+ // should not affect A2DP routing
+ ringtoneMask = AudioSystem.ROUTE_ALL & ~AudioSystem.ROUTE_BLUETOOTH_A2DP;
+ normalMask = AudioSystem.ROUTE_ALL & ~AudioSystem.ROUTE_BLUETOOTH_A2DP;
+ }
+ break;
+
+ case AudioSystem.ROUTE_HEADSET:
+ // handle setWiredHeadsetOn()
+ if (routes != 0 && !mHeadsetIsConnected) {
+ mHeadsetIsConnected = true;
+ // do not act upon headset connection if bluetooth SCO is connected to match phone app behavior
+ if (!mBluetoothScoIsConnected) {
+ mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_HEADSET;
+ mRoutes[AudioSystem.MODE_RINGTONE] = (mRoutes[AudioSystem.MODE_RINGTONE] & AudioSystem.ROUTE_BLUETOOTH_A2DP) |
+ (AudioSystem.ROUTE_HEADSET|AudioSystem.ROUTE_SPEAKER);
+ mRoutes[AudioSystem.MODE_NORMAL] = (mRoutes[AudioSystem.MODE_NORMAL] & AudioSystem.ROUTE_BLUETOOTH_A2DP) |
+ AudioSystem.ROUTE_HEADSET;
+ incallMask = AudioSystem.ROUTE_ALL;
+ // A2DP has higher priority than wired headset, so headset connect/disconnect events
+ // should not affect A2DP routing
+ ringtoneMask = AudioSystem.ROUTE_ALL & ~AudioSystem.ROUTE_BLUETOOTH_A2DP;
+ normalMask = AudioSystem.ROUTE_ALL & ~AudioSystem.ROUTE_BLUETOOTH_A2DP;
+ }
+ } else if (mHeadsetIsConnected) {
+ mHeadsetIsConnected = false;
+ // do not act upon headset disconnection if bluetooth SCO is connected to match phone app behavior
+ if (!mBluetoothScoIsConnected) {
+ if (mSpeakerIsOn) {
+ mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_SPEAKER;
+ } else {
+ mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_EARPIECE;
+ }
+ mRoutes[AudioSystem.MODE_RINGTONE] = (mRoutes[AudioSystem.MODE_RINGTONE] & AudioSystem.ROUTE_BLUETOOTH_A2DP) |
+ AudioSystem.ROUTE_SPEAKER;
+ mRoutes[AudioSystem.MODE_NORMAL] = (mRoutes[AudioSystem.MODE_NORMAL] & AudioSystem.ROUTE_BLUETOOTH_A2DP) |
+ AudioSystem.ROUTE_SPEAKER;
+
+ incallMask = AudioSystem.ROUTE_ALL;
+ // A2DP has higher priority than wired headset, so headset connect/disconnect events
+ // should not affect A2DP routing
+ ringtoneMask = AudioSystem.ROUTE_ALL & ~AudioSystem.ROUTE_BLUETOOTH_A2DP;
+ normalMask = AudioSystem.ROUTE_ALL & ~AudioSystem.ROUTE_BLUETOOTH_A2DP;
+ }
+ }
+ break;
+
+ case AudioSystem.ROUTE_BLUETOOTH_A2DP:
+ // handle setBluetoothA2dpOn()
+ if (routes != 0 && !mBluetoothA2dpIsConnected) {
+ mBluetoothA2dpIsConnected = true;
+ mRoutes[AudioSystem.MODE_RINGTONE] |= AudioSystem.ROUTE_BLUETOOTH_A2DP;
+ mRoutes[AudioSystem.MODE_NORMAL] |= AudioSystem.ROUTE_BLUETOOTH_A2DP;
+ // the audio flinger chooses A2DP as a higher priority,
+ // so there is no need to disable other routes.
+ ringtoneMask = AudioSystem.ROUTE_BLUETOOTH_A2DP;
+ normalMask = AudioSystem.ROUTE_BLUETOOTH_A2DP;
+ } else if (mBluetoothA2dpIsConnected) {
+ mBluetoothA2dpIsConnected = false;
+ mRoutes[AudioSystem.MODE_RINGTONE] &= ~AudioSystem.ROUTE_BLUETOOTH_A2DP;
+ mRoutes[AudioSystem.MODE_NORMAL] &= ~AudioSystem.ROUTE_BLUETOOTH_A2DP;
+ // the audio flinger chooses A2DP as a higher priority,
+ // so there is no need to disable other routes.
+ ringtoneMask = AudioSystem.ROUTE_BLUETOOTH_A2DP;
+ normalMask = AudioSystem.ROUTE_BLUETOOTH_A2DP;
+ }
+ break;
+ }
+
+ // incallMask is != 0 means we must apply ne routing to MODE_IN_CALL mode
+ if (incallMask != 0) {
+ AudioSystem.setRouting(AudioSystem.MODE_IN_CALL,
+ mRoutes[AudioSystem.MODE_IN_CALL],
+ incallMask);
+ }
+ // ringtoneMask is != 0 means we must apply ne routing to MODE_RINGTONE mode
+ if (ringtoneMask != 0) {
+ AudioSystem.setRouting(AudioSystem.MODE_RINGTONE,
+ mRoutes[AudioSystem.MODE_RINGTONE],
+ ringtoneMask);
+ }
+ // normalMask is != 0 means we must apply ne routing to MODE_NORMAL mode
+ if (normalMask != 0) {
+ AudioSystem.setRouting(AudioSystem.MODE_NORMAL,
+ mRoutes[AudioSystem.MODE_NORMAL],
+ normalMask);
+ }
+
+ int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE);
+ int index = mStreamStates[streamType].mIndex;
+ syncRingerAndNotificationStreamVolume(streamType, index, true);
+ setStreamVolumeInt(streamType, index, true);
}
- int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE);
- int index = mStreamStates[streamType].mIndex;
- syncRingerAndNotificationStreamVolume(streamType, index, true);
- setStreamVolumeInt(streamType, index, true);
}
}