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

Commit 2083b297 authored by Eric Laurent's avatar Eric Laurent
Browse files

Fix issue 2265111: Loss of downlink audio while listening, and get a MT call.

The cause of the problem is that under certain circumstance the HeadsetObserver receives unexpected connection events. For instance,
when removing a bad quality 3.5mm stereo jack without mic the following events can be received:
1 connection of a headset with mic
2 removal of a headset with mic.

The result is that the no mic headset is never disconnected and audio policy manager considers it is still present. Then the music or downlink call audio is always routed to headset even if none is connected giving the impression that audio is lost, except whne you reconnect a headset of enable speaker phone.

The fix consists in adding more checks in HeadsetObserver to reject illegal transitions in headset state received from event observer.
parent 57bda83d
Loading
Loading
Loading
Loading
+56 −46
Original line number Diff line number Diff line
@@ -43,6 +43,8 @@ class HeadsetObserver extends UEventObserver {

    private static final int BIT_HEADSET = (1 << 0);
    private static final int BIT_HEADSET_NO_MIC = (1 << 1);
    private static final int SUPPORTED_HEADSETS = (BIT_HEADSET|BIT_HEADSET_NO_MIC);
    private static final int HEADSETS_WITH_MIC = BIT_HEADSET;

    private int mHeadsetState;
    private int mPrevHeadsetState;
@@ -100,20 +102,21 @@ class HeadsetObserver extends UEventObserver {

    private synchronized final void update(String newName, int newState) {
        // Retain only relevant bits
        int headsetState = newState & (BIT_HEADSET|BIT_HEADSET_NO_MIC);

        if (headsetState != mHeadsetState) {
            boolean isUnplug = false;
            if (((mHeadsetState & BIT_HEADSET) != 0 && (headsetState & BIT_HEADSET) == 0) ||
                ((mHeadsetState & BIT_HEADSET_NO_MIC) != 0 && (headsetState & BIT_HEADSET_NO_MIC) == 0)) {
                isUnplug = true;
        int headsetState = newState & SUPPORTED_HEADSETS;
        int newOrOld = headsetState | mHeadsetState;
        // reject all suspect transitions: only accept state changes from:
        // - a: 0 heaset to 1 headset
        // - b: 1 headset to 0 headset
        if (mHeadsetState == headsetState || ((newOrOld & (newOrOld - 1)) != 0)) {
            return;
        }

        mHeadsetName = newName;
        mPrevHeadsetState = mHeadsetState;
        mHeadsetState = headsetState;
        mPendingIntent = true;

            if (isUnplug) {
        if (headsetState == 0) {
            Intent intent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
            mContext.sendBroadcast(intent);

@@ -125,43 +128,50 @@ class HeadsetObserver extends UEventObserver {
            mWakeLock.acquire();
            mHandler.sendEmptyMessageDelayed(0, 1000);
        } else {
                sendIntent();
            sendIntents();
            mPendingIntent = false;
        }
    }

    private synchronized final void sendIntents() {
        int allHeadsets = SUPPORTED_HEADSETS;
        for (int curHeadset = 1; allHeadsets != 0; curHeadset <<= 1) {
            if ((curHeadset & allHeadsets) != 0) {
                sendIntent(curHeadset);
                allHeadsets &= ~curHeadset;
            }
        }
    }

    private synchronized final void sendIntent() {
    private final void sendIntent(int headset) {
        if ((mHeadsetState & headset) != (mPrevHeadsetState & headset)) {
            //  Pack up the values and broadcast them to everyone
            Intent intent = new Intent(Intent.ACTION_HEADSET_PLUG);
            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
            int state = 0;
            int microphone = 0;

        if ((mHeadsetState & BIT_HEADSET) != (mPrevHeadsetState & BIT_HEADSET)) {
            if ((headset & HEADSETS_WITH_MIC) != 0) {
                microphone = 1;
            if ((mHeadsetState & BIT_HEADSET) != 0) {
                state = 1;
            }
        } else if ((mHeadsetState & BIT_HEADSET_NO_MIC) != (mPrevHeadsetState & BIT_HEADSET_NO_MIC)) {
            if ((mHeadsetState & BIT_HEADSET_NO_MIC) != 0) {
            if ((mHeadsetState & headset) != 0) {
                state = 1;
            }
        }

            intent.putExtra("state", state);
            intent.putExtra("name", mHeadsetName);
            intent.putExtra("microphone", microphone);

            if (LOG) Log.v(TAG, "Intent.ACTION_HEADSET_PLUG: state: "+state+" name: "+mHeadsetName+" mic: "+microphone);
            // TODO: Should we require a permission?
            ActivityManagerNative.broadcastStickyIntent(intent, null);
        }
    }

    private final Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if (mPendingIntent) {
                sendIntent();
                sendIntents();
                mPendingIntent = false;
            }
            mWakeLock.release();