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

Commit 4599ee23 authored by Eric Laurent's avatar Eric Laurent
Browse files

AudioService: mute music during BT A2DP device switch

Mute music stream when processing Bluetooth A2DP
device connection or disconnection messages instead of
relying on Bluetooth A2DP service to do it.
This fixes issues due to the async nature of device
disconnection and connection handling.

Bug: 144784716
Test: repro steps in bug.
Change-Id: I83b66cf35b39c29e82f9a6199cd6d31346258038
parent 54b0b93f
Loading
Loading
Loading
Loading
+49 −0
Original line number Diff line number Diff line
@@ -45,7 +45,10 @@ import com.android.internal.annotations.GuardedBy;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;


/** @hide */
@@ -59,6 +62,9 @@ import java.util.NoSuchElementException;
    // Timeout for connection to bluetooth headset service
    /*package*/ static final int BT_HEADSET_CNCT_TIMEOUT_MS = 3000;

    // Delay before checking it music should be unmuted after processing an A2DP message
    private static final int BTA2DP_MUTE_CHECK_DELAY_MS = 50;

    private final @NonNull AudioService mAudioService;
    private final @NonNull Context mContext;

@@ -1050,9 +1056,19 @@ import java.util.NoSuchElementException;
                    final int strategy = msg.arg1;
                    mDeviceInventory.onSaveRemovePreferredDevice(strategy);
                } break;
                case MSG_CHECK_MUTE_MUSIC:
                    checkMessagesMuteMusic();
                    break;
                default:
                    Log.wtf(TAG, "Invalid message " + msg.what);
            }

            // Give some time to Bluetooth service to post a connection message
            // in case of active device switch
            if (MESSAGES_MUTE_MUSIC.contains(msg.what)) {
                sendMsg(MSG_CHECK_MUTE_MUSIC, SENDMSG_REPLACE, BTA2DP_MUTE_CHECK_DELAY_MS);
            }

            if (isMessageHandledUnderWakelock(msg.what)) {
                try {
                    mBrokerEventWakeLock.release();
@@ -1116,6 +1132,7 @@ import java.util.NoSuchElementException;
    private static final int MSG_I_SAVE_REMOVE_PREF_DEVICE_FOR_STRATEGY = 34;

    private static final int MSG_L_SPEAKERPHONE_CLIENT_DIED = 35;
    private static final int MSG_CHECK_MUTE_MUSIC = 36;


    private static boolean isMessageHandledUnderWakelock(int msgId) {
@@ -1132,6 +1149,7 @@ import java.util.NoSuchElementException;
            case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION:
            case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION:
            case MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT:
            case MSG_CHECK_MUTE_MUSIC:
                return true;
            default:
                return false;
@@ -1231,6 +1249,37 @@ import java.util.NoSuchElementException;
            mBrokerHandler.sendMessageAtTime(mBrokerHandler.obtainMessage(msg, arg1, arg2, obj),
                    time);
        }
        if (MESSAGES_MUTE_MUSIC.contains(msg)) {
            checkMessagesMuteMusic();
        }
    }

    /** List of messages for which music is muted while processing is pending */
    private static final Set<Integer> MESSAGES_MUTE_MUSIC;
    static {
        MESSAGES_MUTE_MUSIC = new HashSet<>();
        MESSAGES_MUTE_MUSIC.add(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED);
        MESSAGES_MUTE_MUSIC.add(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED);
        MESSAGES_MUTE_MUSIC.add(MSG_L_A2DP_DEVICE_CONFIG_CHANGE);
        MESSAGES_MUTE_MUSIC.add(MSG_L_A2DP_ACTIVE_DEVICE_CHANGE);
        MESSAGES_MUTE_MUSIC.add(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_CONNECTION);
        MESSAGES_MUTE_MUSIC.add(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION);
    }

    private AtomicBoolean mMusicMuted = new AtomicBoolean(false);

    /** Mutes or unmutes music according to pending A2DP messages */
    private void checkMessagesMuteMusic() {
        boolean mute = false;
        for (int msg : MESSAGES_MUTE_MUSIC) {
            if (mBrokerHandler.hasMessages(msg)) {
                mute = true;
                break;
            }
        }
        if (mute != mMusicMuted.getAndSet(mute)) {
            mAudioService.setMusicMute(mute);
        }
    }

    private class SpeakerphoneClient implements IBinder.DeathRecipient {
+47 −4
Original line number Diff line number Diff line
@@ -5061,6 +5061,10 @@ public class AudioService extends IAudioService.Stub
                profile, suppressNoisyIntent, a2dpVolume);
    }

    /*package*/ void setMusicMute(boolean mute) {
        mStreamStates[AudioSystem.STREAM_MUSIC].muteInternally(mute);
    }

    /**
     * See AudioManager.handleBluetoothA2dpDeviceConfigChange()
     * @param device
@@ -5463,6 +5467,7 @@ public class AudioService extends IAudioService.Stub
        private int mIndexMax;

        private boolean mIsMuted;
        private boolean mIsMutedInternally;
        private String mVolumeIndexSettingName;
        private int mObservedDevices;

@@ -5636,7 +5641,8 @@ public class AudioService extends IAudioService.Stub
            // Only set audio policy BT SCO stream volume to 0 when the stream is actually muted.
            // This allows RX path muting by the audio HAL only when explicitly muted but not when
            // index is just set to 0 to repect BT requirements
            if (mStreamType == AudioSystem.STREAM_BLUETOOTH_SCO && index == 0 && !mIsMuted) {
            if (mStreamType == AudioSystem.STREAM_BLUETOOTH_SCO && index == 0
                    && !isFullyMuted()) {
                index = 1;
            }
            AudioSystem.setStreamVolumeIndexAS(mStreamType, index, device);
@@ -5645,7 +5651,7 @@ public class AudioService extends IAudioService.Stub
        // must be called while synchronized VolumeStreamState.class
        /*package*/ void applyDeviceVolume_syncVSS(int device, boolean isAvrcpAbsVolSupported) {
            int index;
            if (mIsMuted) {
            if (isFullyMuted()) {
                index = 0;
            } else if (AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device)
                    && isAvrcpAbsVolSupported) {
@@ -5668,7 +5674,7 @@ public class AudioService extends IAudioService.Stub
                for (int i = 0; i < mIndexMap.size(); i++) {
                    final int device = mIndexMap.keyAt(i);
                    if (device != AudioSystem.DEVICE_OUT_DEFAULT) {
                        if (mIsMuted) {
                        if (isFullyMuted()) {
                            index = 0;
                        } else if (AudioSystem.DEVICE_OUT_ALL_A2DP_SET.contains(device)
                                && isAvrcpAbsVolSupported) {
@@ -5685,7 +5691,7 @@ public class AudioService extends IAudioService.Stub
                }
                // apply default volume last: by convention , default device volume will be used
                // by audio policy manager if no explicit volume is present for a given device type
                if (mIsMuted) {
                if (isFullyMuted()) {
                    index = 0;
                } else {
                    index = (getIndex(AudioSystem.DEVICE_OUT_DEFAULT) + 5)/10;
@@ -5867,6 +5873,41 @@ public class AudioService extends IAudioService.Stub
            return changed;
        }

        /**
         * Mute/unmute the stream by AudioService
         * @param state the new mute state
         * @return true if the mute state was changed
         */
        public boolean muteInternally(boolean state) {
            boolean changed = false;
            synchronized (VolumeStreamState.class) {
                if (state != mIsMutedInternally) {
                    changed = true;
                    mIsMutedInternally = state;

                    // Set the new mute volume. This propagates the values to
                    // the audio system, otherwise the volume won't be changed
                    // at the lower level.
                    sendMsg(mAudioHandler,
                            MSG_SET_ALL_VOLUMES,
                            SENDMSG_QUEUE,
                            0,
                            0,
                            this, 0);
                }
            }
            if (changed) {
                sVolumeLogger.log(new VolumeEvent(
                        VolumeEvent.VOL_MUTE_STREAM_INT, mStreamType, state));
            }
            return changed;
        }

        @GuardedBy("VolumeStreamState.class")
        public boolean isFullyMuted() {
            return mIsMuted || mIsMutedInternally;
        }

        public int getStreamType() {
            return mStreamType;
        }
@@ -5903,6 +5944,8 @@ public class AudioService extends IAudioService.Stub
        private void dump(PrintWriter pw) {
            pw.print("   Muted: ");
            pw.println(mIsMuted);
            pw.print("   Muted Internally: ");
            pw.println(mIsMutedInternally);
            pw.print("   Min: ");
            pw.print((mIndexMin + 5) / 10);
            if (mIndexMin != mIndexMinNoPerm) {
+21 −0
Original line number Diff line number Diff line
@@ -100,6 +100,7 @@ public class AudioServiceEvents {
        static final int VOL_VOICE_ACTIVITY_HEARING_AID = 6;
        static final int VOL_MODE_CHANGE_HEARING_AID = 7;
        static final int VOL_SET_GROUP_VOL = 8;
        static final int VOL_MUTE_STREAM_INT = 9;

        final int mOp;
        final int mStream;
@@ -188,6 +189,18 @@ public class AudioServiceEvents {
            logMetricEvent();
        }

        /** used for VOL_MUTE_STREAM_INT */
        VolumeEvent(int op, int stream, boolean state) {
            mOp = op;
            mStream = stream;
            mVal1 = state ? 1 : 0;
            mVal2 = 0;
            mCaller = null;
            mGroupName = null;
            mAudioAttributes = null;
            logMetricEvent();
        }


        /**
         * Audio Analytics unique Id.
@@ -278,6 +291,9 @@ public class AudioServiceEvents {
                            .set(MediaMetrics.Property.INDEX, mVal1)
                            .record();
                    return;
                case VOL_MUTE_STREAM_INT:
                    // No value in logging metrics for this internal event
                    return;
                default:
                    return;
            }
@@ -343,6 +359,11 @@ public class AudioServiceEvents {
                            .append(" flags:0x").append(Integer.toHexString(mVal2))
                            .append(") from ").append(mCaller)
                            .toString();
                case VOL_MUTE_STREAM_INT:
                    return new StringBuilder("VolumeStreamState.muteInternally(stream:")
                            .append(AudioSystem.streamToString(mStream))
                            .append(mVal1 == 1 ? ", muted)" : ", unmuted)")
                            .toString();
                default: return new StringBuilder("FIXME invalid op:").append(mOp).toString();
            }
        }