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

Commit 2ae07a8a authored by Jakub Tyszkowski's avatar Jakub Tyszkowski
Browse files

LeAudio: Reestablish streams when back in range

When we were disconnecting due to out of range, we
kept our ASEs in the STREAMING state. This prevented
reestablishing the audio stream once reconnected.

Bug: 240240176
Tag: #feature
Test: atest --host bluetooth_le_audio_test bluetooth_le_audio_client_test --no-bazel-mode
Change-Id: If0817ba67dd630f86d4390747b5b1e57ec314152
parent f1650f0c
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -1951,7 +1951,7 @@ int LeAudioDevice::GetAseCount(uint8_t direction) {
  });
}

struct ase* LeAudioDevice::GetFirstInactiveAseWithState(uint8_t direction,
struct ase* LeAudioDevice::GetFirstAseWithState(uint8_t direction,
                                                AseState state) {
  auto iter = std::find_if(
      ases_.begin(), ases_.end(), [direction, state](const auto& ase) {
+2 −2
Original line number Diff line number Diff line
@@ -115,7 +115,7 @@ class LeAudioDevice {
      types::AudioStreamDataPathState state);
  struct types::ase* GetFirstInactiveAse(uint8_t direction,
                                         bool reconnect = false);
  struct types::ase* GetFirstInactiveAseWithState(uint8_t direction,
  struct types::ase* GetFirstAseWithState(uint8_t direction,
                                          types::AseState state);
  struct types::ase* GetNextActiveAse(struct types::ase* ase);
  struct types::ase* GetAseToMatchBidirectionCis(struct types::ase* ase);
+11 −1
Original line number Diff line number Diff line
@@ -756,12 +756,22 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine {

    FreeLinkQualityReports(leAudioDevice);

    auto ases_pair = leAudioDevice->GetAsesByCisConnHdl(event->cis_conn_hdl);

    /* If this is peer disconnecting CIS, make sure to clear data path */
    if (event->reason != HCI_ERR_CONN_CAUSE_LOCAL_HOST) {
      RemoveDataPathByCisHandle(leAudioDevice, event->cis_conn_hdl);
      // Make sure we won't stay in STREAMING state
      if (ases_pair.sink) {
        ases_pair.sink->state =
            AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED;
      }
      if (ases_pair.source) {
        ases_pair.source->state =
            AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED;
      }
    }

    auto ases_pair = leAudioDevice->GetAsesByCisConnHdl(event->cis_conn_hdl);
    if (ases_pair.sink) {
      ases_pair.sink->data_path_state = AudioStreamDataPathState::CIS_ASSIGNED;
    }
+25 −5
Original line number Diff line number Diff line
@@ -385,6 +385,12 @@ class StateMachineTest : public Test {
            return;
          }

          // When we disconnect the remote with HCI_ERR_PEER_USER, we
          // should be getting HCI_ERR_CONN_CAUSE_LOCAL_HOST from HCI.
          if (reason == HCI_ERR_PEER_USER) {
            reason = HCI_ERR_CONN_CAUSE_LOCAL_HOST;
          }

          for (auto& kv_pair : le_audio_device_groups_) {
            auto& group = kv_pair.second;
            if (group->IsDeviceInTheGroup(dev_it->get())) {
@@ -2683,12 +2689,13 @@ TEST_F(StateMachineTest, testStreamConfigurationAdspDownMix) {
}

static void InjectCisDisconnected(LeAudioDeviceGroup* group,
                                  LeAudioDevice* leAudioDevice) {
                                  LeAudioDevice* leAudioDevice,
                                  uint8_t reason) {
  bluetooth::hci::iso_manager::cis_disconnected_evt event;

  auto* ase = leAudioDevice->GetFirstActiveAse();
  while (ase) {
    event.reason = 0x08;
    event.reason = reason;
    event.cig_id = group->group_id_;
    event.cis_conn_hdl = ase->cis_conn_hdl;
    LeAudioGroupStateMachine::Get()->ProcessHciNotifCisDisconnected(
@@ -2738,8 +2745,8 @@ TEST_F(StateMachineTest, testAttachDeviceToTheStream) {
  ASSERT_EQ(expected_devices_written, num_devices);

  EXPECT_CALL(*mock_iso_manager_, CreateCig(_, _)).Times(1);
  EXPECT_CALL(*mock_iso_manager_, EstablishCis(_)).Times(2);
  EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath(_, _)).Times(3);
  EXPECT_CALL(*mock_iso_manager_, EstablishCis(_)).Times(1);
  EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath(_, _)).Times(2);

  InjectInitialIdleNotification(group);

@@ -2751,9 +2758,10 @@ TEST_F(StateMachineTest, testAttachDeviceToTheStream) {
  // Check if group has transitioned to a proper state
  ASSERT_EQ(group->GetState(),
            types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);
  testing::Mock::VerifyAndClearExpectations(&mock_iso_manager_);

  // Inject CIS and ACL disconnection of first device
  InjectCisDisconnected(group, lastDevice);
  InjectCisDisconnected(group, lastDevice, HCI_ERR_CONNECTION_TOUT);
  InjectAclDisconnected(group, lastDevice);

  // Check if group keeps streaming
@@ -2763,11 +2771,23 @@ TEST_F(StateMachineTest, testAttachDeviceToTheStream) {
  lastDevice->conn_id_ = 3;
  group->UpdateActiveContextsMap();

  // Make sure ASE with disconnected CIS are not left in STREAMING
  ASSERT_EQ(lastDevice->GetFirstAseWithState(
                ::le_audio::types::kLeAudioDirectionSink,
                types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING),
            nullptr);
  ASSERT_EQ(lastDevice->GetFirstAseWithState(
                ::le_audio::types::kLeAudioDirectionSource,
                types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING),
            nullptr);

  EXPECT_CALL(gatt_queue, WriteCharacteristic(lastDevice->conn_id_,
                                              lastDevice->ctp_hdls_.val_hdl, _,
                                              GATT_WRITE_NO_RSP, _, _))
      .Times(AtLeast(3));

  EXPECT_CALL(*mock_iso_manager_, EstablishCis(_)).Times(1);
  EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath(_, _)).Times(1);
  LeAudioGroupStateMachine::Get()->AttachToStream(group, lastDevice);

  // Check if group keeps streaming