Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b9c9d260 authored by Eric Laurent's avatar Eric Laurent
Browse files

fix issue 1713090: After a Bluetooth call, MusicPlayer starts playing on...

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
parent c770ed8a
Loading
Loading
Loading
Loading
+2 −2
Original line number Original line Diff line number Diff line
@@ -63668,7 +63668,7 @@
 synchronized="false"
 synchronized="false"
 static="false"
 static="false"
 final="false"
 final="false"
 deprecated="not deprecated"
 deprecated="deprecated"
 visibility="public"
 visibility="public"
>
>
<parameter name="mode" type="int">
<parameter name="mode" type="int">
@@ -63879,7 +63879,7 @@
 synchronized="false"
 synchronized="false"
 static="false"
 static="false"
 final="false"
 final="false"
 deprecated="not deprecated"
 deprecated="deprecated"
 visibility="public"
 visibility="public"
>
>
<parameter name="mode" type="int">
<parameter name="mode" type="int">
+50 −27
Original line number Original line Diff line number Diff line
@@ -642,7 +642,9 @@ public class AudioManager {
     *           <var>false</var> to turn it off
     *           <var>false</var> to turn it off
     */
     */
    public void setSpeakerphoneOn(boolean on){
    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
     * @return true if speakerphone is on, false if it's off
     */
     */
    public boolean isSpeakerphoneOn() {
    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
     *           headset; <var>false</var> to route audio to/from phone earpiece
     */
     */
    public void setBluetoothScoOn(boolean on){
    public void setBluetoothScoOn(boolean on){
        // Don't disable A2DP when turning off SCO.
        // Temporary fix for issue #1713090 until audio routing is refactored in eclair release.
        // A2DP does not affect in-call routing.
        // MODE_INVALID indicates to AudioService that setRouting() was initiated by AudioManager
        setRouting(MODE_RINGTONE,
        setRoutingP(MODE_INVALID, on ? ROUTE_BLUETOOTH_SCO: 0, ROUTE_BLUETOOTH_SCO);
               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);
    }
    }


    /**
    /**
@@ -678,7 +675,7 @@ public class AudioManager {
     *         false if otherwise
     *         false if otherwise
     */
     */
    public boolean isBluetoothScoOn() {
    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
     *           headset; <var>false</var> disable A2DP audio
     */
     */
    public void setBluetoothA2dpOn(boolean on){
    public void setBluetoothA2dpOn(boolean on){
        // the audio flinger chooses A2DP as a higher priority,
        // Temporary fix for issue #1713090 until audio routing is refactored in eclair release.
        // so there is no need to disable other routes.
        // MODE_INVALID indicates to AudioService that setRouting() was initiated by AudioManager
        setRouting(MODE_RINGTONE,
        setRoutingP(MODE_INVALID, on ? ROUTE_BLUETOOTH_A2DP: 0, ROUTE_BLUETOOTH_A2DP);
               on ? ROUTE_BLUETOOTH_A2DP: 0, ROUTE_BLUETOOTH_A2DP);
        setRouting(MODE_NORMAL,
                on ? ROUTE_BLUETOOTH_A2DP: 0, ROUTE_BLUETOOTH_A2DP);
    }
    }


    /**
    /**
@@ -703,7 +697,7 @@ public class AudioManager {
     *         false if otherwise
     *         false if otherwise
     */
     */
    public boolean isBluetoothA2dpOn() {
    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
     * @hide
     */
     */
    public void setWiredHeadsetOn(boolean on){
    public void setWiredHeadsetOn(boolean on){
        // A2DP has higher priority than wired headset, so headset connect/disconnect events
        // Temporary fix for issue #1713090 until audio routing is refactored in eclair release.
        // should not affect A2DP routing
        // MODE_INVALID indicates to AudioService that setRouting() was initiated by AudioManager
        setRouting(MODE_NORMAL,
        setRoutingP(MODE_INVALID, on ? ROUTE_HEADSET: 0, ROUTE_HEADSET);
                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);
    }
    }


    /**
    /**
@@ -732,7 +721,7 @@ public class AudioManager {
     * @hide
     * @hide
     */
     */
    public boolean isWiredHeadsetOn() {
    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
     *               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
     * @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
     * 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) {
    public void setRouting(int mode, int routes, int mask) {
        IAudioService service = getService();
        IAudioService service = getService();
        try {
        try {
@@ -876,7 +869,10 @@ public class AudioManager {
     * @param mode audio mode to get route (e.g., MODE_RINGTONE)
     * @param mode audio mode to get route (e.g., MODE_RINGTONE)
     * @return an audio route bit vector that can be compared with ROUTE_xxx
     * @return an audio route bit vector that can be compared with ROUTE_xxx
     * bits
     * bits
     * @deprecated   Do not query audio routing directly, use isSpeakerphoneOn(),
     * isBluetoothScoOn(), isBluetoothA2dpOn() and isWiredHeadsetOn() methods instead.
     */
     */
    @Deprecated
    public int getRouting(int mode) {
    public int getRouting(int mode) {
        IAudioService service = getService();
        IAudioService service = getService();
        try {
        try {
@@ -1076,4 +1072,31 @@ public class AudioManager {
      * {@hide}
      * {@hide}
      */
      */
     private IBinder mICallBack = new Binder();
     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;
         }
     }

}
}
+167 −9
Original line number Original line Diff line number Diff line
@@ -100,6 +100,10 @@ public class AudioService extends IAudioService.Stub {
    private int[] mRoutes = new int[AudioSystem.NUM_MODES];
    private int[] mRoutes = new int[AudioSystem.NUM_MODES];
    private Object mSettingsLock = new Object();
    private Object mSettingsLock = new Object();
    private boolean mMediaServerOk;
    private boolean mMediaServerOk;
    private boolean mSpeakerIsOn;
    private boolean mBluetoothScoIsConnected;
    private boolean mHeadsetIsConnected;
    private boolean mBluetoothA2dpIsConnected;


    private SoundPool mSoundPool;
    private SoundPool mSoundPool;
    private Object mSoundEffectsLock = new Object();
    private Object mSoundEffectsLock = new Object();
@@ -189,6 +193,10 @@ public class AudioService extends IAudioService.Stub {
        mMediaServerOk = true;
        mMediaServerOk = true;
        AudioSystem.setErrorCallback(mAudioSystemCallback);
        AudioSystem.setErrorCallback(mAudioSystemCallback);
        loadSoundEffects();
        loadSoundEffects();
        mSpeakerIsOn = false;
        mBluetoothScoIsConnected = false;
        mHeadsetIsConnected = false;
        mBluetoothA2dpIsConnected = false;
    }
    }


    private void createAudioSystemThread() {
    private void createAudioSystemThread() {
@@ -606,9 +614,10 @@ public class AudioService extends IAudioService.Stub {
        }
        }
        synchronized (mSettingsLock) {
        synchronized (mSettingsLock) {
            if (mode != mMode) {
            if (mode != mMode) {
                AudioSystem.setMode(mode);
                if (AudioSystem.setMode(mode) == AudioSystem.AUDIO_STATUS_OK) {
                    mMode = mode;
                    mMode = mode;
                }
                }
            }
            int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE);
            int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE);
            int index = mStreamStates[streamType].mIndex;
            int index = mStreamStates[streamType].mIndex;
            syncRingerAndNotificationStreamVolume(streamType, index, true);
            syncRingerAndNotificationStreamVolume(streamType, index, true);
@@ -623,20 +632,169 @@ public class AudioService extends IAudioService.Stub {


    /** @see AudioManager#setRouting(int, int, int) */
    /** @see AudioManager#setRouting(int, int, int) */
    public void setRouting(int mode, int routes, int mask) {
    public void setRouting(int mode, int routes, int mask) {
        int incallMask = 0;
        int ringtoneMask = 0;
        int normalMask = 0;

        if (!checkAudioSettingsPermission("setRouting()")) {
        if (!checkAudioSettingsPermission("setRouting()")) {
            return;
            return;
        }
        }
        synchronized (mSettingsLock) {
        synchronized (mSettingsLock) {
            if ((mRoutes[mode] & mask) != (routes & mask)) {
            // Temporary fix for issue #1713090 until audio routing is refactored in eclair release.
                AudioSystem.setRouting(mode, routes, mask);
            // mode AudioSystem.MODE_INVALID is used only by the following AudioManager methods:
                mRoutes[mode] = (mRoutes[mode] & ~mask) | (routes & mask);
            // 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 streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE);
                int index = mStreamStates[streamType].mIndex;
                int index = mStreamStates[streamType].mIndex;
                syncRingerAndNotificationStreamVolume(streamType, index, true);
                syncRingerAndNotificationStreamVolume(streamType, index, true);
                setStreamVolumeInt(streamType, index, true);
                setStreamVolumeInt(streamType, index, true);
            }
            }
        }
        }
    }


    /** @see AudioManager#getRouting(int) */
    /** @see AudioManager#getRouting(int) */
    public int getRouting(int mode) {
    public int getRouting(int mode) {