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

Commit 9479db36 authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "leaudio: Fix clearing watchdog" into main

parents 9dfd223e d2cbc3cd
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -1228,14 +1228,14 @@ public:
         * In such an event, there is need to notify upper layer about state
         * from here.
         */
        cancel_watchdog_if_needed(group->group_id_);

        if (current_group_state == AseState::BTA_LE_AUDIO_ASE_STATE_IDLE) {
          cancel_watchdog_if_needed(group->group_id_);
          log::info("Cises disconnected for group {}, we are good in Idle state.",
                    group->group_id_);
          ReleaseCisIds(group);
          state_machine_callbacks_->StatusReportCb(group->group_id_, GroupStreamStatus::IDLE);
        } else if (current_group_state == AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED) {
          cancel_watchdog_if_needed(group->group_id_);
          auto reconfig = group->IsPendingConfiguration();
          log::info(
                  "Cises disconnected for group: {}, we are good in Configured "
+181 −0
Original line number Diff line number Diff line
@@ -766,6 +766,15 @@ protected:
    }
  }

  void InjectCachedConfigurationForGroup(LeAudioDeviceGroup* group) {
    auto leAudioDevice = group->GetFirstActiveDevice();
    while (leAudioDevice) {
      log::info("Group : {},  dev: {}", group->group_id_, leAudioDevice->address_);
      InjectCachedConfigurationForActiveAses(group, leAudioDevice);
      leAudioDevice = group->GetNextActiveDevice(leAudioDevice);
    }
  }

  void InjectStreamingStateFroActiveAses(LeAudioDeviceGroup* group, LeAudioDevice* device) {
    for (auto& ase : device->ases_) {
      if (!ase.active) {
@@ -3806,6 +3815,178 @@ static void InjectCisDisconnected(LeAudioDeviceGroup* group, LeAudioDevice* leAu
  }
}

TEST_F(StateMachineTest, testReleaseMultiple_CisDisconnectedBeforeGettingToIdleState) {
  const auto context_type = kContextTypeMedia;
  const auto leaudio_group_id = 6;
  const auto num_devices = 2;

  /* Test Scenario:
   * 1. Start stream
   * 2. Stop the stream
   * 3. While stopping, make sure that CISes are disconnected before current state is IDLE - verify
   * watchdog keeps running
   * 4. Move to IDLE, make sure watchdog is cleared
   */

  auto* group = PrepareSingleTestDeviceGroup(leaudio_group_id, context_type, num_devices);
  ASSERT_EQ(group->Size(), num_devices);

  PrepareConfigureCodecHandler(group);
  PrepareConfigureQosHandler(group);
  PrepareEnableHandler(group);
  PrepareDisableHandler(group);
  PrepareReleaseHandler(group);

  stay_in_releasing_state_ = true;

  auto* leAudioDevice = group->GetFirstDevice();
  auto expected_devices_written = 0;
  while (leAudioDevice) {
    EXPECT_CALL(gatt_queue,
                WriteCharacteristic(leAudioDevice->conn_id_, leAudioDevice->ctp_hdls_.val_hdl, _,
                                    GATT_WRITE_NO_RSP, _, _))
            .Times(AtLeast(4));
    expected_devices_written++;
    leAudioDevice = group->GetNextDevice(leAudioDevice);
  }
  ASSERT_EQ(expected_devices_written, num_devices);

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

  InjectInitialIdleNotification(group);

  EXPECT_CALL(mock_callbacks_,
              StatusReportCb(leaudio_group_id, bluetooth::le_audio::GroupStreamStatus::STREAMING))
          .Times(1);

  // Start the configuration and stream Media content
  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_STREAMING);

  ASSERT_EQ(1, get_func_call_count("alarm_cancel"));
  reset_mock_function_count_map();
  testing::Mock::VerifyAndClearExpectations(&mock_callbacks_);

  // 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));
  EXPECT_CALL(mock_callbacks_,
              StatusReportCb(leaudio_group_id, bluetooth::le_audio::GroupStreamStatus::STREAMING))
          .Times(0);

  // Stop the stream
  LeAudioGroupStateMachine::Get()->StopStream(group);

  // Watchdog shall not be cancled here.
  ASSERT_EQ(0, get_func_call_count("alarm_cancel"));

  InjectReleaseAndIdleStateForAGroup(group, false, true);

  // Check if group has transitioned to a proper state
  ASSERT_EQ(group->GetState(), types::AseState::BTA_LE_AUDIO_ASE_STATE_IDLE);
  ASSERT_EQ(1, get_func_call_count("alarm_cancel"));
  testing::Mock::VerifyAndClearExpectations(&mock_callbacks_);
  testing::Mock::VerifyAndClearExpectations(mock_iso_manager_);
}

TEST_F(StateMachineTest, testReleaseMultiple_CisDisconnectedBeforeGettingToConfiguredState) {
  const auto context_type = kContextTypeMedia;
  const auto leaudio_group_id = 6;
  const auto num_devices = 2;

  /* Test Scenario:
   * 1. Start stream
   * 2. Stop the stream
   * 3. While stopping, make sure that CISes are disconnected before current state is CONFIGURED -
   * verify watchdog keeps running
   * 4. Move to CONFIGURED, make sure watchdog is cleared
   */

  auto* group = PrepareSingleTestDeviceGroup(leaudio_group_id, context_type, num_devices);
  ASSERT_EQ(group->Size(), num_devices);

  PrepareConfigureCodecHandler(group, 0, true);
  PrepareConfigureQosHandler(group, 0, true);
  PrepareEnableHandler(group);
  PrepareDisableHandler(group);
  PrepareReleaseHandler(group);

  stay_in_releasing_state_ = true;

  auto* leAudioDevice = group->GetFirstDevice();
  auto expected_devices_written = 0;
  while (leAudioDevice) {
    EXPECT_CALL(gatt_queue,
                WriteCharacteristic(leAudioDevice->conn_id_, leAudioDevice->ctp_hdls_.val_hdl, _,
                                    GATT_WRITE_NO_RSP, _, _))
            .Times(AtLeast(4));
    expected_devices_written++;
    leAudioDevice = group->GetNextDevice(leAudioDevice);
  }
  ASSERT_EQ(expected_devices_written, num_devices);

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

  InjectInitialIdleNotification(group);

  EXPECT_CALL(mock_callbacks_,
              StatusReportCb(leaudio_group_id, bluetooth::le_audio::GroupStreamStatus::STREAMING))
          .Times(1);

  // Start the configuration and stream Media content
  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_STREAMING);

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

  ASSERT_EQ(1, get_func_call_count("alarm_cancel"));
  reset_mock_function_count_map();

  // 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::CONFIGURED_AUTONOMOUS));
  EXPECT_CALL(mock_callbacks_,
              StatusReportCb(leaudio_group_id, bluetooth::le_audio::GroupStreamStatus::STREAMING))
          .Times(0);

  // Stop the stream
  LeAudioGroupStateMachine::Get()->StopStream(group);

  // Watchdog shall not be cancled here.
  ASSERT_EQ(0, get_func_call_count("alarm_cancel"));

  InjectCachedConfigurationForGroup(group);

  // Check if group has transitioned to a proper state
  ASSERT_EQ(group->GetState(), types::AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED);
  ASSERT_EQ(1, get_func_call_count("alarm_cancel"));
  testing::Mock::VerifyAndClearExpectations(&mock_callbacks_);
  testing::Mock::VerifyAndClearExpectations(mock_iso_manager_);
}

TEST_F(StateMachineTest, testAutonomousReleaseMultiple) {
  const auto context_type = kContextTypeMedia;
  const auto leaudio_group_id = 6;