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

Commit ee39cdc5 authored by Łukasz Rymanowski's avatar Łukasz Rymanowski
Browse files

leaudio: Improve disconnection during streaming

When LeAudio Set is disconnected during the streaming
from the upper layer, we want to make sure that ACL of both devices are
disconnect when Stream is closed.

Previosly disconnection of first device triggered StopStream but when
second device was disconnected from the upper layer, that cause ACL
disconnection. Usually it worked fine as disconnecting ACL  implies CIS
to be disconnected, but some controllers keeps CIS resources preventing
CIG to be removed later on during the process.

With this CL, all the members will wait until stream is closed

Bug: 381378205
Flag: Exempt, regression verified with unit test
Test: atest bluetooth_le_audio_client_test
Change-Id: I3f531c453ec59da3c91935d73b7163dbc48ae91a
parent 54edfd68
Loading
Loading
Loading
Loading
+15 −0
Original line number Diff line number Diff line
@@ -1896,11 +1896,26 @@ public:
              dev->SetConnectionState(DeviceConnectState::DISCONNECTED);
            }
          }

          /* If group is Streaming or is in transition for Streaming - lets stop it
           * and mark device to disconnect when stream is closed
           */
          if (group->IsStreaming() || !group->IsReleasingOrIdle()) {
            log::debug("group_id {} needs to stop streaming before {} disconnection",
                       group->group_id_, leAudioDevice->address_);
            leAudioDevice->closing_stream_for_disconnection_ = true;
            groupStateMachine_->StopStream(group);
            return;
          }

          if (group->IsReleasing()) {
            log::debug("group_id {} needs to stop streaming before {} disconnection",
                       group->group_id_, leAudioDevice->address_);
            /* Stream is releasing, wait till it is completed and then disconnect ACL. */
            leAudioDevice->closing_stream_for_disconnection_ = true;
            return;
          }

          force_acl_disconnect &= group->IsEnabled();
        }

+4 −0
Original line number Diff line number Diff line
@@ -1138,6 +1138,10 @@ bool LeAudioDeviceGroup::IsReleasingOrIdle(void) const {
          !in_transition_);
}

bool LeAudioDeviceGroup::IsReleasing(void) const {
  return (target_state_ == AseState::BTA_LE_AUDIO_ASE_STATE_IDLE) && in_transition_;
}

bool LeAudioDeviceGroup::IsGroupStreamReady(void) const {
  bool is_device_ready = false;

+1 −0
Original line number Diff line number Diff line
@@ -407,6 +407,7 @@ public:
  }
  bool IsStreaming(void) const;
  bool IsReleasingOrIdle(void) const;
  bool IsReleasing(void) const;

  void PrintDebugState(void) const;
  void Dump(std::stringstream& stream, int active_group_id) const;
+72 −1
Original line number Diff line number Diff line
@@ -492,6 +492,12 @@ protected:
            base::Unretained(this->gatt_callback), event_data));
  }
  void TriggerDisconnectionFromApp(const RawAddress& address) {
    do_in_main_thread(base::BindOnce(
            [](LeAudioClient* client, const RawAddress& address) { client->Disconnect(address); },
            LeAudioClient::Get(), address));
  }
  void InjectDisconnectedEvent(uint16_t conn_id,
                               tGATT_DISCONN_REASON reason = GATT_CONN_TERMINATE_LOCAL_HOST) {
    ASSERT_NE(conn_id, GATT_INVALID_CONN_ID);
@@ -1434,9 +1440,15 @@ protected:
      // Inject the state
      group->SetTargetState(types::AseState::BTA_LE_AUDIO_ASE_STATE_IDLE);
      group->SetState(group->GetTargetState());
      group->SetState(types::AseState::BTA_LE_AUDIO_ASE_STATE_RELEASING);
      state_machine_callbacks_->StatusReportCb(group->group_id_, GroupStreamStatus::RELEASING);
      if (stay_at_releasing_stop_stream) {
        log::info("StopStream {} -> stay in Releasing state", group->group_id_);
        return;
      }
      group->SetState(group->GetTargetState());
      do_in_main_thread(base::BindOnce(
              [](bluetooth::le_audio::LeAudioGroupStateMachine::Callbacks* cb, int group_id) {
                cb->StatusReportCb(group_id, GroupStreamStatus::IDLE);
@@ -1477,6 +1489,7 @@ protected:
    SetUpMockCodecManager(codec_location);
    stay_at_qos_config_in_start_stream = false;
    stay_at_releasing_stop_stream = false;
    available_snk_context_types_ = 0xffff;
    available_src_context_types_ = 0xffff;
@@ -2699,6 +2712,7 @@ protected:
  bluetooth::le_audio::LeAudioGroupStateMachine::Callbacks* state_machine_callbacks_;
  std::map<int, LeAudioDeviceGroup*> streaming_groups;
  bool stay_at_qos_config_in_start_stream = false;
  bool stay_at_releasing_stop_stream = false;
  bool attach_to_stream_scheduled = false;
@@ -5960,6 +5974,63 @@ TEST_F(UnicastTest, DisconnecteWhileAlmostStreaming) {
  ASSERT_EQ(group->GetState(), types::AseState::BTA_LE_AUDIO_ASE_STATE_IDLE);
}
TEST_F(UnicastTest, DisconnecteWhileAlmostStreaming_twoDevices) {
  const RawAddress test_address0 = GetTestAddress(0);
  const RawAddress test_address1 = GetTestAddress(1);
  int group_id = 5;
  TestSetupRemoteDevices(group_id);
  SyncOnMainLoop();
  EXPECT_CALL(mock_state_machine_, StartStream(_, _, _, _)).Times(1);
  StartStreaming(AUDIO_USAGE_MEDIA, AUDIO_CONTENT_TYPE_MUSIC, group_id);
  SyncOnMainLoop();
  Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_);
  Mock::VerifyAndClearExpectations(mock_le_audio_source_hal_client_);
  Mock::VerifyAndClearExpectations(&mock_state_machine_);
  SyncOnMainLoop();
  ASSERT_NE(0lu, streaming_groups.count(group_id));
  auto group_inject = streaming_groups.at(group_id);
  // This shall be called once only when first device from the group is disconnecting.
  EXPECT_CALL(mock_state_machine_, StopStream(_)).Times(1);
  EXPECT_CALL(mock_audio_hal_client_callbacks_, OnConnectionState(ConnectionState::DISCONNECTED, _))
          .Times(0);
  // Do not got to IDLE state imidiatelly.
  stay_at_releasing_stop_stream = true;
  log::info("First of all disconnect: {}", test_address0);
  TriggerDisconnectionFromApp(test_address0);
  SyncOnMainLoop();
  log::info("Secondly disconnect: {}", test_address1);
  TriggerDisconnectionFromApp(test_address1);
  SyncOnMainLoop();
  Mock::VerifyAndClearExpectations(&mock_state_machine_);
  Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_);
  EXPECT_CALL(mock_audio_hal_client_callbacks_,
              OnConnectionState(ConnectionState::DISCONNECTED, test_address0))
          .Times(1);
  EXPECT_CALL(mock_audio_hal_client_callbacks_,
              OnConnectionState(ConnectionState::DISCONNECTED, test_address1))
          .Times(1);
  do_in_main_thread(base::BindOnce(
          [](bluetooth::le_audio::LeAudioGroupStateMachine::Callbacks* cb, int group_id) {
            cb->StatusReportCb(group_id, GroupStreamStatus::IDLE);
          },
          state_machine_callbacks_, group_id));
  SyncOnMainLoop();
  Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_);
}
TEST_F(UnicastTest, EarbudsTwsStyleStreaming) {
  const RawAddress test_address0 = GetTestAddress(0);
  int group_id = bluetooth::groups::kGroupUnknown;