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

Commit 7647273f authored by Eric Laurent's avatar Eric Laurent
Browse files

AudioService: fix overlapping speakerphone + Bluetooth SCO routing requests

VoIP apps sometimes send conflicting requests to route call audio
to both speaker phone and BT SCO. This can happen if the app does not
reset the speakerphone request properly before requesting BT SCO.

In this case we must make sure to cancel the oldest request so that the
correct routing is restored when the audio mode owner is updated.

Bug: 162527933
Test: repro steps in bug
Test: regression tests with speakerphone and BT routes with major
VoIP apps.

Change-Id: I2d390a283822732474583ad655dbc760b0ba8c66
parent 4fc10841
Loading
Loading
Loading
Loading
+37 −0
Original line number Diff line number Diff line
@@ -224,12 +224,35 @@ import java.util.concurrent.atomic.AtomicBoolean;
            if (!addSpeakerphoneClient(cb, pid, on)) {
                return false;
            }
            if (on) {
                // Cancel BT SCO ON request by this same client: speakerphone and BT SCO routes
                // are mutually exclusive.
                // See symmetrical operation for startBluetoothScoForClient_Sync().
                mBtHelper.stopBluetoothScoForPid(pid);
            }
            final boolean wasOn = isSpeakerphoneOn();
            updateSpeakerphoneOn(eventSource);
            return (wasOn != isSpeakerphoneOn());
        }
    }

    /**
     * Turns speakerphone off for a given pid and update speakerphone state.
     * @param pid
     */
    @GuardedBy("mDeviceStateLock")
    private void setSpeakerphoneOffForPid(int pid) {
        SpeakerphoneClient client = getSpeakerphoneClientForPid(pid);
        if (client == null) {
            return;
        }
        client.unregisterDeathRecipient();
        mSpeakerphoneClients.remove(client);
        final String eventSource = new StringBuilder("setSpeakerphoneOffForPid(")
                .append(pid).append(")").toString();
        updateSpeakerphoneOn(eventSource);
    }

    @GuardedBy("mDeviceStateLock")
    private void updateSpeakerphoneOn(String eventSource) {
        if (isSpeakerphoneOnRequested()) {
@@ -488,6 +511,10 @@ import java.util.concurrent.atomic.AtomicBoolean;
    /*package*/ void startBluetoothScoForClient_Sync(IBinder cb, int scoAudioMode,
                @NonNull String eventSource) {
        synchronized (mDeviceStateLock) {
            // Cancel speakerphone ON request by this same client: speakerphone and BT SCO routes
            // are mutually exclusive.
            // See symmetrical operation for setSpeakerphoneOn(true).
            setSpeakerphoneOffForPid(Binder.getCallingPid());
            mBtHelper.startBluetoothScoForClient(cb, scoAudioMode, eventSource);
        }
    }
@@ -1379,6 +1406,16 @@ import java.util.concurrent.atomic.AtomicBoolean;
        return false;
    }

    @GuardedBy("mDeviceStateLock")
    private SpeakerphoneClient getSpeakerphoneClientForPid(int pid) {
        for (SpeakerphoneClient cl : mSpeakerphoneClients) {
            if (cl.getPid() == pid) {
                return cl;
            }
        }
        return null;
    }

    // List of clients requesting speakerPhone ON
    @GuardedBy("mDeviceStateLock")
    private final @NonNull ArrayList<SpeakerphoneClient> mSpeakerphoneClients =
+35 −9
Original line number Diff line number Diff line
@@ -432,19 +432,35 @@ public class BtHelper {
        // and this must be done on behalf of system server to make sure permissions are granted.
        final long ident = Binder.clearCallingIdentity();
        if (client != null) {
            stopAndRemoveClient(client, eventSource);
        }
        Binder.restoreCallingIdentity(ident);
    }

    // @GuardedBy("AudioDeviceBroker.mSetModeLock")
    @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
    /*package*/ synchronized void stopBluetoothScoForPid(int pid) {
        ScoClient client = getScoClientForPid(pid);
        if (client == null) {
            return;
        }
        final String eventSource = new StringBuilder("stopBluetoothScoForPid(")
                .append(pid).append(")").toString();
        stopAndRemoveClient(client, eventSource);
    }

    @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
    private void stopAndRemoveClient(ScoClient client, @NonNull String eventSource) {
        AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(eventSource));
        client.requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED,
                SCO_MODE_VIRTUAL_CALL);
            // If a disconnection is pending, the client will be removed whne clearAllScoClients()
        // If a disconnection is pending, the client will be removed when clearAllScoClients()
        // is called form receiveBtEvent()
        if (mScoAudioState != SCO_STATE_DEACTIVATE_REQ
                && mScoAudioState != SCO_STATE_DEACTIVATING) {
            client.remove(false /*stop */, true /*unregister*/);
        }
    }
        Binder.restoreCallingIdentity(ident);
    }


    /*package*/ synchronized void setHearingAidVolume(int index, int streamType) {
        if (mHearingAid == null) {
@@ -974,6 +990,16 @@ public class BtHelper {
        return null;
    }

    @GuardedBy("BtHelper.this")
    private ScoClient getScoClientForPid(int pid) {
        for (ScoClient cl : mScoClients) {
            if (cl.getPid() == pid) {
                return cl;
            }
        }
        return null;
    }

    // @GuardedBy("AudioDeviceBroker.mSetModeLock")
    //@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
    @GuardedBy("BtHelper.this")