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

Commit fc86cfa8 authored by Jean-Michel Trivi's avatar Jean-Michel Trivi
Browse files

AudioService: make all BT device updates async

Make AudioManager methods to (dis)connect a BT device
  (hearing aid or A2DP) and handle config update run
  on the AudioDeviceBroker event thread to prevent
  concurrency issues between BtService and AudioService.

Bug: 126239173
Test: have multiple A2DP devices connected, switch between them

Change-Id: Iefd4b4d29e28b15cbb74e8a175ee5e6cc7918c1d
parent b1011334
Loading
Loading
Loading
Loading
+17 −12
Original line number Diff line number Diff line
@@ -4068,6 +4068,10 @@ public class AudioManager {
     /**
     * Indicate Hearing Aid connection state change and eventually suppress
     * the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent.
     * This operation is asynchronous but its execution will still be sequentially scheduled
     * relative to calls to {@link #setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
     * * BluetoothDevice, int, int, boolean, int)} and
     * and {@link #handleBluetoothA2dpDeviceConfigChange(BluetoothDevice)}.
     * @param device Bluetooth device connected/disconnected
     * @param state new connection state (BluetoothProfile.STATE_xxx)
     * @param musicDevice Default get system volume for the connecting device.
@@ -4075,27 +4079,27 @@ public class AudioManager {
     * {@link android.bluetooth.BluetoothProfile.HEARING_AID})
     * @param suppressNoisyIntent if true the
     * {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent will not be sent.
     * @return a delay in ms that the caller should wait before broadcasting
     * BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED intent.
     * {@hide}
     */
    public int setBluetoothHearingAidDeviceConnectionState(
    public void setBluetoothHearingAidDeviceConnectionState(
                BluetoothDevice device, int state, boolean suppressNoisyIntent,
                int musicDevice) {
        final IAudioService service = getService();
        int delay = 0;
        try {
            delay = service.setBluetoothHearingAidDeviceConnectionState(device,
            service.setBluetoothHearingAidDeviceConnectionState(device,
                state, suppressNoisyIntent, musicDevice);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
        return delay;
    }

     /**
     * Indicate A2DP source or sink connection state change and eventually suppress
     * the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent.
     * This operation is asynchronous but its execution will still be sequentially scheduled
     * relative to calls to {@link #setBluetoothHearingAidDeviceConnectionState(BluetoothDevice,
     * int, boolean, int)} and
     * {@link #handleBluetoothA2dpDeviceConfigChange(BluetoothDevice)}.
     * @param device Bluetooth device connected/disconnected
     * @param state  new connection state, {@link BluetoothProfile#STATE_CONNECTED}
     *     or {@link BluetoothProfile#STATE_DISCONNECTED}
@@ -4105,26 +4109,27 @@ public class AudioManager {
     * {@link android.bluetooth.BluetoothProfile.A2DP_SINK})
     * @param suppressNoisyIntent if true the
     * {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent will not be sent.
     * @return a delay in ms that the caller should wait before broadcasting
     * BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED intent.
     * {@hide}
     */
    public int setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
    public void setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
            BluetoothDevice device, int state,
            int profile, boolean suppressNoisyIntent, int a2dpVolume) {
        final IAudioService service = getService();
        int delay = 0;
        try {
            delay = service.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(device,
            service.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(device,
                state, profile, suppressNoisyIntent, a2dpVolume);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
        return delay;
    }

     /**
     * Indicate A2DP device configuration has changed.
     * This operation is asynchronous but its execution will still be sequentially scheduled
     * relative to calls to
     * {@link #setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(BluetoothDevice, int, int,
     * boolean, int)} and
     * {@link #setBluetoothHearingAidDeviceConnectionState(BluetoothDevice, int, boolean, int)}
     * @param device Bluetooth device whose configuration has changed.
     * {@hide}
     */
+2 −2
Original line number Diff line number Diff line
@@ -228,10 +228,10 @@ interface IAudioService {

    oneway void playerHasOpPlayAudio(in int piid, in boolean hasOpPlayAudio);

    int setBluetoothHearingAidDeviceConnectionState(in BluetoothDevice device,
    void setBluetoothHearingAidDeviceConnectionState(in BluetoothDevice device,
            int state, boolean suppressNoisyIntent, int musicDevice);

    int setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(in BluetoothDevice device,
    void setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(in BluetoothDevice device,
            int state, int profile, boolean suppressNoisyIntent, int a2dpVolume);

    oneway void setFocusRequestResultFromExtPolicy(in AudioFocusInfo afi, int requestResult,
+85 −29
Original line number Diff line number Diff line
@@ -201,53 +201,74 @@ import java.util.ArrayList;
        }
    }

    /*package*/ int setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
            @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state,
            int profile, boolean suppressNoisyIntent, int a2dpVolume) {
        AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent(
                "setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent state=" + state
                        // only querying address as this is the only readily available field
                        // on the device
                        + " addr=" + device.getAddress()
                        + " prof=" + profile + " supprNoisy=" + suppressNoisyIntent
                        + " vol=" + a2dpVolume)).printLog(TAG));
        synchronized (mDeviceStateLock) {
            if (mBrokerHandler.hasMessages(MSG_IL_SET_A2DP_SINK_CONNECTION_STATE,
                    new BtHelper.BluetoothA2dpDeviceInfo(device))) {
                AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
                        "A2DP connection state ignored"));
                return 0;
    private static final class BtDeviceConnectionInfo {
        final @NonNull BluetoothDevice mDevice;
        final @AudioService.BtProfileConnectionState int mState;
        final int mProfile;
        final boolean mSupprNoisy;
        final int mVolume;

        BtDeviceConnectionInfo(@NonNull BluetoothDevice device,
                @AudioService.BtProfileConnectionState int state,
                int profile, boolean suppressNoisyIntent, int vol) {
            mDevice = device;
            mState = state;
            mProfile = profile;
            mSupprNoisy = suppressNoisyIntent;
            mVolume = vol;
        }
            return mDeviceInventory.setBluetoothA2dpDeviceConnectionState(
                    device, state, profile, suppressNoisyIntent,
                    AudioSystem.DEVICE_NONE, a2dpVolume);
    }

    /*package*/ void postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
            @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state,
            int profile, boolean suppressNoisyIntent, int a2dpVolume) {
        final BtDeviceConnectionInfo info = new BtDeviceConnectionInfo(device, state, profile,
                suppressNoisyIntent, a2dpVolume);

        // TODO add a check to try to remove unprocessed messages for the same device (the old
        //      check didn't work), and  make sure it doesn't conflict with config change message
        sendLMsgNoDelay(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT, SENDMSG_QUEUE, info);
    }

    /*package*/ int handleBluetoothA2dpActiveDeviceChange(
            @NonNull BluetoothDevice device,
            @AudioService.BtProfileConnectionState int state, int profile,
            boolean suppressNoisyIntent, int a2dpVolume) {
        // FIXME method was added by @a8439e2 but never used, and now conflicts with async behavior
        //   of handleBluetoothA2dpDeviceConfigChange and
        //   setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent
        synchronized (mDeviceStateLock) {
            return mDeviceInventory.handleBluetoothA2dpActiveDeviceChange(device, state, profile,
                    suppressNoisyIntent, a2dpVolume);
        }
    }

    /*package*/ int setBluetoothHearingAidDeviceConnectionState(
            @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state,
    private static final class HearingAidDeviceConnectionInfo {
        final @NonNull BluetoothDevice mDevice;
        final @AudioService.BtProfileConnectionState int mState;
        final boolean mSupprNoisy;
        final int mMusicDevice;
        final @NonNull String mEventSource;

        HearingAidDeviceConnectionInfo(@NonNull BluetoothDevice device,
                @AudioService.BtProfileConnectionState int state,
                boolean suppressNoisyIntent, int musicDevice, @NonNull String eventSource) {
        AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent(
                "setHearingAidDeviceConnectionState state=" + state
                        + " addr=" + device.getAddress()
                        + " supprNoisy=" + suppressNoisyIntent
                        + " src=" + eventSource)).printLog(TAG));
        synchronized (mDeviceStateLock) {
            return mDeviceInventory.setBluetoothHearingAidDeviceConnectionState(
                    device, state, suppressNoisyIntent, musicDevice);
            mDevice = device;
            mState = state;
            mSupprNoisy = suppressNoisyIntent;
            mMusicDevice = musicDevice;
            mEventSource = eventSource;
        }
    }

    /*package*/ void postBluetoothHearingAidDeviceConnectionState(
            @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state,
            boolean suppressNoisyIntent, int musicDevice, @NonNull String eventSource) {
        final HearingAidDeviceConnectionInfo info = new HearingAidDeviceConnectionInfo(
                device, state, suppressNoisyIntent, musicDevice, eventSource);
        sendLMsgNoDelay(MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT, SENDMSG_QUEUE, info);
    }

    // never called by system components
    /*package*/ void setBluetoothScoOnByApp(boolean on) {
        synchronized (mDeviceStateLock) {
@@ -766,6 +787,35 @@ import java.util.ArrayList;
                        mBtHelper.onHeadsetProfileConnected((BluetoothHeadset) msg.obj);
                    }
                    break;
                case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT: {
                    final BtDeviceConnectionInfo info = (BtDeviceConnectionInfo) msg.obj;
                    AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent(
                            "setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent "
                                    + " state=" + info.mState
                                    // only querying address as this is the only readily available
                                    // field on the device
                                    + " addr=" + info.mDevice.getAddress()
                                    + " prof=" + info.mProfile + " supprNoisy=" + info.mSupprNoisy
                                    + " vol=" + info.mVolume)).printLog(TAG));
                    synchronized (mDeviceStateLock) {
                        mDeviceInventory.setBluetoothA2dpDeviceConnectionState(
                                info.mDevice, info.mState, info.mProfile, info.mSupprNoisy,
                                AudioSystem.DEVICE_NONE, info.mVolume);
                    }
                } break;
                case MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT: {
                    final HearingAidDeviceConnectionInfo info =
                            (HearingAidDeviceConnectionInfo) msg.obj;
                    AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent(
                            "setHearingAidDeviceConnectionState state=" + info.mState
                                    + " addr=" + info.mDevice.getAddress()
                                    + " supprNoisy=" + info.mSupprNoisy
                                    + " src=" + info.mEventSource)).printLog(TAG));
                    synchronized (mDeviceStateLock) {
                        mDeviceInventory.setBluetoothHearingAidDeviceConnectionState(
                                info.mDevice, info.mState, info.mSupprNoisy, info.mMusicDevice);
                    }
                } break;
                default:
                    Log.wtf(TAG, "Invalid message " + msg.what);
            }
@@ -809,6 +859,10 @@ import java.util.ArrayList;
    private static final int MSG_L_BT_SERVICE_CONNECTED_PROFILE_A2DP_SINK = 24;
    private static final int MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEARING_AID = 25;
    private static final int MSG_L_BT_SERVICE_CONNECTED_PROFILE_HEADSET = 26;
    // process external command to (dis)connect an A2DP device
    private static final int MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT = 27;
    // process external command to (dis)connect a hearing aid device
    private static final int MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT = 28;


    private static boolean isMessageHandledUnderWakelock(int msgId) {
@@ -821,6 +875,8 @@ import java.util.ArrayList;
            case MSG_L_A2DP_DEVICE_CONFIG_CHANGE:
            case MSG_TOGGLE_HDMI:
            case MSG_L_A2DP_ACTIVE_DEVICE_CHANGE:
            case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT:
            case MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT:
                return true;
            default:
                return false;
+39 −35
Original line number Diff line number Diff line
@@ -578,7 +578,7 @@ public final class AudioDeviceInventory {
        return mCurAudioRoutes;
    }

    /*package*/ int setBluetoothA2dpDeviceConnectionState(
    /*package*/ void setBluetoothA2dpDeviceConnectionState(
            @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state,
            int profile, boolean suppressNoisyIntent, int musicDevice, int a2dpVolume) {
        int delay;
@@ -614,15 +614,18 @@ public final class AudioDeviceInventory {
                        delay);
            }
        }
        return delay;
    }

    /*package*/ int handleBluetoothA2dpActiveDeviceChange(
            @NonNull BluetoothDevice device,
            @AudioService.BtProfileConnectionState int state, int profile,
            boolean suppressNoisyIntent, int a2dpVolume) {
        // method was added by QC but never used, and now conflicts with async behavior of
        // handleBluetoothA2dpDeviceConfigChange and
        // setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent
        if (false) {
            if (state == BluetoothProfile.STATE_DISCONNECTED) {
            return setBluetoothA2dpDeviceConnectionState(device, state, profile,
                setBluetoothA2dpDeviceConnectionState(device, state, profile,
                        suppressNoisyIntent, AudioSystem.DEVICE_NONE, a2dpVolume);
            }
            // state == BluetoothProfile.STATE_CONNECTED
@@ -657,6 +660,7 @@ public final class AudioDeviceInventory {
                    }
                }
            }
        }
        return 0;
    }

+10 −4
Original line number Diff line number Diff line
@@ -4041,7 +4041,10 @@ public class AudioService extends IAudioService.Stub
    @Retention(RetentionPolicy.SOURCE)
    public @interface BtProfileConnectionState {}

    public int setBluetoothHearingAidDeviceConnectionState(
    /**
     * See AudioManager.setBluetoothHearingAidDeviceConnectionState()
     */
    public void setBluetoothHearingAidDeviceConnectionState(
            @NonNull BluetoothDevice device, @BtProfileConnectionState int state,
            boolean suppressNoisyIntent, int musicDevice)
    {
@@ -4053,14 +4056,14 @@ public class AudioService extends IAudioService.Stub
            throw new IllegalArgumentException("Illegal BluetoothProfile state for device "
                    + " (dis)connection, got " + state);
        }
        return mDeviceBroker.setBluetoothHearingAidDeviceConnectionState(
        mDeviceBroker.postBluetoothHearingAidDeviceConnectionState(
                device, state, suppressNoisyIntent, musicDevice, "AudioService");
    }

    /**
     * See AudioManager.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent()
     */
    public int setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
    public void setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(
            @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state,
            int profile, boolean suppressNoisyIntent, int a2dpVolume) {
        if (device == null) {
@@ -4071,7 +4074,7 @@ public class AudioService extends IAudioService.Stub
            throw new IllegalArgumentException("Illegal BluetoothProfile state for device "
                    + " (dis)connection, got " + state);
        }
        return mDeviceBroker.setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(device, state,
        mDeviceBroker.postBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(device, state,
                profile, suppressNoisyIntent, a2dpVolume);
    }

@@ -4094,6 +4097,9 @@ public class AudioService extends IAudioService.Stub
    public int handleBluetoothA2dpActiveDeviceChange(
            BluetoothDevice device, int state, int profile, boolean suppressNoisyIntent,
            int a2dpVolume) {
        // FIXME method was added by @a8439e2 but never used, and now conflicts with async behavior
        //   of handleBluetoothA2dpDeviceConfigChange and
        //   setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent
        if (device == null) {
            throw new IllegalArgumentException("Illegal null device");
        }
Loading