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

Commit cd62db6f authored by Łukasz Rymanowski's avatar Łukasz Rymanowski Committed by Automerger Merge Worker
Browse files

leaudio: Inactivate device on internal state machine error am: 2aa5bafb

parents b03d7e3a 2aa5bafb
Loading
Loading
Loading
Loading
+50 −14
Original line number Diff line number Diff line
@@ -517,6 +517,12 @@ class LeAudioClientImpl : public LeAudioClient {
      DisconnectDevice(leAudioDevice, true, recovery);
      leAudioDevice = group->GetNextActiveDevice(leAudioDevice);
    } while (leAudioDevice);

    if (recovery) {
      /* Both devices will  be disconnected soon. Notify upper layer that group
       * is inactive */
      groupSetAndNotifyInactive();
    }
  }

  void OnDeviceAutonomousStateTransitionTimeout(LeAudioDevice* leAudioDevice) {
@@ -1118,26 +1124,35 @@ class LeAudioClientImpl : public LeAudioClient {
    return group->is_duplex_preference_le_audio;
  }

  void GroupSetActive(const int group_id) override {
    LOG_INFO(" group_id: %d", group_id);

    if (group_id == bluetooth::groups::kGroupUnknown) {
  void groupSetAndNotifyInactive(void) {
    if (active_group_id_ == bluetooth::groups::kGroupUnknown) {
        /* Nothing to do */
      return;
    }

      LOG_INFO("Active group_id changed %d -> %d", active_group_id_, group_id);
    auto group_id_to_close = active_group_id_;
    active_group_id_ = bluetooth::groups::kGroupUnknown;

    LOG_INFO("Group id: %d", group_id_to_close);
    if (alarm_is_scheduled(suspend_timeout_)) alarm_cancel(suspend_timeout_);

    StopAudio();
    ClientAudioInterfaceRelease();
    callbacks_->OnGroupStatus(group_id_to_close, GroupStatus::INACTIVE);
  }

  void GroupSetActive(const int group_id) override {
    LOG_INFO(" group_id: %d", group_id);

    if (group_id == bluetooth::groups::kGroupUnknown) {
      if (active_group_id_ == bluetooth::groups::kGroupUnknown) {
        /* Nothing to do */
        return;
      }

      LOG_INFO("Active group_id changed %d -> %d", active_group_id_, group_id);
      auto group_id_to_close = active_group_id_;
      groupSetAndNotifyInactive();
      GroupStop(group_id_to_close);
      callbacks_->OnGroupStatus(group_id_to_close, GroupStatus::INACTIVE);

      return;
    }

@@ -1202,18 +1217,24 @@ class LeAudioClientImpl : public LeAudioClient {
      return;
    }

    if (active_group_id_ == bluetooth::groups::kGroupUnknown) {
    auto previous_active_group = active_group_id_;
    LOG_INFO("Active group_id changed %d -> %d", previous_active_group,
             group_id);

    if (previous_active_group == bluetooth::groups::kGroupUnknown) {
      /* Expose audio sessions if there was no previous active group */
      StartAudioSession(group, &current_source_codec_config,
                        &current_sink_codec_config);
      active_group_id_ = group_id;
    } else {
      /* In case there was an active group. Stop the stream */
      GroupStop(active_group_id_);
      callbacks_->OnGroupStatus(active_group_id_, GroupStatus::INACTIVE);
      /* In case there was an active group. Stop the stream, but before that, set
       * the new group so the group change is correctly handled in OnStateMachineStatusReportCb
       */
      active_group_id_ = group_id;
      GroupStop(previous_active_group);
      callbacks_->OnGroupStatus(previous_active_group, GroupStatus::INACTIVE);
    }

    LOG_INFO("Active group_id changed %d -> %d", active_group_id_, group_id);
    active_group_id_ = group_id;
    callbacks_->OnGroupStatus(active_group_id_, GroupStatus::ACTIVE);
    SendAudioGroupSelectableCodecConfigChanged(group);
  }
@@ -5422,6 +5443,21 @@ class LeAudioClientImpl : public LeAudioClient {
      }
      case GroupStreamStatus::RELEASING:
      case GroupStreamStatus::SUSPENDING:
        if (active_group_id_ != bluetooth::groups::kGroupUnknown &&
            (active_group_id_ == group->group_id_) &&
            !group->IsPendingConfiguration() &&
            (audio_sender_state_ == AudioState::STARTED ||
             audio_receiver_state_ == AudioState::STARTED)) {
          /* If releasing state is happening but it was not initiated either by
           * reconfiguration or Audio Framework actions either by the Active group change,
           * it means that it is some internal state machine error. This is very unlikely and
           * for now just Inactivate the group.
           */
          LOG_ERROR("Internal state machine error");
          group->PrintDebugState();
          groupSetAndNotifyInactive();
        }

        if (audio_sender_state_ != AudioState::IDLE)
          audio_sender_state_ = AudioState::RELEASING;

+63 −0
Original line number Diff line number Diff line
@@ -4326,6 +4326,69 @@ TEST_F(UnicastTest, RemoveNodeWhileStreaming) {
  Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_);
}

TEST_F(UnicastTest, InactiveDeviceOnInternalStateMachineError) {
  const RawAddress test_address0 = GetTestAddress(0);
  int group_id = bluetooth::groups::kGroupUnknown;

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

  // Start streaming
  constexpr uint8_t cis_count_out = 1;
  constexpr uint8_t cis_count_in = 0;

  constexpr int gmcs_ccid = 1;
  constexpr int gtbs_ccid = 2;

  // Audio sessions are started only when device gets active
  EXPECT_CALL(*mock_le_audio_source_hal_client_, Start(_, _)).Times(1);
  EXPECT_CALL(*mock_le_audio_sink_hal_client_, Start(_, _)).Times(1);
  LeAudioClient::Get()->SetCcidInformation(gmcs_ccid, 4 /* Media */);
  LeAudioClient::Get()->SetCcidInformation(gtbs_ccid, 2 /* Phone */);
  LeAudioClient::Get()->GroupSetActive(group_id);
  SyncOnMainLoop();

  types::BidirectionalPair<std::vector<uint8_t>> ccids = {.sink = {gmcs_ccid},
                                                          .source = {}};
  EXPECT_CALL(mock_state_machine_, StartStream(_, _, _, ccids)).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();

  // Verify Data transfer on one audio source cis
  TestAudioDataTransfer(group_id, cis_count_out, cis_count_in, 1920);

  EXPECT_CALL(mock_audio_hal_client_callbacks_,
              OnGroupStatus(group_id, GroupStatus::INACTIVE))
      .Times(1);

  /* This is internal error of the state machine */
  state_machine_callbacks_->StatusReportCb(group_id,
                                           GroupStreamStatus::RELEASING);

  SyncOnMainLoop();
  Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_);
}

TEST_F(UnicastTest, GroupingAddTwiceNoRemove) {
  // Earbud connects without known grouping
  uint8_t group_id0 = bluetooth::groups::kGroupUnknown;