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

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

leaudio: Fix invalid state machine timeout

This patch fixes following scenario
1. State machine is ordered to start the stream and Codec Config CMD
is send out to the remote, but there is not yet answer

2. Group is set to inactive while waiting for CODEC CONFIGURED state.

In this particular case, internal state machine watchdog was not cleared
which lead later to disconnect devices.

THis is fixed here.

Bug: 361009375
Test: atest bluetooth_le_audio_test bluetooth_le_audio_client_test
Flag: Exempt, regression tested with unit tests, tests checking new
behaviour has been added

Change-Id: I5414534c56f170f836b6a6b7dd894d9252bdd8ee
parent 285e9ff7
Loading
Loading
Loading
Loading
+0 −11
Original line number Diff line number Diff line
@@ -955,17 +955,6 @@ public:
      return;
    }

    if (group->GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_IDLE) {
      if (group->GetTargetState() != AseState::BTA_LE_AUDIO_ASE_STATE_IDLE) {
        log::warn("group {} was about to stream, but got canceled: {}", group_id,
                  ToString(group->GetTargetState()));
        group->SetTargetState(AseState::BTA_LE_AUDIO_ASE_STATE_IDLE);
      } else {
        log::warn(", group {} already stopped: {}", group_id, ToString(group->GetState()));
      }
      return;
    }

    groupStateMachine_->StopStream(group);
  }

+9 −2
Original line number Diff line number Diff line
@@ -1065,8 +1065,15 @@ bool LeAudioDeviceGroup::IsStreaming(void) const {
}

bool LeAudioDeviceGroup::IsReleasingOrIdle(void) const {
  return (target_state_ == AseState::BTA_LE_AUDIO_ASE_STATE_IDLE) ||
         (current_state_ == AseState::BTA_LE_AUDIO_ASE_STATE_IDLE);
  /* If target state is IDLE then for sure group is either releasing or idle.
   * Otherwise, we have "idle states" - Idle or Configured when caching is
   * supported on the remote side. In both cases to check it is to make sure
   * group is not in transition.
   */
  return target_state_ == AseState::BTA_LE_AUDIO_ASE_STATE_IDLE ||
         ((current_state_ == AseState::BTA_LE_AUDIO_ASE_STATE_IDLE ||
           current_state_ == AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED) &&
          !in_transition_);
}

bool LeAudioDeviceGroup::IsGroupStreamReady(void) const {
+51 −0
Original line number Diff line number Diff line
@@ -1191,6 +1191,11 @@ protected:
              // Inject the state
              group->SetTargetState(types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);
              if (block_qos_config) {
                return true;
              }
              group->SetState(types::AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED);
              streaming_groups[group->group_id_] = group;
@@ -1432,6 +1437,7 @@ protected:
    SetUpMockCodecManager(codec_location);
    block_streaming_state_callback = false;
    block_qos_config = false;
    available_snk_context_types_ = 0xffff;
    available_src_context_types_ = 0xffff;
@@ -2654,6 +2660,7 @@ protected:
  bluetooth::le_audio::LeAudioGroupStateMachine::Callbacks* state_machine_callbacks_;
  std::map<int, LeAudioDeviceGroup*> streaming_groups;
  bool block_streaming_state_callback = false;
  bool block_qos_config = false;
  bool attach_to_stream_scheduled = false;
@@ -4788,6 +4795,50 @@ TEST_F(UnicastTest, GroupSetActive_SourcePacksEmpty) {
  Mock::VerifyAndClearExpectations(mock_le_audio_source_hal_client_);
}
TEST_F(UnicastTest, GroupSetActive_and_InactiveDuringStreamConfiguration) {
  const RawAddress test_address0 = GetTestAddress(0);
  int group_id = bluetooth::groups::kGroupUnknown;
  empty_source_pack_ = true;
  /**
   * In this test we want to make sure that StopStream is called when group is set to inactive
   * while being between IDLE and CONFIGURED state
   */
  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(1);
  block_qos_config = true;
  LeAudioClient::Get()->GroupSetActive(group_id);
  StartStreaming(AUDIO_USAGE_MEDIA, AUDIO_CONTENT_TYPE_MUSIC, group_id, AUDIO_SOURCE_INVALID, false,
                 false);
  SyncOnMainLoop();
  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;
+2 −1
Original line number Diff line number Diff line
@@ -314,7 +314,8 @@ public:

  void StopStream(LeAudioDeviceGroup* group) override {
    if (group->IsReleasingOrIdle()) {
      log::info("group: {} already in releasing process", group->group_id_);
      log::info("group: {} in_transition: {}, current_state {}", group->group_id_,
                group->IsInTransition(), ToString(group->GetState()));
      return;
    }

+55 −0
Original line number Diff line number Diff line
@@ -8474,5 +8474,60 @@ TEST_F(StateMachineTest, testAutonomousDisable_GoToIdle) {
  testing::Mock::VerifyAndClearExpectations(&mock_callbacks_);
}

TEST_F(StateMachineTest, testStopStreamBeforeCodecConfigureIsArrived) {
  /* Device is banded headphones with 1x snk + 0x src ase
   * (1xunidirectional CIS with channel count 2 for stereo)
   */
  const auto context_type = kContextTypeRingtone;
  const int leaudio_group_id = 4;
  channel_count_ = kLeAudioCodecChannelCountSingleChannel | kLeAudioCodecChannelCountTwoChannel;

  // Prepare fake connected device group
  auto* group = PrepareSingleTestDeviceGroup(leaudio_group_id, context_type);

  auto* leAudioDevice = group->GetFirstDevice();

  /*
   * 1 - Configure ASE
   * 2 - Release ASE
   */
  EXPECT_CALL(gatt_queue,
              WriteCharacteristic(1, leAudioDevice->ctp_hdls_.val_hdl, _, GATT_WRITE_NO_RSP, _, _))
          .Times(2);

  EXPECT_CALL(*mock_iso_manager_, CreateCig(_, _)).Times(0);
  EXPECT_CALL(*mock_iso_manager_, EstablishCis(_)).Times(0);
  EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath(_, _)).Times(0);
  EXPECT_CALL(*mock_iso_manager_, RemoveIsoDataPath(_, _)).Times(0);
  EXPECT_CALL(*mock_iso_manager_, DisconnectCis(_, _)).Times(0);
  EXPECT_CALL(*mock_iso_manager_, RemoveCig(_, _)).Times(0);

  InjectInitialIdleNotification(group);

  // Validate GroupStreamStatus
  EXPECT_CALL(mock_callbacks_,
              StatusReportCb(leaudio_group_id, bluetooth::le_audio::GroupStreamStatus::RELEASING));
  EXPECT_CALL(mock_callbacks_,
              StatusReportCb(leaudio_group_id, bluetooth::le_audio::GroupStreamStatus::IDLE));

  // Start the configuration and stream Media content
  ASSERT_TRUE(LeAudioGroupStateMachine::Get()->StartStream(
          group, context_type,
          {.sink = types::AudioContexts(context_type),
           .source = types::AudioContexts(context_type)}));

  // Stop the stream before Codec Configured arrived
  LeAudioGroupStateMachine::Get()->StopStream(group);

  testing::Mock::VerifyAndClearExpectations(&gatt_queue);

  InjectCachedConfigurationForActiveAses(group, leAudioDevice);
  InjectReleaseAndIdleStateForAGroup(group);

  // Check if group has transitioned to a proper state
  ASSERT_EQ(group->GetState(), types::AseState::BTA_LE_AUDIO_ASE_STATE_IDLE);
  ASSERT_EQ(2, get_func_call_count("alarm_cancel"));
}

}  // namespace internal
}  // namespace bluetooth::le_audio