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

Commit cc1723f4 authored by Michal Belusiak's avatar Michal Belusiak Committed by Automerger Merge Worker
Browse files

Merge "Broadcaster: BIG creation/termination depends on audio resume/pause"...

Merge "Broadcaster: BIG creation/termination depends on audio resume/pause" into main am: b9353cb1 am: f3b3ee4b

Original change: https://android-review.googlesource.com/c/platform/packages/modules/Bluetooth/+/3121871



Change-Id: I744ec48e4c13f527c9c85d7a40ffba35654d4cb4
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents b6fe5a6f f3b3ee4b
Loading
Loading
Loading
Loading
+45 −17
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import static android.bluetooth.IBluetoothLeAudio.LE_AUDIO_GROUP_ID_INVALID;

import static com.android.bluetooth.Utils.enforceBluetoothPrivilegedPermission;
import static com.android.bluetooth.flags.Flags.leaudioAllowedContextMask;
import static com.android.bluetooth.flags.Flags.leaudioBigDependsOnAudioState;
import static com.android.bluetooth.flags.Flags.leaudioBroadcastAssistantPeripheralEntrustment;
import static com.android.bluetooth.flags.Flags.leaudioBroadcastAudioHandoverPolicies;
import static com.android.bluetooth.flags.Flags.leaudioBroadcastExtractPeriodicScannerFromStateMachine;
@@ -2424,6 +2425,20 @@ public class BassClientService extends ProfileService {
        if (leaudioBroadcastAssistantPeripheralEntrustment()) {
            if (isLocalBroadcast(sourceMetadata)) {
                LeAudioService leAudioService = mServiceFactory.getLeAudioService();
                if (leaudioBigDependsOnAudioState()) {
                    if (leAudioService == null
                            || !(leAudioService.isPaused(sourceMetadata.getBroadcastId())
                                    || leAudioService.isPlaying(sourceMetadata.getBroadcastId()))) {
                        Log.w(TAG, "addSource: Local source can't be add");

                        mCallbacks.notifySourceAddFailed(
                                sink,
                                sourceMetadata,
                                BluetoothStatusCodes.ERROR_LOCAL_NOT_ENOUGH_RESOURCES);

                        return;
                    }
                } else {
                    if (leAudioService == null
                            || !leAudioService.isPlaying(sourceMetadata.getBroadcastId())) {
                        Log.w(TAG, "addSource: Local source can't be add");
@@ -2436,6 +2451,7 @@ public class BassClientService extends ProfileService {
                        return;
                    }
                }
            }
        } else {
            if (!isAllowedToAddSource()) {
                Log.d(TAG, "Add source to pending list");
@@ -2963,17 +2979,29 @@ public class BassClientService extends ProfileService {
            return;
        }

        for (Map.Entry<Integer, HashSet<BluetoothDevice>> entry :
                mLocalBroadcastReceivers.entrySet()) {
        Iterator<Map.Entry<Integer, HashSet<BluetoothDevice>>> iterator =
                mLocalBroadcastReceivers.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<Integer, HashSet<BluetoothDevice>> entry = iterator.next();
            Integer broadcastId = entry.getKey();
            HashSet<BluetoothDevice> devices = entry.getValue();

            if (leaudioBigDependsOnAudioState()) {
                /* If somehow there is a non configured/playing broadcast, let's remove it */
                if (!(leAudioService.isPaused(broadcastId)
                        || leAudioService.isPlaying(broadcastId))) {
                    Log.w(TAG, "Non playing broadcast remove from receivers list");
                    iterator.remove();
                    continue;
                }
            } else {
                /* If somehow there is a non playing broadcast, let's remove it */
                if (!leAudioService.isPlaying(broadcastId)) {
                    Log.w(TAG, "Non playing broadcast remove from receivers list");
                mLocalBroadcastReceivers.remove(broadcastId);
                    iterator.remove();
                    continue;
                }
            }

            if (isIntentional) {
                /* Check if disconnecting device participated in this broadcast reception */
@@ -2998,7 +3026,7 @@ public class BassClientService extends ProfileService {
                                + "(broadcast ID: "
                                + broadcastId
                                + ") receivers - stopping broadcast");
                mLocalBroadcastReceivers.remove(broadcastId);
                iterator.remove();
                leAudioService.stopBroadcast(broadcastId);
            } else {
                /* Unintentional disconnection of primary device in private broadcast mode */
@@ -3010,7 +3038,7 @@ public class BassClientService extends ProfileService {
                                                        && (getConnectionState(d)
                                                                == BluetoothProfile
                                                                        .STATE_CONNECTED))) {
                    mLocalBroadcastReceivers.remove(broadcastId);
                    iterator.remove();
                    leAudioService.stopBroadcast(broadcastId);
                    continue;
                }
+52 −15
Original line number Diff line number Diff line
@@ -23,6 +23,8 @@ import static android.bluetooth.IBluetoothLeAudio.LE_AUDIO_GROUP_ID_INVALID;
import static com.android.bluetooth.Utils.enforceBluetoothPrivilegedPermission;
import static com.android.bluetooth.flags.Flags.leaudioAllowedContextMask;
import static com.android.bluetooth.flags.Flags.leaudioApiSynchronizedBlockFix;
import static com.android.bluetooth.flags.Flags.leaudioBigDependsOnAudioState;
import static com.android.bluetooth.flags.Flags.leaudioBroadcastAssistantPeripheralEntrustment;
import static com.android.bluetooth.flags.Flags.leaudioBroadcastFeatureSupport;
import static com.android.bluetooth.flags.Flags.leaudioUseAudioModeListener;
import static com.android.modules.utils.build.SdkLevel.isAtLeastU;
@@ -196,6 +198,7 @@ public class LeAudioService extends ProfileService {
    private final LinkedList<BluetoothLeBroadcastSettings> mCreateBroadcastQueue =
            new LinkedList<>();
    boolean mIsSourceStreamMonitorModeEnabled = false;
    boolean mIsBroadcastPausedFromOutside = false;

    @VisibleForTesting TbsService mTbsService;

@@ -530,6 +533,7 @@ public class LeAudioService extends ProfileService {
        mCreateBroadcastQueue.clear();
        mAwaitingBroadcastCreateResponse = false;
        mIsSourceStreamMonitorModeEnabled = false;
        mIsBroadcastPausedFromOutside = false;

        clearBroadcastTimeoutCallback();

@@ -1262,7 +1266,18 @@ public class LeAudioService extends ProfileService {
            return;
        }

        if (Flags.leaudioBroadcastAssistantPeripheralEntrustment()) {
        if (leaudioBigDependsOnAudioState()) {
            if (isPlaying(broadcastId)) {
                Log.d(TAG, "pauseBroadcast");
                mIsBroadcastPausedFromOutside = true;
                mLeAudioBroadcasterNativeInterface.pauseBroadcast(broadcastId);
            } else if (isPaused(broadcastId)) {
                transitionFromBroadcastToUnicast();
            } else {
                Log.d(TAG, "pauseBroadcast: Broadcast is stopped, skip pause request");
            }
        } else {
            if (leaudioBroadcastAssistantPeripheralEntrustment()) {
                if (!isPlaying(broadcastId)) {
                    Log.d(TAG, "pauseBroadcast: Broadcast is not playing, skip pause request");
                    return;
@@ -1278,6 +1293,7 @@ public class LeAudioService extends ProfileService {
            Log.d(TAG, "pauseBroadcast");
            mLeAudioBroadcasterNativeInterface.pauseBroadcast(broadcastId);
        }
    }

    /**
     * Stop LeAudio Broadcast instance.
@@ -1345,7 +1361,23 @@ public class LeAudioService extends ProfileService {
            return false;
        }

        return descriptor.mState.equals(LeAudioStackEvent.BROADCAST_STATE_STREAMING);
        return (descriptor.mState.equals(LeAudioStackEvent.BROADCAST_STATE_STREAMING));
    }

    /**
     * Checks if Broadcast instance is paused.
     *
     * @param broadcastId broadcast instance identifier
     * @return true if if broadcast is paused, false otherwise
     */
    public boolean isPaused(int broadcastId) {
        LeAudioBroadcastDescriptor descriptor = mBroadcastDescriptors.get(broadcastId);
        if (descriptor == null) {
            Log.e(TAG, "isPaused: No valid descriptor for broadcastId: " + broadcastId);
            return false;
        }

        return (descriptor.mState.equals(LeAudioStackEvent.BROADCAST_STATE_PAUSED));
    }

    /**
@@ -3535,13 +3567,18 @@ public class LeAudioService extends ProfileService {
                                            broadcastId,
                                            BluetoothStatusCodes.REASON_LOCAL_STACK_REQUEST));

                    if (!Flags.leaudioBroadcastAssistantPeripheralEntrustment()) {
                    if (bassClientService != null) {
                        if (!leaudioBroadcastAssistantPeripheralEntrustment()) {
                            bassClientService.suspendReceiversSourceSynchronization(broadcastId);
                        } else if (leaudioBigDependsOnAudioState()) {
                            bassClientService.cacheSuspendingSources(broadcastId);
                        }
                    }

                    if (!leaudioBigDependsOnAudioState() || mIsBroadcastPausedFromOutside) {
                        mIsBroadcastPausedFromOutside = false;
                        transitionFromBroadcastToUnicast();
                    }
                    break;
                case LeAudioStackEvent.BROADCAST_STATE_STOPPING:
                    Log.d(TAG, "Broadcast broadcastId: " + broadcastId + " stopping.");
+100 −60
Original line number Diff line number Diff line
@@ -3823,6 +3823,105 @@ public class BassClientServiceTest {
        }
    }

    @Test
    public void testLocalAddSourceWhenBroadcastIsPlaying() {
        mSetFlagsRule.enableFlags(Flags.FLAG_LEAUDIO_BROADCAST_ASSISTANT_PERIPHERAL_ENTRUSTMENT);
        doReturn(true).when(mLeAudioService).isPlaying(TEST_BROADCAST_ID);
        if (Flags.leaudioBigDependsOnAudioState()) {
            doReturn(false).when(mLeAudioService).isPaused(TEST_BROADCAST_ID);
        }

        prepareTwoSynchronizedDevicesForLocalBroadcast();
    }

    @Test
    public void testLocalAddSourceWhenBroadcastIsPaused() {
        mSetFlagsRule.enableFlags(Flags.FLAG_LEAUDIO_BROADCAST_ASSISTANT_PERIPHERAL_ENTRUSTMENT);
        mSetFlagsRule.enableFlags(Flags.FLAG_LEAUDIO_BIG_DEPENDS_ON_AUDIO_STATE);
        doReturn(false).when(mLeAudioService).isPlaying(TEST_BROADCAST_ID);
        doReturn(true).when(mLeAudioService).isPaused(TEST_BROADCAST_ID);

        prepareTwoSynchronizedDevicesForLocalBroadcast();
    }

    @Test
    public void testLocalAddSourceWhenBroadcastIsStopped() {
        mSetFlagsRule.enableFlags(Flags.FLAG_LEAUDIO_BROADCAST_ASSISTANT_PERIPHERAL_ENTRUSTMENT);
        doReturn(false).when(mLeAudioService).isPlaying(TEST_BROADCAST_ID);
        if (Flags.leaudioBigDependsOnAudioState()) {
            doReturn(false).when(mLeAudioService).isPaused(TEST_BROADCAST_ID);
        }

        BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);

        doReturn(new ArrayList<BluetoothLeBroadcastMetadata>(Arrays.asList(meta)))
                .when(mLeAudioService)
                .getAllBroadcastMetadata();
        prepareConnectedDeviceGroup();
        mBassClientService.addSource(mCurrentDevice, meta, true);
        try {
            verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce())
                    .onSourceAddFailed(
                            eq(mCurrentDevice),
                            eq(meta),
                            eq(BluetoothStatusCodes.ERROR_LOCAL_NOT_ENOUGH_RESOURCES));
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    @Test
    public void testSinksDisconnectionWhenBroadcastIsPlaying() {
        /* Imitate broadcast being active */
        doReturn(true).when(mLeAudioService).isPlaying(TEST_BROADCAST_ID);
        if (Flags.leaudioBigDependsOnAudioState()) {
            doReturn(false).when(mLeAudioService).isPaused(TEST_BROADCAST_ID);
        }

        prepareTwoSynchronizedDevicesForLocalBroadcast();

        /* Imitiate scenario when if there would be broadcast - stop would be called */
        mBassClientService.handleDeviceDisconnection(mCurrentDevice, true);
        mBassClientService.handleDeviceDisconnection(mCurrentDevice1, true);

        verify(mLeAudioService, times(1)).stopBroadcast(eq(TEST_BROADCAST_ID));
    }

    @Test
    public void testSinksDisconnectionWhenBroadcastIsPaused() {
        mSetFlagsRule.enableFlags(Flags.FLAG_LEAUDIO_BIG_DEPENDS_ON_AUDIO_STATE);
        /* Imitate broadcast being active */
        doReturn(false).when(mLeAudioService).isPlaying(TEST_BROADCAST_ID);
        doReturn(true).when(mLeAudioService).isPaused(TEST_BROADCAST_ID);

        prepareTwoSynchronizedDevicesForLocalBroadcast();

        /* Imitiate scenario when if there would be broadcast - stop would be called */
        mBassClientService.handleDeviceDisconnection(mCurrentDevice, true);
        mBassClientService.handleDeviceDisconnection(mCurrentDevice1, true);

        verify(mLeAudioService, times(1)).stopBroadcast(eq(TEST_BROADCAST_ID));
    }

    @Test
    public void testSinksDisconnectionWhenBroadcastIsStopped() {
        /* Imitate broadcast being active */
        doReturn(true).when(mLeAudioService).isPlaying(TEST_BROADCAST_ID);
        if (Flags.leaudioBigDependsOnAudioState()) {
            doReturn(false).when(mLeAudioService).isPaused(TEST_BROADCAST_ID);
        }

        prepareTwoSynchronizedDevicesForLocalBroadcast();

        doReturn(false).when(mLeAudioService).isPlaying(TEST_BROADCAST_ID);

        /* Imitiate scenario when if there would be broadcast - stop would be called */
        mBassClientService.handleDeviceDisconnection(mCurrentDevice, true);
        mBassClientService.handleDeviceDisconnection(mCurrentDevice1, true);

        verify(mLeAudioService, never()).stopBroadcast(eq(TEST_BROADCAST_ID));
    }

    @Test
    public void testPrivateBroadcastIntentionalDisconnection() {
        /* Imitate broadcast being active */
@@ -3944,61 +4043,7 @@ public class BassClientServiceTest {
        /* Imitate broadcast being active */
        doReturn(true).when(mLeAudioService).isPlaying(TEST_BROADCAST_ID);

        BluetoothLeBroadcastMetadata meta = createBroadcastMetadata(TEST_BROADCAST_ID);

        doReturn(new ArrayList<BluetoothLeBroadcastMetadata>(Arrays.asList(meta)))
                .when(mLeAudioService)
                .getAllBroadcastMetadata();
        prepareConnectedDeviceGroup();
        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);
                // verify source id
                try {
                    verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce())
                            .onSourceAdded(
                                    eq(mCurrentDevice),
                                    eq(TEST_SOURCE_ID),
                                    eq(BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST));
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            } 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);
                // verify source id
                try {
                    verify(mCallback, timeout(TIMEOUT_MS).atLeastOnce())
                            .onSourceAdded(
                                    eq(mCurrentDevice1),
                                    eq(TEST_SOURCE_ID + 1),
                                    eq(BluetoothStatusCodes.REASON_LOCAL_APP_REQUEST));
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
        }

        /* Imitate broadcast being not active */
        doReturn(false).when(mLeAudioService).isPlaying(TEST_BROADCAST_ID);
        prepareTwoSynchronizedDevicesForLocalBroadcast();

        mBassClientService.notifyBroadcastStateChanged(
                0 /* BROADCAST_STATE_STOPPED */, TEST_BROADCAST_ID);
@@ -4007,11 +4052,6 @@ public class BassClientServiceTest {
        mBassClientService.handleDeviceDisconnection(mCurrentDevice, true);
        mBassClientService.handleDeviceDisconnection(mCurrentDevice1, true);

        /* Imitate first device being in disconnected state */
        doReturn(BluetoothProfile.STATE_DISCONNECTED)
                .when(mStateMachines.get(mCurrentDevice))
                .getConnectionState();

        /* After second device disconnection and de-synchronization expect not calling broadcast to
         * stop due to previous broadcast stream stopped */
        verify(mLeAudioService, never()).stopBroadcast(eq(TEST_BROADCAST_ID));
+237 −0

File changed.

Preview size limit exceeded, changes collapsed.

+39 −24
Original line number Diff line number Diff line
@@ -708,8 +708,12 @@ public:
    log::info("broadcast_id={}", broadcast_id);

    if (broadcasts_.count(broadcast_id) != 0) {
      if (!com::android::bluetooth::flags::leaudio_big_depends_on_audio_state()) {
        log::info("Stopping AudioHalClient");
      if (le_audio_source_hal_client_) le_audio_source_hal_client_->Stop();
        if (le_audio_source_hal_client_) {
          le_audio_source_hal_client_->Stop();
        }
      }
      audio_state_ = AudioState::SUSPENDED;
      broadcasts_[broadcast_id]->SetMuted(true);
      broadcasts_[broadcast_id]->ProcessMessage(
@@ -927,11 +931,12 @@ public:
                         "assert failed: broadcasts_.count(broadcast_id) != 0");
        broadcasts_[broadcast_id]->HandleHciEvent(HCI_BLE_TERM_BIG_CPL_EVT,
                                                  evt);
        auto result =
            CodecManager::GetInstance()->UpdateActiveBroadcastAudioHalClient(
        if (!com::android::bluetooth::flags::leaudio_big_depends_on_audio_state()) {
          auto result = CodecManager::GetInstance()->UpdateActiveBroadcastAudioHalClient(
                  le_audio_source_hal_client_.get(), false);
          log::assert_that(result, "Could not update session in codec manager");
          le_audio_source_hal_client_.reset();
        }
      } break;
      default:
        log::error("Invalid event={}", event);
@@ -1052,13 +1057,13 @@ public:
              broadcast->SetMuted(false);
              auto is_started = instance->le_audio_source_hal_client_->Start(
                  broadcast_config.GetAudioHalClientConfig(), &audio_receiver_);
              if (!com::android::bluetooth::flags::leaudio_big_depends_on_audio_state()) {
                if (!is_started) {
                  /* Audio Source setup failed - stop the broadcast */
                  instance->StopAudioBroadcast(broadcast_id);
                  return;
                }

              if (com::android::bluetooth::flags::leaudio_big_depends_on_audio_state()) {
              } else {
                instance->UpdateAudioActiveStateInPublicAnnouncement();
              }
            }
@@ -1324,15 +1329,20 @@ public:

    virtual void OnAudioSuspend(void) override {
      log::info("");
      /* TODO: Should we suspend all broadcasts - remove BIGs? */
      if (!instance) {
        return;
      }

      instance->audio_state_ = AudioState::SUSPENDED;

      if (com::android::bluetooth::flags::leaudio_big_depends_on_audio_state()) {
        instance->UpdateAudioActiveStateInPublicAnnouncement();

        // TODO: add some timeout to execute below
        for (auto& broadcast_pair : instance->broadcasts_) {
          auto& broadcast = broadcast_pair.second;
          broadcast->SetMuted(true);
          broadcast->ProcessMessage(BroadcastStateMachine::Message::SUSPEND, nullptr);
        }
      }
    }

@@ -1340,18 +1350,23 @@ public:
      log::info("");
      if (!instance) return;

      /* TODO: Should we resume all broadcasts - recreate BIGs? */
      instance->audio_state_ = AudioState::ACTIVE;
      if (com::android::bluetooth::flags::leaudio_big_depends_on_audio_state()) {
        instance->UpdateAudioActiveStateInPublicAnnouncement();

        for (auto& broadcast_pair : instance->broadcasts_) {
          auto& broadcast = broadcast_pair.second;
          broadcast->ProcessMessage(BroadcastStateMachine::Message::START, nullptr);
        }

        instance->le_audio_source_hal_client_->ConfirmStreamingRequest();
      } else {
        if (!IsAnyoneStreaming()) {
          instance->le_audio_source_hal_client_->CancelStreamingRequest();
          return;
        }

        instance->le_audio_source_hal_client_->ConfirmStreamingRequest();

      if (com::android::bluetooth::flags::leaudio_big_depends_on_audio_state()) {
        instance->UpdateAudioActiveStateInPublicAnnouncement();
      }
    }

Loading