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

Commit 0acf60cc authored by Rongxuan Liu's avatar Rongxuan Liu Committed by Gerrit Code Review
Browse files

Merge "[le audio] Avoid inactivating the device while receiving broadcast" into main

parents cb7aef9f 7ea933a3
Loading
Loading
Loading
Loading
+21 −21
Original line number Diff line number Diff line
@@ -636,7 +636,7 @@ public class BassClientService extends ProfileService {
            return;
        }

        boolean isAssistantActive = isAnyReceiverReceivingBroadcast();
        boolean isAssistantActive = isAnyReceiverReceivingBroadcast(getConnectedDevices());

        if (isAssistantActive) {
            /* Assistant become active */
@@ -1908,25 +1908,6 @@ public class BassClientService extends ProfileService {
        return true;
    }

    private boolean isAnyReceiverReceivingBroadcast() {
        for (BluetoothDevice device : getConnectedDevices()) {
            for (BluetoothLeBroadcastReceiveState receiveState : getAllSources(device)) {
                for (int i = 0; i < receiveState.getNumSubgroups(); i++) {
                    Long syncState = receiveState.getBisSyncState().get(i);

                    /* Not synced to BIS of failed to sync to BIG */
                    if (syncState == 0x00000000 || syncState == 0xFFFFFFFF) {
                        continue;
                    }

                    return true;
                }
            }
        }

        return false;
    }

    /** Return true if there is any non primary device receiving broadcast */
    private boolean isAudioSharingModeOn(Integer broadcastId) {
        if (mLocalBroadcastReceivers == null) {
@@ -2069,7 +2050,7 @@ public class BassClientService extends ProfileService {
        mUnicastSourceStreamStatus = Optional.of(status);

        if (status == STATUS_LOCAL_STREAM_REQUESTED) {
            if (isAnyReceiverReceivingBroadcast()) {
            if (isAnyReceiverReceivingBroadcast(getConnectedDevices())) {
                suspendAllReceiversSourceSynchronization();
            }
        } else if (status == STATUS_LOCAL_STREAM_SUSPENDED) {
@@ -2092,6 +2073,25 @@ public class BassClientService extends ProfileService {
        }
    }

    /** Check if any sink receivers are receiving broadcast stream */
    public boolean isAnyReceiverReceivingBroadcast(List<BluetoothDevice> devices) {
        for (BluetoothDevice device : devices) {
            for (BluetoothLeBroadcastReceiveState receiveState : getAllSources(device)) {
                for (int i = 0; i < receiveState.getNumSubgroups(); i++) {
                    Long syncState = receiveState.getBisSyncState().get(i);
                    /* Not synced to BIS of failed to sync to BIG */
                    if (syncState == 0x00000000 || syncState == 0xFFFFFFFF) {
                        continue;
                    }

                    return true;
                }
            }
        }

        return false;
    }

    /** Handle broadcast state changed */
    public void notifyBroadcastStateChanged(int state, int broadcastId) {
        switch (state) {
+16 −1
Original line number Diff line number Diff line
@@ -2196,7 +2196,9 @@ public class LeAudioService extends ProfileService {
            case LeAudioStackEvent.HEALTH_RECOMMENDATION_ACTION_INACTIVATE_GROUP:
                if (Flags.leaudioUnicastInactivateDeviceBasedOnContext()) {
                    LeAudioGroupDescriptor groupDescriptor = getGroupDescriptor(groupId);
                    if (groupDescriptor != null && groupDescriptor.mIsActive) {
                    if (groupDescriptor != null
                            && groupDescriptor.mIsActive
                            && !isGroupReceivingBroadcast(groupId)) {
                        Log.i(
                                TAG,
                                "Group "
@@ -2332,6 +2334,19 @@ public class LeAudioService extends ProfileService {
        }
    }

    private boolean isGroupReceivingBroadcast(int groupId) {
        if (!Flags.leaudioBroadcastAudioHandoverPolicies()) {
            return false;
        }

        BassClientService bassClientService = getBassClientService();
        if (bassClientService == null) {
            return false;
        }

        return bassClientService.isAnyReceiverReceivingBroadcast(getGroupDevices(groupId));
    }

    private void notifyGroupStreamStatusChanged(int groupId, int groupStreamStatus) {
        if (mLeAudioCallbacks != null) {
            int n = mLeAudioCallbacks.beginBroadcast();
+93 −0
Original line number Diff line number Diff line
@@ -1588,6 +1588,99 @@ public class BassClientServiceTest {
        }
    }

    @Test
    public void testIsAnyReceiverReceivingBroadcast() {
        prepareConnectedDeviceGroup();
        BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);
        verifyAddSourceForGroup(meta);
        for (BassClientStateMachine sm : mStateMachines.values()) {
            if (sm.getDevice().equals(mCurrentDevice)) {
                injectRemoteSourceStateSourceAdded(
                        sm,
                        meta,
                        TEST_SOURCE_ID,
                        BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE,
                        meta.isEncrypted()
                                ? BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING
                                : BluetoothLeBroadcastReceiveState
                                        .BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
                        null);
                injectRemoteSourceStateChanged(
                        sm,
                        meta,
                        TEST_SOURCE_ID,
                        BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED,
                        meta.isEncrypted()
                                ? BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING
                                : BluetoothLeBroadcastReceiveState
                                        .BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
                        null,
                        (long) 0x00000000);
            } else if (sm.getDevice().equals(mCurrentDevice1)) {
                injectRemoteSourceStateSourceAdded(
                        sm,
                        meta,
                        TEST_SOURCE_ID + 1,
                        BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_IDLE,
                        meta.isEncrypted()
                                ? BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING
                                : BluetoothLeBroadcastReceiveState
                                        .BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
                        null);
                injectRemoteSourceStateChanged(
                        sm,
                        meta,
                        TEST_SOURCE_ID + 1,
                        BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED,
                        meta.isEncrypted()
                                ? BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING
                                : BluetoothLeBroadcastReceiveState
                                        .BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
                        null,
                        (long) 0x00000000);
            }
        }

        List<BluetoothDevice> devices = mBassClientService.getConnectedDevices();
        // Verify isAnyReceiverReceivingBroadcast returns false if no BIS synced
        assertThat(mBassClientService.isAnyReceiverReceivingBroadcast(devices)).isFalse();

        // Update receiver state with lost BIS sync
        for (BassClientStateMachine sm : mStateMachines.values()) {
            if (sm.getDevice().equals(mCurrentDevice)) {
                injectRemoteSourceStateChanged(
                        sm,
                        meta,
                        TEST_SOURCE_ID,
                        BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED,
                        meta.isEncrypted()
                                ? BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING
                                : BluetoothLeBroadcastReceiveState
                                        .BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
                        null,
                        (long) 0x00000001);
            } else if (sm.getDevice().equals(mCurrentDevice1)) {
                injectRemoteSourceStateChanged(
                        sm,
                        meta,
                        TEST_SOURCE_ID + 1,
                        BluetoothLeBroadcastReceiveState.PA_SYNC_STATE_SYNCHRONIZED,
                        meta.isEncrypted()
                                ? BluetoothLeBroadcastReceiveState.BIG_ENCRYPTION_STATE_DECRYPTING
                                : BluetoothLeBroadcastReceiveState
                                        .BIG_ENCRYPTION_STATE_NOT_ENCRYPTED,
                        null,
                        (long) 0x00000002);
            }
        }
        BluetoothDevice invalidDevice = TestUtils.getTestDevice(mBluetoothAdapter, 2);
        // Verify isAnyReceiverReceivingBroadcast returns false if invalid device
        assertThat(mBassClientService.isAnyReceiverReceivingBroadcast(List.of(invalidDevice)))
                .isFalse();
        // Verify isAnyReceiverReceivingBroadcast returns true if BIS synced
        assertThat(mBassClientService.isAnyReceiverReceivingBroadcast(devices)).isTrue();
    }

    private void prepareTwoSynchronizedDevices() {
        BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);

+48 −0
Original line number Diff line number Diff line
@@ -58,6 +58,7 @@ import androidx.test.filters.MediumTest;
import androidx.test.runner.AndroidJUnit4;

import com.android.bluetooth.TestUtils;
import com.android.bluetooth.bass_client.BassClientService;
import com.android.bluetooth.btservice.ActiveDeviceManager;
import com.android.bluetooth.btservice.AdapterService;
import com.android.bluetooth.btservice.ServiceFactory;
@@ -132,6 +133,7 @@ public class LeAudioServiceTest {
    @Mock private VolumeControlService mVolumeControlService;
    @Mock private HapClientService mHapClientService;
    @Mock private CsipSetCoordinatorService mCsipSetCoordinatorService;
    @Mock private BassClientService mBassClientService;
    @Spy private LeAudioObjectsFactory mObjectsFactory = LeAudioObjectsFactory.getInstance();
    @Spy private ServiceFactory mServiceFactory = new ServiceFactory();

@@ -202,10 +204,12 @@ public class LeAudioServiceTest {
        mService.mMcpService = mMcpService;
        mService.mTbsService = mTbsService;
        mService.mHapClientService = mHapClientService;
        mService.mBassClientService = mBassClientService;
        mService.mServiceFactory = mServiceFactory;
        when(mServiceFactory.getVolumeControlService()).thenReturn(mVolumeControlService);
        when(mServiceFactory.getHapClientService()).thenReturn(mHapClientService);
        when(mServiceFactory.getCsipSetCoordinatorService()).thenReturn(mCsipSetCoordinatorService);
        when(mServiceFactory.getBassClientService()).thenReturn(mBassClientService);

        LeAudioStackEvent stackEvent =
        new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_NATIVE_INITIALIZED);
@@ -1767,6 +1771,50 @@ public class LeAudioServiceTest {
                        eq(mSingleDevice), any(), any(BluetoothProfileConnectionInfo.class));
    }

    @Test
    public void testMediaContextUnavailableWhileReceivingBroadcast() {
        mSetFlagsRule.enableFlags(Flags.FLAG_LEAUDIO_UNICAST_INACTIVATE_DEVICE_BASED_ON_CONTEXT);
        mSetFlagsRule.enableFlags(Flags.FLAG_AUDIO_ROUTING_CENTRALIZATION);
        mSetFlagsRule.enableFlags(Flags.FLAG_LEAUDIO_BROADCAST_AUDIO_HANDOVER_POLICIES);

        doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
        connectTestDevice(mSingleDevice, testGroupId);

        Integer contexts = BluetoothLeAudio.CONTEXT_TYPE_MEDIA;
        injectAudioConfChanged(testGroupId, contexts, 1);

        // Set group and device as active.
        injectGroupStatusChange(testGroupId, LeAudioStackEvent.GROUP_STATUS_ACTIVE);

        verify(mAudioManager, times(1))
                .handleBluetoothActiveDeviceChanged(
                        eq(mSingleDevice), any(), any(BluetoothProfileConnectionInfo.class));

        doReturn(true)
                .when(mBassClientService)
                .isAnyReceiverReceivingBroadcast(mService.getGroupDevices(testGroupId));
        LeAudioStackEvent healthBasedGroupAction =
                new LeAudioStackEvent(
                        LeAudioStackEvent.EVENT_TYPE_HEALTH_BASED_GROUP_RECOMMENDATION);
        healthBasedGroupAction.valueInt1 = testGroupId;
        healthBasedGroupAction.valueInt2 =
                LeAudioStackEvent.HEALTH_RECOMMENDATION_ACTION_INACTIVATE_GROUP;
        mService.messageFromNative(healthBasedGroupAction);
        // Verify skip setting device inactive if group is receiving broadcast
        verify(mAudioManager, times(0))
                .handleBluetoothActiveDeviceChanged(
                        eq(null), any(), any(BluetoothProfileConnectionInfo.class));

        doReturn(false)
                .when(mBassClientService)
                .isAnyReceiverReceivingBroadcast(mService.getGroupDevices(testGroupId));
        mService.messageFromNative(healthBasedGroupAction);
        // Verify setting device inactive if group is not receiving broadcast
        verify(mAudioManager, times(1))
                .handleBluetoothActiveDeviceChanged(
                        eq(null), any(), any(BluetoothProfileConnectionInfo.class));
    }

    private void sendEventAndVerifyIntentForGroupStatusChanged(int groupId, int groupStatus) {

        onGroupStatusCallbackCalled = false;