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

Commit 9272b4b4 authored by Eric Laurent's avatar Eric Laurent
Browse files

Fix issue 2349345: Media sound output stuck on earpiece rather than speaker.

This change fixes a problem occuring when an application (for instance a VoIP application)
changes the audio mode to MODE_IN_CALL and crashes. In this case, the audio routing policy
remains as if we were in call until the audio mode is changed back to MODE_NORMAL, for instance when a new call
made or received and terminated.

The fix consists in registering a death receipient to the binder that made the setMode() request and resetting the audio
mode in case of client process crash.
parent ff65c8c5
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -797,7 +797,7 @@ public class AudioManager {
    public void setMode(int mode) {
        IAudioService service = getService();
        try {
            service.setMode(mode);
            service.setMode(mode, mICallBack);
        } catch (RemoteException e) {
            Log.e(TAG, "Dead object in setMode", e);
        }
+84 −3
Original line number Diff line number Diff line
@@ -54,7 +54,6 @@ import java.util.Iterator;
import java.util.Map;
import java.util.Set;


/**
 * The implementation of the volume manager service.
 * <p>
@@ -231,6 +230,9 @@ public class AudioService extends IAudioService.Stub {
    // Forced device usage for communications
    private int mForcedUseForComm;

    // List of binder death handlers for setMode() client processes.
    // The last process to have called setMode() is at the top of the list.
    private ArrayList <SetModeDeathHandler> mSetModeDeathHandlers = new ArrayList <SetModeDeathHandler>();

    ///////////////////////////////////////////////////////////////////////////
    // Construction
@@ -248,11 +250,13 @@ public class AudioService extends IAudioService.Stub {

        mVolumePanel = new VolumePanel(context, this);
        mSettingsObserver = new SettingsObserver();
        mMode = AudioSystem.MODE_NORMAL;
        mForcedUseForComm = AudioSystem.FORCE_NONE;
        createAudioSystemThread();
        readPersistedSettings();
        createStreamStates();
        // Call setMode() to initialize mSetModeDeathHandlers
        mMode = AudioSystem.MODE_INVALID;
        setMode(AudioSystem.MODE_NORMAL, null);
        mMediaServerOk = true;
        AudioSystem.setErrorCallback(mAudioSystemCallback);
        loadSoundEffects();
@@ -582,8 +586,54 @@ public class AudioService extends IAudioService.Stub {
        return existingValue;
    }

    /** @see AudioManager#setMode(int) */
    private class SetModeDeathHandler implements IBinder.DeathRecipient {
        private IBinder mCb; // To be notified of client's death
        private int mMode = AudioSystem.MODE_NORMAL; // Current mode set by this client

        SetModeDeathHandler(IBinder cb) {
            mCb = cb;
        }

        public void binderDied() {
            synchronized(mSetModeDeathHandlers) {
                Log.w(TAG, "setMode() client died");
                int index = mSetModeDeathHandlers.indexOf(this);
                if (index < 0) {
                    Log.w(TAG, "unregistered setMode() client died");
                } else {
                    mSetModeDeathHandlers.remove(this);
                    // If dead client was a the top of client list,
                    // apply next mode in the stack
                    if (index == 0) {
                        // mSetModeDeathHandlers is never empty as the initial entry
                        // created when AudioService starts is never removed
                        SetModeDeathHandler hdlr = mSetModeDeathHandlers.get(0);
                        int mode = hdlr.getMode();
                        if (AudioService.this.mMode != mode) {
                            if (AudioSystem.setPhoneState(mode) == AudioSystem.AUDIO_STATUS_OK) {
                                AudioService.this.mMode = mode;
                            }
                        }
                    }
                }
            }
        }

        public void setMode(int mode) {
            mMode = mode;
        }

        public int getMode() {
            return mMode;
        }

        public IBinder getBinder() {
            return mCb;
        }
    }

    /** @see AudioManager#setMode(int) */
    public void setMode(int mode, IBinder cb) {
        if (!checkAudioSettingsPermission("setMode()")) {
            return;
        }
@@ -599,6 +649,37 @@ public class AudioService extends IAudioService.Stub {
            if (mode != mMode) {
                if (AudioSystem.setPhoneState(mode) == AudioSystem.AUDIO_STATUS_OK) {
                    mMode = mode;

                    synchronized(mSetModeDeathHandlers) {
                        SetModeDeathHandler hdlr = null;
                        Iterator iter = mSetModeDeathHandlers.iterator();
                        while (iter.hasNext()) {
                            SetModeDeathHandler h = (SetModeDeathHandler)iter.next();
                            if (h.getBinder() == cb) {
                                hdlr = h;
                                // Remove from client list so that it is re-inserted at top of list
                                iter.remove();
                                break;
                            }
                        }
                        if (hdlr == null) {
                            hdlr = new SetModeDeathHandler(cb);
                            // cb is null when setMode() is called by AudioService constructor
                            if (cb != null) {
                                // Register for client death notification
                                try {
                                    cb.linkToDeath(hdlr, 0);
                                } catch (RemoteException e) {
                                    // Client has died!
                                    Log.w(TAG, "setMode() could not link to "+cb+" binder death");
                                }
                            }
                        }
                        // Last client to call setMode() is always at top of client list
                        // as required by SetModeDeathHandler.binderDied()
                        mSetModeDeathHandlers.add(0, hdlr);
                        hdlr.setMode(mode);
                    }
                }
            }
            int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE);
+1 −1
Original line number Diff line number Diff line
@@ -47,7 +47,7 @@ interface IAudioService {
    
    boolean shouldVibrate(int vibrateType);

    void setMode(int mode);
    void setMode(int mode, IBinder cb);

    int getMode();