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

Commit 2c3f54c5 authored by Rongxuan Liu's avatar Rongxuan Liu
Browse files

[AudioStream] Hysteresis mode support

Flag: com.android.settingslib.flags.audio_sharing_hysteresis_mode_fix
Test: atest com.android.settings.connecteddevice.audiosharing.audiostreams
Test: manual test with broadcast hysteresis mode
Bug: 355222285
Bug: 355221818
Change-Id: If3a1fbdc391eeda6979868829bc00c435a43c329
parent c3cfb425
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -13553,6 +13553,8 @@
    <string name="audio_streams_dialog_cannot_play">Can\u0027t play this audio stream on <xliff:g example="LE headset" id="device_name">%1$s</xliff:g>.</string>
    <!-- The preference summary when add source succeed [CHAR LIMIT=NONE] -->
    <string name="audio_streams_listening_now">Listening now</string>
    <!-- The preference summary when source is present on sinks [CHAR LIMIT=NONE] -->
    <string name="audio_streams_present_now">Paused by host</string>
    <!-- Le audio streams service notification leave broadcast text [CHAR LIMIT=NONE] -->
    <string name="audio_streams_media_service_notification_leave_broadcast_text">Stop listening</string>
    <!-- Le audio streams no le device dialog title [CHAR LIMIT=NONE] -->
+20 −6
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.settings.connecteddevice.audiosharing.audiostreams;

import static com.android.settingslib.flags.Flags.audioSharingHysteresisModeFix;

import android.app.settings.SettingsEnums;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeBroadcastAssistant;
@@ -41,6 +43,7 @@ import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
import com.android.settingslib.utils.ThreadUtils;
import com.android.settingslib.widget.ActionButtonsPreference;

import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

@@ -73,14 +76,20 @@ public class AudioStreamButtonController extends BasePreferenceController
                        int sourceId,
                        BluetoothLeBroadcastReceiveState state) {
                    super.onReceiveStateChanged(sink, sourceId, state);
                    if (AudioStreamsHelper.isConnected(state)) {
                    boolean shouldUpdateButton =
                            audioSharingHysteresisModeFix()
                                    ? AudioStreamsHelper.hasSourcePresent(state)
                                    : AudioStreamsHelper.isConnected(state);
                    if (shouldUpdateButton) {
                        updateButton();
                        if (AudioStreamsHelper.isConnected(state)) {
                            mMetricsFeatureProvider.action(
                                    mContext,
                                    SettingsEnums.ACTION_AUDIO_STREAM_JOIN_SUCCEED,
                                    SOURCE_ORIGIN_REPOSITORY);
                        }
                    }
                }

                @Override
                public void onSourceAddFailed(
@@ -146,8 +155,13 @@ public class AudioStreamButtonController extends BasePreferenceController
            Log.w(TAG, "updateButton(): preference is null!");
            return;
        }

        List<BluetoothLeBroadcastReceiveState> sources =
                audioSharingHysteresisModeFix()
                        ? mAudioStreamsHelper.getAllPresentSources()
                        : mAudioStreamsHelper.getAllConnectedSources();
        boolean isConnected =
                mAudioStreamsHelper.getAllConnectedSources().stream()
                sources.stream()
                        .map(BluetoothLeBroadcastReceiveState::getBroadcastId)
                        .anyMatch(connectedBroadcastId -> connectedBroadcastId == mBroadcastId);

+34 −4
Original line number Diff line number Diff line
@@ -16,6 +16,10 @@

package com.android.settings.connecteddevice.audiosharing.audiostreams;

import static com.android.settingslib.flags.Flags.audioSharingHysteresisModeFix;

import static java.util.stream.Collectors.toList;

import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeBroadcastAssistant;
import android.bluetooth.BluetoothLeBroadcastReceiveState;
@@ -48,6 +52,8 @@ public class AudioStreamHeaderController extends BasePreferenceController
    static final int AUDIO_STREAM_HEADER_LISTENING_NOW_SUMMARY =
            R.string.audio_streams_listening_now;

    static final int AUDIO_STREAM_HEADER_PRESENT_NOW_SUMMARY = R.string.audio_streams_present_now;

    @VisibleForTesting static final String AUDIO_STREAM_HEADER_NOT_LISTENING_SUMMARY = "";
    private static final String TAG = "AudioStreamHeaderController";
    private static final String KEY = "audio_stream_header";
@@ -80,6 +86,10 @@ public class AudioStreamHeaderController extends BasePreferenceController
                        updateSummary();
                        mAudioStreamsHelper.startMediaService(
                                mContext, mBroadcastId, mBroadcastName);
                    } else if (audioSharingHysteresisModeFix()
                            && AudioStreamsHelper.hasSourcePresent(state)) {
                        // if source present but not connected, only update the summary
                        updateSummary();
                    }
                }
            };
@@ -140,8 +150,27 @@ public class AudioStreamHeaderController extends BasePreferenceController
        var unused =
                ThreadUtils.postOnBackgroundThread(
                        () -> {
                            var connectedSourceList =
                                    mAudioStreamsHelper.getAllPresentSources().stream()
                                            .filter(
                                                    state ->
                                                            (state.getBroadcastId()
                                                                    == mBroadcastId))
                                            .collect(toList());

                            var latestSummary =
                                    mAudioStreamsHelper.getAllConnectedSources().stream()
                                    audioSharingHysteresisModeFix()
                                            ? connectedSourceList.isEmpty()
                                                    ? AUDIO_STREAM_HEADER_NOT_LISTENING_SUMMARY
                                                    : (connectedSourceList.stream()
                                                                    .anyMatch(
                                                                            AudioStreamsHelper
                                                                                    ::isConnected)
                                                            ? mContext.getString(
                                                                    AUDIO_STREAM_HEADER_LISTENING_NOW_SUMMARY)
                                                            : mContext.getString(
                                                                    AUDIO_STREAM_HEADER_PRESENT_NOW_SUMMARY))
                                            : mAudioStreamsHelper.getAllConnectedSources().stream()
                                                    .map(
                                                            BluetoothLeBroadcastReceiveState
                                                                    ::getBroadcastId)
@@ -152,6 +181,7 @@ public class AudioStreamHeaderController extends BasePreferenceController
                                                    ? mContext.getString(
                                                            AUDIO_STREAM_HEADER_LISTENING_NOW_SUMMARY)
                                                    : AUDIO_STREAM_HEADER_NOT_LISTENING_SUMMARY;

                            ThreadUtils.postOnMainThread(
                                    () -> {
                                        if (mHeaderController != null) {
+8 −2
Original line number Diff line number Diff line
@@ -18,6 +18,8 @@ package com.android.settings.connecteddevice.audiosharing.audiostreams;

import static android.text.Spanned.SPAN_EXCLUSIVE_INCLUSIVE;

import static com.android.settingslib.flags.Flags.audioSharingHysteresisModeFix;

import android.os.Handler;
import android.os.Looper;
import android.text.SpannableString;
@@ -94,8 +96,12 @@ class AudioStreamStateHandler {
                    }
                    preference.setIsConnected(
                            newState
                                    == AudioStreamsProgressCategoryController.AudioStreamState
                                            .SOURCE_ADDED);
                                            == AudioStreamsProgressCategoryController
                                                    .AudioStreamState.SOURCE_ADDED
                                    || (audioSharingHysteresisModeFix()
                                            && newState
                                                    == AudioStreamsProgressCategoryController
                                                            .AudioStreamState.SOURCE_PRESENT));
                    preference.setOnPreferenceClickListener(getOnClickListener(controller));
                });
    }
+37 −3
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.settings.connecteddevice.audiosharing.audiostreams;
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamMediaService.BROADCAST_ID;
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamMediaService.BROADCAST_TITLE;
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamMediaService.DEVICES;
import static com.android.settingslib.flags.Flags.audioSharingHysteresisModeFix;

import static java.util.Collections.emptyList;

@@ -63,6 +64,12 @@ public class AudioStreamsHelper {

    private final @Nullable LocalBluetoothManager mBluetoothManager;
    private final @Nullable LocalBluetoothLeBroadcastAssistant mLeBroadcastAssistant;
    // Referring to Broadcast Audio Scan Service 1.0
    // Table 3.9: Broadcast Receive State characteristic format
    // 0x00000000: 0b0 = Not synchronized to BIS_index[x]
    // 0xFFFFFFFF: Failed to sync to BIG
    private static final long BIS_SYNC_NOT_SYNC_TO_BIS = 0x00000000L;
    private static final long BIS_SYNC_FAILED_SYNC_TO_BIG = 0xFFFFFFFFL;

    AudioStreamsHelper(@Nullable LocalBluetoothManager bluetoothManager) {
        mBluetoothManager = bluetoothManager;
@@ -144,6 +151,19 @@ public class AudioStreamsHelper {
                .toList();
    }

    /** Retrieves a list of all LE broadcast receive states from sinks with source present. */
    @VisibleForTesting
    public List<BluetoothLeBroadcastReceiveState> getAllPresentSources() {
        if (mLeBroadcastAssistant == null) {
            Log.w(TAG, "getAllPresentSources(): LeBroadcastAssistant is null!");
            return emptyList();
        }
        return getConnectedBluetoothDevices(mBluetoothManager, /* inSharingOnly= */ true).stream()
                .flatMap(sink -> mLeBroadcastAssistant.getAllSources(sink).stream())
                .filter(AudioStreamsHelper::hasSourcePresent)
                .toList();
    }

    /** Retrieves LocalBluetoothLeBroadcastAssistant. */
    @VisibleForTesting
    @Nullable
@@ -153,7 +173,18 @@ public class AudioStreamsHelper {

    /** Checks the connectivity status based on the provided broadcast receive state. */
    public static boolean isConnected(BluetoothLeBroadcastReceiveState state) {
        return state.getBisSyncState().stream().anyMatch(bitmap -> bitmap != 0);
        return state.getBisSyncState().stream()
                .anyMatch(
                        bitmap ->
                                (bitmap != BIS_SYNC_NOT_SYNC_TO_BIS
                                        && bitmap != BIS_SYNC_FAILED_SYNC_TO_BIG));
    }

    /** Checks the connectivity status based on the provided broadcast receive state. */
    public static boolean hasSourcePresent(BluetoothLeBroadcastReceiveState state) {
        // Referring to Broadcast Audio Scan Service 1.0
        // All zero address means no source on the sink device
        return !state.getSourceDevice().getAddress().equals("00:00:00:00:00:00");
    }

    static boolean isBadCode(BluetoothLeBroadcastReceiveState state) {
@@ -242,7 +273,8 @@ public class AudioStreamsHelper {
        List<BluetoothLeBroadcastReceiveState> sourceList =
                assistant.getAllSources(cachedDevice.getDevice());
        if (!sourceList.isEmpty()
                && sourceList.stream().anyMatch(AudioStreamsHelper::isConnected)) {
                && (audioSharingHysteresisModeFix()
                        || sourceList.stream().anyMatch(AudioStreamsHelper::isConnected))) {
            Log.d(
                    TAG,
                    "Lead device has connected broadcast source, device = "
@@ -253,7 +285,9 @@ public class AudioStreamsHelper {
        for (CachedBluetoothDevice device : cachedDevice.getMemberDevice()) {
            List<BluetoothLeBroadcastReceiveState> list =
                    assistant.getAllSources(device.getDevice());
            if (!list.isEmpty() && list.stream().anyMatch(AudioStreamsHelper::isConnected)) {
            if (!list.isEmpty()
                    && (audioSharingHysteresisModeFix()
                            || list.stream().anyMatch(AudioStreamsHelper::isConnected))) {
                Log.d(
                        TAG,
                        "Member device has connected broadcast source, device = "
Loading