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

Commit 312bc33e authored by Łukasz Rymanowski (xWF)'s avatar Łukasz Rymanowski (xWF) Committed by Gerrit Code Review
Browse files

Merge changes Iaedd0059,I66c6316d,I5c7bae06,If3001b71 into main

* changes:
  leaudio: Improve LeAudio switch during phone call
  leaudio: Improve sending QoSConfigure when CIG is already created
  leaudio: Move QoS Configured state to right place
  leaudio: Minor cleanup
parents 0af8835b 4a6cbce6
Loading
Loading
Loading
Loading
+36 −0
Original line number Diff line number Diff line
@@ -1302,6 +1302,35 @@ public:
    active_group_id_ = bluetooth::groups::kGroupUnknown;
  }

  void PrepareStreamForAConversational(LeAudioDeviceGroup* group) {
    if (!com::android::bluetooth::flags::leaudio_improve_switch_during_phone_call()) {
      log::info("Flag leaudio_improve_switch_during_phone_call is not enabled");
      return;
    }

    log::debug("group_id: {}", group->group_id_);

    auto remote_direction = bluetooth::le_audio::types::kLeAudioDirectionSink;
    ReconfigureOrUpdateRemote(group, remote_direction);

    if (configuration_context_type_ != LeAudioContextType::CONVERSATIONAL) {
      log::error("Something went wrong {} != {} ", ToString(configuration_context_type_),
                 ToString(LeAudioContextType::CONVERSATIONAL));
      return;
    }

    BidirectionalPair<std::vector<uint8_t>> ccids = {
            .sink = ContentControlIdKeeper::GetInstance()->GetAllCcids(
                    local_metadata_context_types_.sink),
            .source = ContentControlIdKeeper::GetInstance()->GetAllCcids(
                    local_metadata_context_types_.source)};
    if (!groupStateMachine_->ConfigureStream(group, configuration_context_type_,
                                             local_metadata_context_types_, ccids, true)) {
      log::info("Reconfiguration is needed for group {}", group->group_id_);
      initReconfiguration(group, LeAudioContextType::UNSPECIFIED);
    }
  }

  void GroupSetActive(const int group_id) override {
    log::info("group_id: {}", group_id);

@@ -1418,6 +1447,13 @@ public:
      callbacks_->OnGroupStatus(active_group_id_, GroupStatus::ACTIVE);
      SendAudioGroupSelectableCodecConfigChanged(group);
    }

    /* If group become active while phone call, let's configure it right away,
     * so when audio framework resumes the stream, it will be almost there.
     */
    if (IsInCall()) {
      PrepareStreamForAConversational(group);
    }
  }

  void SetEnableState(const RawAddress& address, bool enabled) override {
+2 −2
Original line number Diff line number Diff line
@@ -249,7 +249,7 @@ public:

  inline types::AseState GetState(void) const { return current_state_; }
  void SetState(types::AseState state) {
    log::info("current state: {}, new state {}, in_transition_ {}",
    log::info("group_id: {} current state: {}, new state {}, in_transition_ {}", group_id_,
              bluetooth::common::ToString(current_state_), bluetooth::common::ToString(state),
              in_transition_);
    LeAudioLogHistory::Get()->AddLogHistory(kLogStateMachineTag, group_id_, RawAddress::kEmpty,
@@ -272,7 +272,7 @@ public:
    return notify_streaming_when_cises_are_ready_;
  }
  void SetTargetState(types::AseState state) {
    log::info("target state: {}, new target state: {}, in_transition_ {}",
    log::info("group_id: {} target state: {}, new target state: {}, in_transition_ {}", group_id_,
              bluetooth::common::ToString(target_state_), bluetooth::common::ToString(state),
              in_transition_);
    LeAudioLogHistory::Get()->AddLogHistory(
+127 −13
Original line number Diff line number Diff line
@@ -851,15 +851,18 @@ protected:
    ON_CALL(mock_state_machine_, Initialize(_))
            .WillByDefault(SaveArg<0>(&state_machine_callbacks_));
    ON_CALL(mock_state_machine_, ConfigureStream(_, _, _, _))
    ON_CALL(mock_state_machine_, ConfigureStream(_, _, _, _, _))
            .WillByDefault([this](LeAudioDeviceGroup* group, types::LeAudioContextType context_type,
                                  types::BidirectionalPair<types::AudioContexts>
                                          metadata_context_types,
                                  types::BidirectionalPair<std::vector<uint8_t>> ccid_lists) {
                                  types::BidirectionalPair<std::vector<uint8_t>> ccid_lists,
                                  bool configure_qos) {
              bool isReconfiguration = group->IsPendingConfiguration();
              log::info("ConfigureStream: group_id {}, context_type {} isReconfiguration {}",
                        group->group_id_, bluetooth::common::ToString(context_type),
              log::info(
                      "ConfigureStream: group_id {}, context_type {}, configure_qos {}, "
                      "isReconfiguration {}",
                      group->group_id_, bluetooth::common::ToString(context_type), configure_qos,
                      isReconfiguration);
              /* Do what ReleaseCisIds(group) does: start */
@@ -885,6 +888,12 @@ protected:
              types::AseState config_state =
                      types::AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED;
              if (configure_qos) {
                // Make sure CIG is created
                config_state = types::AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED;
                group->cig.SetState(types::CigState::CREATED);
              }
              for (LeAudioDevice* device = group->GetFirstDevice(); device != nullptr;
                   device = group->GetNextDevice(device)) {
                if (!group->cig.AssignCisIds(device)) {
@@ -4855,6 +4864,111 @@ TEST_F(UnicastTest, GroupSetActive_and_InactiveDuringStreamConfiguration) {
  Mock::VerifyAndClearExpectations(&mock_state_machine_);
}
TEST_F(UnicastTest, GroupSetActive_and_GroupSetInactive_DuringPhoneCall) {
  com::android::bluetooth::flags::provider_->leaudio_improve_switch_during_phone_call(true);
  const RawAddress test_address0 = GetTestAddress(0);
  int group_id = bluetooth::groups::kGroupUnknown;
  /**
   * Scenario:
   * 1. Call is started
   * 2. Group is set active - it is expected the state machine to be instructed to Configure to Qos
   * 3. Group is set to inactive - it is expected that state machine is instructed to stop *
   */
  default_channel_cnt = 1;
  SetSampleDatabaseEarbudsValid(
          1, test_address0, codec_spec_conf::kLeAudioLocationStereo,
          codec_spec_conf::kLeAudioLocationStereo, default_channel_cnt, default_channel_cnt, 0x0004,
          /* source sample freq 16khz */ false /*add_csis*/, true /*add_cas*/, true /*add_pacs*/,
          default_ase_cnt /*add_ascs_cnt*/, 1 /*set_size*/, 0 /*rank*/);
  EXPECT_CALL(mock_audio_hal_client_callbacks_,
              OnConnectionState(ConnectionState::CONNECTED, test_address0))
          .Times(1);
  EXPECT_CALL(mock_audio_hal_client_callbacks_,
              OnGroupNodeStatus(test_address0, _, GroupNodeStatus::ADDED))
          .WillOnce(DoAll(SaveArg<1>(&group_id)));
  ConnectLeAudio(test_address0);
  ASSERT_NE(group_id, bluetooth::groups::kGroupUnknown);
  EXPECT_CALL(mock_state_machine_, StartStream(_, _, _, _)).Times(0);
  EXPECT_CALL(mock_state_machine_, ConfigureStream(_, _, _, _, true)).Times(1);
  log::info("Call is started and group is getting Active");
  LeAudioClient::Get()->SetInCall(true);
  LeAudioClient::Get()->GroupSetActive(group_id);
  SyncOnMainLoop();
  Mock::VerifyAndClearExpectations(&mock_state_machine_);
  log::info("Group is getting inctive");
  EXPECT_CALL(mock_state_machine_, StopStream(_)).Times(1);
  LeAudioClient::Get()->GroupSetActive(bluetooth::groups::kGroupUnknown);
  SyncOnMainLoop();
  Mock::VerifyAndClearExpectations(&mock_state_machine_);
}
TEST_F(UnicastTest, GroupSetActive_DuringPhoneCall_ThenResume) {
  com::android::bluetooth::flags::provider_->leaudio_improve_switch_during_phone_call(true);
  const RawAddress test_address0 = GetTestAddress(0);
  int group_id = bluetooth::groups::kGroupUnknown;
  /**
   * Scenario:
   * 1. Call is started
   * 2. Group is set active - it is expected the state machine to be instructed to Configure to Qos
   * 3. Audio Framework callse Resume - expect stream is started.
   * 4. Group is set to inactive - it is expected that state machine is instructed to stop *
   */
  default_channel_cnt = 1;
  SetSampleDatabaseEarbudsValid(
          1, test_address0, codec_spec_conf::kLeAudioLocationStereo,
          codec_spec_conf::kLeAudioLocationStereo, default_channel_cnt, default_channel_cnt, 0x0004,
          /* source sample freq 16khz */ false /*add_csis*/, true /*add_cas*/, true /*add_pacs*/,
          default_ase_cnt /*add_ascs_cnt*/, 1 /*set_size*/, 0 /*rank*/);
  EXPECT_CALL(mock_audio_hal_client_callbacks_,
              OnConnectionState(ConnectionState::CONNECTED, test_address0))
          .Times(1);
  EXPECT_CALL(mock_audio_hal_client_callbacks_,
              OnGroupNodeStatus(test_address0, _, GroupNodeStatus::ADDED))
          .WillOnce(DoAll(SaveArg<1>(&group_id)));
  ConnectLeAudio(test_address0);
  ASSERT_NE(group_id, bluetooth::groups::kGroupUnknown);
  EXPECT_CALL(mock_state_machine_, StartStream(_, _, _, _)).Times(0);
  EXPECT_CALL(mock_state_machine_, ConfigureStream(_, _, _, _, true)).Times(1);
  log::info("Call is started and group is getting Active");
  LeAudioClient::Get()->SetInCall(true);
  LeAudioClient::Get()->GroupSetActive(group_id);
  SyncOnMainLoop();
  Mock::VerifyAndClearExpectations(&mock_state_machine_);
  log::info("AF resumes the stream");
  /* Simulate resume and expect StartStream to be called.
   * Do not expect confirmation on resume, as this part is not mocked on the state machine
   */
  EXPECT_CALL(mock_state_machine_, StartStream(_, LeAudioContextType::CONVERSATIONAL, _, _))
          .Times(1);
  LocalAudioSourceResume(true, false);
  SyncOnMainLoop();
  Mock::VerifyAndClearExpectations(&mock_state_machine_);
  log::info("Group is getting inactive");
  EXPECT_CALL(mock_state_machine_, StopStream(_)).Times(1);
  LeAudioClient::Get()->GroupSetActive(bluetooth::groups::kGroupUnknown);
  SyncOnMainLoop();
  Mock::VerifyAndClearExpectations(&mock_state_machine_);
}
TEST_F(UnicastTest, ChangeAvailableContextTypeWhenInCodecConfigured) {
  const RawAddress test_address0 = GetTestAddress(0);
  int group_id = bluetooth::groups::kGroupUnknown;
@@ -8133,7 +8247,7 @@ TEST_F(UnicastTest, TwoEarbudsStreamingContextSwitchReconfigure) {
          .source = types::AudioContexts()};
  EXPECT_CALL(mock_state_machine_,
              ConfigureStream(_, bluetooth::le_audio::types::LeAudioContextType::MEDIA, contexts,
                              ccids))
                              ccids, _))
          .Times(1);
  StartStreaming(AUDIO_USAGE_MEDIA, AUDIO_CONTENT_TYPE_MUSIC, group_id, AUDIO_SOURCE_INVALID, true);
@@ -8202,7 +8316,7 @@ TEST_F(UnicastTest, TwoEarbudsStreamingContextSwitchReconfigure_SpeedUpReconfigF
  EXPECT_CALL(*mock_le_audio_source_hal_client_, ReconfigurationComplete())
          .Times(1)
          .After(reconfigure);
  EXPECT_CALL(mock_state_machine_, ConfigureStream(_, _, _, _)).Times(1);
  EXPECT_CALL(mock_state_machine_, ConfigureStream(_, _, _, _, _)).Times(1);
  // SetInCall is used by GTBS - and only then we can expect CCID to be set.
  LeAudioClient::Get()->SetInCall(true);
  SyncOnMainLoop();
@@ -8237,7 +8351,7 @@ TEST_F(UnicastTest, TwoEarbudsStreamingContextSwitchReconfigure_SpeedUpReconfigF
  EXPECT_CALL(*mock_le_audio_source_hal_client_, ReconfigurationComplete())
          .Times(1)
          .After(reconfigure);
  EXPECT_CALL(mock_state_machine_, ConfigureStream(_, _, _, _)).Times(1);
  EXPECT_CALL(mock_state_machine_, ConfigureStream(_, _, _, _, _)).Times(1);
  LeAudioClient::Get()->SetInCall(false);
  SyncOnMainLoop();
@@ -8253,7 +8367,7 @@ TEST_F(UnicastTest, TwoEarbudsStreamingContextSwitchReconfigure_SpeedUpReconfigF
          .source = types::AudioContexts()};
  EXPECT_CALL(mock_state_machine_,
              ConfigureStream(_, bluetooth::le_audio::types::LeAudioContextType::MEDIA, contexts,
                              ccids))
                              ccids, _))
          .Times(0);
  EXPECT_CALL(
          mock_state_machine_,
@@ -8405,7 +8519,7 @@ TEST_F(UnicastTest, TwoReconfigureAndVerifyEnableContextType) {
          .source = types::AudioContexts(types::LeAudioContextType::CONVERSATIONAL)};
  EXPECT_CALL(mock_state_machine_,
              ConfigureStream(_, types::LeAudioContextType::CONVERSATIONAL, _, _))
              ConfigureStream(_, types::LeAudioContextType::CONVERSATIONAL, _, _, _))
          .Times(AtLeast(1));
  // Update metadata and resume
@@ -9743,7 +9857,7 @@ TEST_F(UnicastTest, MicrophoneAttachToCurrentMediaScenario) {
  // When the local audio sink resumes we should reconfigure
  EXPECT_CALL(mock_state_machine_,
              ConfigureStream(_, bluetooth::le_audio::types::LeAudioContextType::LIVE, _, _))
              ConfigureStream(_, bluetooth::le_audio::types::LeAudioContextType::LIVE, _, _, _))
          .Times(1);
  EXPECT_CALL(*mock_le_audio_source_hal_client_, ReconfigurationComplete()).Times(1);
@@ -10156,7 +10270,7 @@ TEST_F(UnicastTest, UpdateMultipleBidirContextTypes_SpeedUpReconfigFlagEnabled)
  EXPECT_CALL(*mock_le_audio_source_hal_client_, SuspendedForReconfiguration()).Times(0);
  EXPECT_CALL(*mock_le_audio_source_hal_client_, CancelStreamingRequest()).Times(0);
  EXPECT_CALL(*mock_le_audio_source_hal_client_, ReconfigurationComplete()).Times(0);
  EXPECT_CALL(mock_state_machine_, ConfigureStream(_, _, _, _)).Times(0);
  EXPECT_CALL(mock_state_machine_, ConfigureStream(_, _, _, _, _)).Times(0);
  EXPECT_CALL(mock_state_machine_, StopStream(_)).Times(0);
  LeAudioClient::Get()->SetInCall(true);
  SyncOnMainLoop();
@@ -10191,7 +10305,7 @@ TEST_F(UnicastTest, UpdateMultipleBidirContextTypes_SpeedUpReconfigFlagEnabled)
  EXPECT_CALL(*mock_le_audio_source_hal_client_, SuspendedForReconfiguration()).Times(0);
  EXPECT_CALL(*mock_le_audio_source_hal_client_, CancelStreamingRequest()).Times(0);
  EXPECT_CALL(*mock_le_audio_source_hal_client_, ReconfigurationComplete()).Times(0);
  EXPECT_CALL(mock_state_machine_, ConfigureStream(_, _, _, _)).Times(0);
  EXPECT_CALL(mock_state_machine_, ConfigureStream(_, _, _, _, _)).Times(0);
  EXPECT_CALL(mock_state_machine_, StopStream(_)).Times(0);
  LeAudioClient::Get()->SetInCall(false);
+2 −1
Original line number Diff line number Diff line
@@ -41,7 +41,8 @@ public:
               bluetooth::le_audio::types::LeAudioContextType context_type,
               const bluetooth::le_audio::types::BidirectionalPair<
                       bluetooth::le_audio::types::AudioContexts>& metadata_context_types,
               bluetooth::le_audio::types::BidirectionalPair<std::vector<uint8_t>> ccid_lists),
               bluetooth::le_audio::types::BidirectionalPair<std::vector<uint8_t>> ccid_lists,
               bool configure_qos),
              (override));
  MOCK_METHOD((void), StopStream, (bluetooth::le_audio::LeAudioDeviceGroup * group), (override));
  MOCK_METHOD((void), ProcessGattNotifEvent,
+75 −11
Original line number Diff line number Diff line
@@ -241,7 +241,25 @@ public:
      case AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED: {
        LeAudioDevice* leAudioDevice = group->GetFirstActiveDevice();
        if (!leAudioDevice) {
          log::error("group has no active devices");
          group->PrintDebugState();
          log::error("group_id: {} has no active devices", group->group_id_);
          return false;
        }

        if (!group->IsConfiguredForContext(context_type)) {
          if (group->GetConfigurationContextType() == context_type) {
            log::info(
                    "Looks like another device connected in the meantime to group_id: {}, try to "
                    "reconfigure.",
                    group->group_id_);
            if (group->Configure(context_type, metadata_context_types, ccid_lists)) {
              return PrepareAndSendCodecConfigToTheGroup(group);
            }
          }
          log::error("Trying to start stream not configured for the context {} in group_id: {} ",
                     ToString(context_type), group->group_id_);
          group->PrintDebugState();
          StopStream(group);
          return false;
        }

@@ -283,13 +301,26 @@ public:

  bool ConfigureStream(LeAudioDeviceGroup* group, LeAudioContextType context_type,
                       const BidirectionalPair<AudioContexts>& metadata_context_types,
                       BidirectionalPair<std::vector<uint8_t>> ccid_lists) override {
                       BidirectionalPair<std::vector<uint8_t>> ccid_lists,
                       bool configure_qos) override {
    if (group->GetState() > AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED) {
      log::error("Stream should be stopped or in configured stream. Current state: {}",
                 ToString(group->GetState()));
      return false;
    }

    if (configure_qos) {
      if (group->IsConfiguredForContext(context_type)) {
        if (group->Activate(context_type, metadata_context_types, ccid_lists)) {
          SetTargetState(group, AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED);
          if (CigCreate(group)) {
            return true;
          }
        }
      }
      log::info("Could not activate device, try to configure it again");
    }

    group->Deactivate();
    ReleaseCisIds(group);

@@ -301,7 +332,11 @@ public:
    }

    group->cig.GenerateCisIds(context_type);
    if (configure_qos) {
      SetTargetState(group, AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED);
    } else {
      SetTargetState(group, AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED);
    }
    return PrepareAndSendCodecConfigToTheGroup(group);
  }

@@ -489,7 +524,8 @@ public:
              group->group_id_, ToString(group->cig.GetState()),
              static_cast<int>(conn_handles.size()));

    if (group->GetTargetState() != AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
    if (group->GetTargetState() != AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING &&
        group->GetTargetState() != AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED) {
      /* Group is not going to stream. It happen while CIG was creating.
       * Remove CIG in such a case
       */
@@ -505,9 +541,6 @@ public:
    /* Assign all connection handles to multiple device ASEs */
    group->AssignCisConnHandlesToAses();

    /* Last node configured, process group to codec configured state */
    group->SetState(AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED);

    PrepareAndSendQoSToTheGroup(group);
  }

@@ -1867,6 +1900,7 @@ private:
    }

    std::vector<uint8_t> value;
    log::info("{} -> ", leAudioDevice->address_);
    bluetooth::le_audio::client_parser::ascs::PrepareAseCtpCodecConfig(confs, value);
    WriteToControlPoint(leAudioDevice, value);

@@ -1996,12 +2030,17 @@ private:
        /* Last node configured, process group to codec configured state */
        group->SetState(AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED);

        if (group->GetTargetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
        if (group->GetTargetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING ||
            group->GetTargetState() == AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED) {
          if (group->cig.GetState() == CigState::CREATED) {
            /* It can happen on the earbuds switch scenario. When one device
             * is getting remove while other is adding to the stream and CIG is
             * already created */
            PrepareAndSendConfigQos(group, leAudioDevice);
             * already created.
             * Also if one of the set members got reconnected while the other was in QoSConfigured
             * state. In this case, state machine will keep CIG but will send Codec Config to all
             * the set members and when ASEs will move to Codec Configured State, we would like a
             * whole group to move to QoS Configure.*/
            PrepareAndSendQoSToTheGroup(group);
          } else if (!CigCreate(group)) {
            log::error("Could not create CIG. Stop the stream for group {}", group->group_id_);
            StopStream(group);
@@ -2204,7 +2243,8 @@ private:
      case AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED: {
        SetAseState(leAudioDevice, ase, AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED);

        if (group->GetTargetState() != AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
        if (group->GetTargetState() != AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING &&
            group->GetTargetState() != AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED) {
          log::warn("{}, ase_id: {}, target state: {}", leAudioDevice->address_, ase->id,
                    ToString(group->GetTargetState()));
          group->PrintDebugState();
@@ -2230,6 +2270,14 @@ private:
          return;
        }

        group->SetState(AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED);

        if (group->GetTargetState() == AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED) {
          cancel_watchdog_if_needed(group->group_id_);
          state_machine_callbacks_->StatusReportCb(group->group_id_,
                                                   GroupStreamStatus::CONFIGURED_BY_USER);
          return;
        }
        PrepareAndSendEnableToTheGroup(group);

        break;
@@ -2468,12 +2516,23 @@ private:
    msg_stream << kLogAseQoSConfigOp;

    std::stringstream extra_stream;
    int number_of_active_ases = 0;
    int number_of_streaming_ases = 0;

    for (struct ase* ase = leAudioDevice->GetFirstActiveAse(); ase != nullptr;
         ase = leAudioDevice->GetNextActiveAse(ase)) {
      log::debug("device: {}, ase_id: {}, cis_id: {}, ase state: {}", leAudioDevice->address_,
                 ase->id, ase->cis_id, ToString(ase->state));

      /* QoS Config can be done on ASEs which are in Codec Configured and QoS Configured state.
       * If ASE is streaming, it can be skipped.
       */
      number_of_active_ases++;
      if (ase->state == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
        number_of_streaming_ases++;
        continue;
      }

      /* Fill in the whole group dependent ASE parameters */
      if (!group->GetPresentationDelay(&ase->qos_config.presentation_delay, ase->direction)) {
        log::error("inconsistent presentation delay for group");
@@ -2524,6 +2583,11 @@ private:
                   << +conf.retrans_nb << "," << +conf.phy << "," << +conf.framing << ";;";
    }

    if (number_of_streaming_ases > 0 && number_of_streaming_ases == number_of_active_ases) {
      log::debug("Device {} is already streaming", leAudioDevice->address_);
      return;
    }

    if (confs.size() == 0 || !validate_transport_latency || !validate_max_sdu_size) {
      log::error("Invalid configuration or latency or sdu size");
      group->PrintDebugState();
Loading