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

Commit fe2fd0e0 authored by Jakub Pawlowski's avatar Jakub Pawlowski Committed by Gerrit Code Review
Browse files

Merge "le_audio: Introduce connection state handling for LE Audio group"

parents 1e5f1a29 46aba356
Loading
Loading
Loading
Loading
+136 −6
Original line number Diff line number Diff line
@@ -65,9 +65,6 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
     * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
     * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
     * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
     *
     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
     * receive.
     */
    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
    public static final String ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED =
@@ -82,15 +79,83 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
     * be null if no device is active. </li>
     * </ul>
     *
     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
     * receive.
     *
     * @hide
     */
    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
    public static final String ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED =
            "android.bluetooth.action.LE_AUDIO_ACTIVE_DEVICE_CHANGED";

    /**
     * Intent used to broadcast group node status information.
     *
     * <p>This intent will have 3 extra:
     * <ul>
     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can
     * be null if no device is active. </li>
     * <li> {@link #EXTRA_LE_AUDIO_GROUP_ID} - Group id. </li>
     * <li> {@link #EXTRA_LE_AUDIO_GROUP_NODE_STATUS} - Group node status. </li>
     * </ul>
     *
     * @hide
     */
    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
    public static final String ACTION_LE_AUDIO_GROUP_NODE_STATUS_CHANGED =
            "android.bluetooth.action.LE_AUDIO_GROUP_NODE_STATUS_CHANGED";


    /**
     * Intent used to broadcast group status information.
     *
     * <p>This intent will have 4 extra:
     * <ul>
     * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. It can
     * be null if no device is active. </li>
     * <li> {@link #EXTRA_LE_AUDIO_GROUP_ID} - Group id. </li>
     * <li> {@link #EXTRA_LE_AUDIO_GROUP_STATUS} - Group status. </li>
     * </ul>
     *
     * @hide
     */
    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
    public static final String ACTION_LE_AUDIO_GROUP_STATUS_CHANGED =
            "android.bluetooth.action.LE_AUDIO_GROUP_STATUS_CHANGED";

    /**
     * Intent used to broadcast group audio configuration changed information.
     *
     * <p>This intent will have 5 extra:
     * <ul>
     * <li> {@link #EXTRA_LE_AUDIO_GROUP_ID} - Group id. </li>
     * <li> {@link #EXTRA_LE_AUDIO_DIRECTION} - Direction as bit mask. </li>
     * <li> {@link #EXTRA_LE_AUDIO_SINK_LOCATION} - Sink location as per Bluetooth Assigned
     * Numbers </li>
     * <li> {@link #EXTRA_LE_AUDIO_SOURCE_LOCATION} - Source location as per Bluetooth Assigned
     * Numbers </li>
     * <li> {@link #EXTRA_LE_AUDIO_AVAILABLE_CONTEXTS} - Available contexts for group as per
     * Bluetooth Assigned Numbers </li>
     * </ul>
     *
     * @hide
     */
    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
    public static final String ACTION_LE_AUDIO_CONF_CHANGED =
            "android.bluetooth.action.LE_AUDIO_CONF_CHANGED";

    /**
     * Indicates conversation between humans as, for example, in telephony or video calls.
     * @hide
     */
    public static final int CONTEXT_TYPE_COMMUNICATION = 0x0002;

    /**
     * Indicates media as, for example, in music, public radio, podcast or video soundtrack.
     * @hide
     */
    public static final int CONTEXT_TYPE_MEDIA = 0x0004;

    /**
     * This represents an invalid group ID.
     *
@@ -98,6 +163,71 @@ public final class BluetoothLeAudio implements BluetoothProfile, AutoCloseable {
     */
    public static final int GROUP_ID_INVALID = IBluetoothLeAudio.LE_AUDIO_GROUP_ID_INVALID;

    /**
     * Contains group id.
     * @hide
     */
    public static final String EXTRA_LE_AUDIO_GROUP_ID =
            "android.bluetooth.extra.LE_AUDIO_GROUP_ID";

    /**
     * Contains group node status, can be any of
     * <p>
     * <ul>
     * <li> {@link #GROUP_NODE_ADDED} </li>
     * <li> {@link #GROUP_NODE_REMOVED} </li>
     * </ul>
     * <p>
     * @hide
     */
    public static final String EXTRA_LE_AUDIO_GROUP_NODE_STATUS =
            "android.bluetooth.extra.LE_AUDIO_GROUP_NODE_STATUS";

    /**
     * Contains group status, can be any of
     *
     * <p>
     * <ul>
     * <li> {@link #GROUP_STATUS_IDLE} </li>
     * <li> {@link #GROUP_STATUS_STREAMING} </li>
     * <li> {@link #GROUP_STATUS_SUSPENDED} </li>
     * <li> {@link #GROUP_STATUS_RECONFIGURED} </li>
     * <li> {@link #GROUP_STATUS_DESTROYED} </li>
     * </ul>
     * <p>
     * @hide
     */
    public static final String EXTRA_LE_AUDIO_GROUP_STATUS =
            "android.bluetooth.extra.LE_AUDIO_GROUP_STATUS";

    /**
     * Contains bit mask for direction, bit 0 set when Sink, bit 1 set when Source.
     * @hide
     */
    public static final String EXTRA_LE_AUDIO_DIRECTION =
            "android.bluetooth.extra.LE_AUDIO_DIRECTION";

    /**
     * Contains source location as per Bluetooth Assigned Numbers
     * @hide
     */
    public static final String EXTRA_LE_AUDIO_SOURCE_LOCATION =
            "android.bluetooth.extra.LE_AUDIO_SOURCE_LOCATION";

    /**
     * Contains sink location as per Bluetooth Assigned Numbers
     * @hide
     */
    public static final String EXTRA_LE_AUDIO_SINK_LOCATION =
            "android.bluetooth.extra.LE_AUDIO_SINK_LOCATION";

    /**
     * Contains available context types for group as per Bluetooth Assigned Numbers
     * @hide
     */
    public static final String EXTRA_LE_AUDIO_AVAILABLE_CONTEXTS =
            "android.bluetooth.extra.LE_AUDIO_AVAILABLE_CONTEXTS";

    private BluetoothAdapter mAdapter;
    private final BluetoothProfileConnector<IBluetoothLeAudio> mProfileConnector =
            new BluetoothProfileConnector(this, BluetoothProfile.LE_AUDIO, "BluetoothLeAudio",
+34 −0
Original line number Diff line number Diff line
@@ -5273,6 +5273,40 @@ public class AudioManager {
        }
    }

    /**
    * Indicate Le Audio output device connection state change and eventually suppress
    * the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent.
    * @param device Bluetooth device connected/disconnected
    * @param state new connection state (BluetoothProfile.STATE_xxx)
    * @param suppressNoisyIntent if true the
    * {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent will not be sent.
    * {@hide}
    */
    public void setBluetoothLeAudioOutDeviceConnectionState(BluetoothDevice device, int state,
            boolean suppressNoisyIntent) {
        final IAudioService service = getService();
        try {
            service.setBluetoothLeAudioOutDeviceConnectionState(device, state, suppressNoisyIntent);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
    * Indicate Le Audio input connection state change.
    * @param device Bluetooth device connected/disconnected
    * @param state new connection state (BluetoothProfile.STATE_xxx)
    * {@hide}
    */
    public void setBluetoothLeAudioInDeviceConnectionState(BluetoothDevice device, int state) {
        final IAudioService service = getService();
        try {
            service.setBluetoothLeAudioInDeviceConnectionState(device, state);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

     /**
     * Indicate A2DP source or sink connection state change and eventually suppress
     * the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent.
+5 −0
Original line number Diff line number Diff line
@@ -256,6 +256,11 @@ interface IAudioService {
    void setBluetoothHearingAidDeviceConnectionState(in BluetoothDevice device,
            int state, boolean suppressNoisyIntent, int musicDevice);

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

    void setBluetoothLeAudioInDeviceConnectionState(in BluetoothDevice device, int state);

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

+94 −1
Original line number Diff line number Diff line
@@ -563,6 +563,37 @@ import java.util.concurrent.atomic.AtomicBoolean;
        sendLMsgNoDelay(MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT, SENDMSG_QUEUE, info);
    }

    private static final class LeAudioDeviceConnectionInfo {
        final @NonNull BluetoothDevice mDevice;
        final @AudioService.BtProfileConnectionState int mState;
        final boolean mSupprNoisy;
        final @NonNull String mEventSource;

        LeAudioDeviceConnectionInfo(@NonNull BluetoothDevice device,
                @AudioService.BtProfileConnectionState int state,
                boolean suppressNoisyIntent, @NonNull String eventSource) {
            mDevice = device;
            mState = state;
            mSupprNoisy = suppressNoisyIntent;
            mEventSource = eventSource;
        }
    }

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

    /*package*/ void postBluetoothLeAudioInDeviceConnectionState(
            @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state,
            @NonNull String eventSource) {
        final LeAudioDeviceConnectionInfo info = new LeAudioDeviceConnectionInfo(
                device, state, false, eventSource);
        sendLMsgNoDelay(MSG_L_LE_AUDIO_DEVICE_IN_CONNECTION_CHANGE_EXT, SENDMSG_QUEUE, info);
    }

    /**
     * Current Bluetooth SCO audio active state indicated by BtHelper via setBluetoothScoOn().
@@ -849,6 +880,23 @@ import java.util.concurrent.atomic.AtomicBoolean;
                delay);
    }

    /*package*/ void postSetLeAudioOutConnectionState(
            @AudioService.BtProfileConnectionState int state,
            @NonNull BluetoothDevice device, int delay) {
        sendILMsg(MSG_IL_SET_LE_AUDIO_OUT_CONNECTION_STATE, SENDMSG_QUEUE,
                state,
                device,
                delay);
    }

    /*package*/ void postSetLeAudioInConnectionState(
            @AudioService.BtProfileConnectionState int state,
            @NonNull BluetoothDevice device) {
        sendILMsgNoDelay(MSG_IL_SET_LE_AUDIO_IN_CONNECTION_STATE, SENDMSG_QUEUE,
                state,
                device);
    }

    /*package*/ void postDisconnectA2dp() {
        sendMsgNoDelay(MSG_DISCONNECT_A2DP, SENDMSG_QUEUE);
    }
@@ -1154,7 +1202,20 @@ import java.util.concurrent.atomic.AtomicBoolean;
                    synchronized (mDeviceStateLock) {
                        mDeviceInventory.onSetHearingAidConnectionState(
                                (BluetoothDevice) msg.obj, msg.arg1,
                                mAudioService.getHearingAidStreamType());
                                mAudioService.getBluetoothContextualVolumeStream());
                    }
                    break;
                case MSG_IL_SET_LE_AUDIO_OUT_CONNECTION_STATE:
                    synchronized (mDeviceStateLock) {
                        mDeviceInventory.onSetLeAudioOutConnectionState(
                                (BluetoothDevice) msg.obj, msg.arg1,
                                mAudioService.getBluetoothContextualVolumeStream());
                    }
                    break;
                case MSG_IL_SET_LE_AUDIO_IN_CONNECTION_STATE:
                    synchronized (mDeviceStateLock) {
                        mDeviceInventory.onSetLeAudioInConnectionState(
                                (BluetoothDevice) msg.obj, msg.arg1);
                    }
                    break;
                case MSG_BT_HEADSET_CNCT_FAILED:
@@ -1343,6 +1404,31 @@ import java.util.concurrent.atomic.AtomicBoolean;
                    final int capturePreset = msg.arg1;
                    mDeviceInventory.onSaveClearPreferredDevicesForCapturePreset(capturePreset);
                } break;
                case MSG_L_LE_AUDIO_DEVICE_OUT_CONNECTION_CHANGE_EXT: {
                    final LeAudioDeviceConnectionInfo info =
                            (LeAudioDeviceConnectionInfo) msg.obj;
                    AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent(
                            "setLeAudioDeviceOutConnectionState state=" + info.mState
                                    + " addr=" + info.mDevice.getAddress()
                                    + " supprNoisy=" + info.mSupprNoisy
                                    + " src=" + info.mEventSource)).printLog(TAG));
                    synchronized (mDeviceStateLock) {
                        mDeviceInventory.setBluetoothLeAudioOutDeviceConnectionState(
                                info.mDevice, info.mState, info.mSupprNoisy);
                    }
                } break;
                case MSG_L_LE_AUDIO_DEVICE_IN_CONNECTION_CHANGE_EXT: {
                    final LeAudioDeviceConnectionInfo info =
                            (LeAudioDeviceConnectionInfo) msg.obj;
                    AudioService.sDeviceLogger.log((new AudioEventLogger.StringEvent(
                            "setLeAudioDeviceInConnectionState state=" + info.mState
                                    + " addr=" + info.mDevice.getAddress()
                                    + " src=" + info.mEventSource)).printLog(TAG));
                    synchronized (mDeviceStateLock) {
                        mDeviceInventory.setBluetoothLeAudioInDeviceConnectionState(info.mDevice,
                                info.mState);
                    }
                } break;
                default:
                    Log.wtf(TAG, "Invalid message " + msg.what);
            }
@@ -1424,6 +1510,11 @@ import java.util.concurrent.atomic.AtomicBoolean;
    private static final int MSG_IL_SET_PREF_DEVICES_FOR_STRATEGY = 40;
    private static final int MSG_I_REMOVE_PREF_DEVICES_FOR_STRATEGY = 41;

    private static final int MSG_IL_SET_LE_AUDIO_OUT_CONNECTION_STATE = 42;
    private static final int MSG_IL_SET_LE_AUDIO_IN_CONNECTION_STATE = 43;
    private static final int MSG_L_LE_AUDIO_DEVICE_OUT_CONNECTION_CHANGE_EXT = 44;
    private static final int MSG_L_LE_AUDIO_DEVICE_IN_CONNECTION_CHANGE_EXT = 45;

    private static boolean isMessageHandledUnderWakelock(int msgId) {
        switch(msgId) {
            case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE:
@@ -1439,6 +1530,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
            case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT_DISCONNECTION:
            case MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT:
            case MSG_CHECK_MUTE_MUSIC:
            case MSG_L_LE_AUDIO_DEVICE_OUT_CONNECTION_CHANGE_EXT:
            case MSG_L_LE_AUDIO_DEVICE_IN_CONNECTION_CHANGE_EXT:
                return true;
            default:
                return false;
+93 −0
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHearingAid;
import android.bluetooth.BluetoothLeAudio;
import android.bluetooth.BluetoothProfile;
import android.content.Intent;
import android.media.AudioDeviceAttributes;
@@ -423,6 +424,45 @@ public class AudioDeviceInventory {
        }
    }

    /*package*/ void onSetLeAudioConnectionState(BluetoothDevice btDevice,
                @AudioService.BtProfileConnectionState int state, int streamType, int device) {
        String address = btDevice.getAddress();
        if (!BluetoothAdapter.checkBluetoothAddress(address)) {
            address = "";
        }
        AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
                "onSetLeAudioConnectionState addr=" + address));

        synchronized (mDevicesLock) {
            DeviceInfo di = null;
            boolean isConnected = false;

            String key = DeviceInfo.makeDeviceListKey(device, btDevice.getAddress());
            di = mConnectedDevices.get(key);
            isConnected = di != null;

            if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
                makeLeAudioDeviceUnavailable(address, device);
            } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
                makeLeAudioDeviceAvailable(address, BtHelper.getName(btDevice), streamType,
                        device, "onSetLeAudioConnectionState");
            }
        }
    }

    /*package*/ void onSetLeAudioOutConnectionState(BluetoothDevice btDevice,
                @AudioService.BtProfileConnectionState int state, int streamType) {
        // TODO: b/198610537 clarify DEVICE_OUT_BLE_HEADSET vs DEVICE_OUT_BLE_SPEAKER criteria
        onSetLeAudioConnectionState(btDevice, state, streamType,
                AudioSystem.DEVICE_OUT_BLE_HEADSET);
    }

    /*package*/ void onSetLeAudioInConnectionState(BluetoothDevice btDevice,
                @AudioService.BtProfileConnectionState int state) {
        onSetLeAudioConnectionState(btDevice, state, AudioSystem.STREAM_DEFAULT,
                AudioSystem.DEVICE_IN_BLE_HEADSET);
    }

    @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
        /*package*/ void onBluetoothA2dpActiveDeviceChange(
            @NonNull BtHelper.BluetoothA2dpDeviceInfo btInfo, int event) {
@@ -943,6 +983,28 @@ public class AudioDeviceInventory {
        }
    }

    /*package*/ int setBluetoothLeAudioOutDeviceConnectionState(
            @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state,
            boolean suppressNoisyIntent) {
        synchronized (mDevicesLock) {
            /* Active device become null and it's previous device is not connected anymore */
            int delay = 0;
            if (!suppressNoisyIntent) {
                int intState = (state == BluetoothLeAudio.STATE_CONNECTED) ? 1 : 0;
                delay = checkSendBecomingNoisyIntentInt(AudioSystem.DEVICE_OUT_BLE_HEADSET,
                        intState, AudioSystem.DEVICE_NONE);
            }
            mDeviceBroker.postSetLeAudioOutConnectionState(state, device, delay);
            return delay;
        }
    }

    /*package*/ void setBluetoothLeAudioInDeviceConnectionState(
            @NonNull BluetoothDevice device, @AudioService.BtProfileConnectionState int state) {
        synchronized (mDevicesLock) {
            mDeviceBroker.postSetLeAudioInConnectionState(state, device);
        }
    }

    //-------------------------------------------------------------------
    // Internal utilities
@@ -1114,6 +1176,36 @@ public class AudioDeviceInventory {
                .record();
    }

    @GuardedBy("mDevicesLock")
    private void makeLeAudioDeviceAvailable(String address, String name, int streamType, int device,
            String eventSource) {
        if (device != AudioSystem.DEVICE_NONE) {
            AudioSystem.setDeviceConnectionState(device, AudioSystem.DEVICE_STATE_AVAILABLE,
                    address, name, AudioSystem.AUDIO_FORMAT_DEFAULT);
            mConnectedDevices.put(DeviceInfo.makeDeviceListKey(device, address),
                    new DeviceInfo(device, name, address, AudioSystem.AUDIO_FORMAT_DEFAULT));
            mDeviceBroker.postAccessoryPlugMediaUnmute(device);
        }

        if (streamType == AudioSystem.STREAM_DEFAULT) {
            // No need to update volume for input devices
            return;
        }

        mDeviceBroker.postApplyVolumeOnDevice(streamType, device, "makeLeAudioDeviceAvailable");
    }

    @GuardedBy("mDevicesLock")
    private void makeLeAudioDeviceUnavailable(String address, int device) {
        if (device != AudioSystem.DEVICE_NONE) {
            AudioSystem.setDeviceConnectionState(device, AudioSystem.DEVICE_STATE_UNAVAILABLE,
                    address, "", AudioSystem.AUDIO_FORMAT_DEFAULT);
            mConnectedDevices.remove(DeviceInfo.makeDeviceListKey(device, address));
        }

        setCurrentAudioRouteNameIfPossible(null, false /*fromA2dp*/);
    }

    @GuardedBy("mDevicesLock")
    private void setCurrentAudioRouteNameIfPossible(String name, boolean fromA2dp) {
        synchronized (mCurAudioRoutes) {
@@ -1150,6 +1242,7 @@ public class AudioDeviceInventory {
        BECOMING_NOISY_INTENT_DEVICES_SET.add(AudioSystem.DEVICE_OUT_HEARING_AID);
        BECOMING_NOISY_INTENT_DEVICES_SET.addAll(AudioSystem.DEVICE_OUT_ALL_A2DP_SET);
        BECOMING_NOISY_INTENT_DEVICES_SET.addAll(AudioSystem.DEVICE_OUT_ALL_USB_SET);
        BECOMING_NOISY_INTENT_DEVICES_SET.addAll(AudioSystem.DEVICE_OUT_ALL_BLE_SET);
    }

    // must be called before removing the device from mConnectedDevices
Loading