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

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

leaudio: Fix assert on group disconnect while CIG creation

This fixes race when group was disconnected while state machine was
waiting for CIG creation which leads to assert:

bluetooth: assertion 'leAudioDevice' failed - Shouldn't be called without an active device.

With this patch assert will not happen and CIG will be removed

Bug: 325627564
Test: atest bluetooth_le_audio_test
Flags: Exempt, regression verified with unit test, new test added
Change-Id: I21d29af4e487a609361fdf5fe6cfe1d6bf5de050
parent 89291600
Loading
Loading
Loading
Loading
+12 −9
Original line number Diff line number Diff line
@@ -507,6 +507,17 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine {
              ToString(group->cig.GetState()),
              static_cast<int>(conn_handles.size()));

    if (group->GetTargetState() != AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
      /* Group is not going to stream. It happen while CIG was creating.
       * Remove CIG in such a case
       */
      log::warn("group_id {} is not going to stream anymore. Remove CIG.",
                group->group_id_);
      group->PrintDebugState();
      RemoveCigForGroup(group);
      return;
    }

    /* Assign all connection handles to CIS ids of the CIG */
    group->cig.AssignCisConnHandles(conn_handles);

@@ -516,15 +527,7 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine {
    /* Last node configured, process group to codec configured state */
    group->SetState(AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED);

    if (group->GetTargetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
    PrepareAndSendQoSToTheGroup(group);
    } else {
      log::error(", invalid state transition, from: {} , to: {}",
                 ToString(group->GetState()),
                 ToString(group->GetTargetState()));
      StopStream(group);
      return;
    }
  }

  void FreeLinkQualityReports(LeAudioDevice* leAudioDevice) {
+54 −0
Original line number Diff line number Diff line
@@ -1888,6 +1888,60 @@ TEST_F(StateMachineTest, testConfigureQosFailed) {
  testing::Mock::VerifyAndClearExpectations(mock_iso_manager_);
}

TEST_F(StateMachineTest, testDeviceDisconnectedWhileCigCreated) {
  const auto context_type = kContextTypeMedia;
  const auto leaudio_group_id = 3;
  const auto num_devices = 1;

  // verify proper cleaning when group is disconnected while CIG is creating.

  // Prepare fake connected device in a group
  auto* group =
      PrepareSingleTestDeviceGroup(leaudio_group_id, context_type, num_devices);
  ASSERT_EQ(group->Size(), num_devices);

  PrepareConfigureCodecHandler(group);

  ON_CALL(*mock_iso_manager_, CreateCig).WillByDefault(Return());

  auto* leAudioDevice = group->GetFirstDevice();
  EXPECT_CALL(gatt_queue, WriteCharacteristic(leAudioDevice->conn_id_,
                                              leAudioDevice->ctp_hdls_.val_hdl,
                                              _, GATT_WRITE_NO_RSP, _, _))
      .Times(1);

  EXPECT_CALL(*mock_iso_manager_, CreateCig(_, _)).Times(1);
  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);

  InjectInitialIdleNotification(group);

  // 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)}));

  // Check if group has transitioned to a proper state
  ASSERT_EQ(group->GetState(),
            types::AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED);

  testing::Mock::VerifyAndClearExpectations(mock_iso_manager_);

  InjectAclDisconnected(group, leAudioDevice);
  std::vector<uint16_t> conn_handles = {0x0001, 0x0002};
  int cig_id = 1;

  EXPECT_CALL(*mock_iso_manager_, RemoveCig(_, _)).Times(1);
  LeAudioGroupStateMachine::Get()->ProcessHciNotifOnCigCreate(
      group, HCI_SUCCESS, cig_id, conn_handles);

  ASSERT_EQ(1, get_func_call_count("alarm_cancel"));
  testing::Mock::VerifyAndClearExpectations(mock_iso_manager_);
}

TEST_F(StateMachineTest, testStreamCreationError) {
  /* Device is banded headphones with 1x snk + 0x src ase
   * (1xunidirectional CIS) with channel count 2 (for stereo