diff options
author | Eric Laurent <elaurent@google.com> | 2009-05-06 08:13:20 -0700 |
---|---|---|
committer | Eric Laurent <elaurent@google.com> | 2009-05-06 10:52:19 -0700 |
commit | b9c9d260f21b321527c4622a123af9767630d94d (patch) | |
tree | 581cf182a724dcc4a7ba9078229d57f8e3e9ff4f | |
parent | c770ed8a6bb7614ed4a85a672e4e6830bed19a95 (diff) | |
download | frameworks_base-b9c9d260f21b321527c4622a123af9767630d94d.zip frameworks_base-b9c9d260f21b321527c4622a123af9767630d94d.tar.gz frameworks_base-b9c9d260f21b321527c4622a123af9767630d94d.tar.bz2 |
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
-rw-r--r-- | api/current.xml | 4 | ||||
-rw-r--r-- | media/java/android/media/AudioManager.java | 77 | ||||
-rw-r--r-- | media/java/android/media/AudioService.java | 176 |
3 files changed, 219 insertions, 38 deletions
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 @@ public class AudioManager { * <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 @@ public class AudioManager { * @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 @@ public class AudioManager { * 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 @@ public class AudioManager { * 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 @@ public class AudioManager { * 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 @@ public class AudioManager { * 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 @@ public class AudioManager { * @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 @@ public class AudioManager { * @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 @@ public class AudioManager { * 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 @@ public class AudioManager { * @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 @@ public class AudioManager { * {@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 @@ public class AudioService extends IAudioService.Stub { 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 @@ public class AudioService extends IAudioService.Stub { mMediaServerOk = true; AudioSystem.setErrorCallback(mAudioSystemCallback); loadSoundEffects(); + mSpeakerIsOn = false; + mBluetoothScoIsConnected = false; + mHeadsetIsConnected = false; + mBluetoothA2dpIsConnected = false; } private void createAudioSystemThread() { @@ -606,8 +614,9 @@ public class AudioService extends IAudioService.Stub { } 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 @@ public class AudioService extends IAudioService.Stub { /** @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); } } |