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

Commit 30eb9ea3 authored by Łukasz Rymanowski's avatar Łukasz Rymanowski Committed by Gerrit Code Review
Browse files

Merge "leaudio: Fix clearing state machine"

parents 87d40daf a7c12865
Loading
Loading
Loading
Loading
+1 −1
Original line number Original line Diff line number Diff line
@@ -1829,7 +1829,7 @@ bool LeAudioDeviceGroup::IsCisPartOfCurrentStream(uint16_t cis_conn_hdl) {
      stream_conf.source_streams.begin(), stream_conf.source_streams.end(),
      stream_conf.source_streams.begin(), stream_conf.source_streams.end(),
      [cis_conn_hdl](auto& pair) { return cis_conn_hdl == pair.first; });
      [cis_conn_hdl](auto& pair) { return cis_conn_hdl == pair.first; });


  return iter != stream_conf.sink_streams.end();
  return (iter != stream_conf.source_streams.end());
}
}


void LeAudioDeviceGroup::StreamOffloaderUpdated(uint8_t direction) {
void LeAudioDeviceGroup::StreamOffloaderUpdated(uint8_t direction) {
+20 −10
Original line number Original line Diff line number Diff line
@@ -704,18 +704,26 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine {
       * changed notify upper layer so the offloader can be updated with CIS
       * changed notify upper layer so the offloader can be updated with CIS
       * information.
       * information.
       */
       */
      if (!group->HaveAllCisesDisconnected() &&
      if (!group->HaveAllCisesDisconnected()) {
          (group->GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) &&
        /* some CISes are connected */

        if ((group->GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) &&
            (group->GetTargetState() ==
            (group->GetTargetState() ==
             AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING)) {
             AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING)) {
        /* We keep streaming but want others to let know user that it might be
          /* We keep streaming but want others to let know user that it might
         * need to update offloader with new CIS configuration
           * be need to update offloader with new CIS configuration
           */
           */
        state_machine_callbacks_->StatusReportCb(group->group_id_,
          state_machine_callbacks_->StatusReportCb(
                                                 GroupStreamStatus::STREAMING);
              group->group_id_, GroupStreamStatus::STREAMING);
        } else {
          LOG_WARN("group_id %d not in streaming, CISes are still there",
                   group->group_id_);
          group->PrintDebugState();
        }
        }

        return;
        return;
      }
      }
    }


    /* Group is not connected and all the CISes are down.
    /* Group is not connected and all the CISes are down.
     * Clean states and destroy HCI group
     * Clean states and destroy HCI group
@@ -896,11 +904,13 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine {
          ases_pair.sink->state == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
          ases_pair.sink->state == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
        ases_pair.sink->state =
        ases_pair.sink->state =
            AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED;
            AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED;
        ases_pair.sink->active = false;
      }
      }
      if (ases_pair.source && ases_pair.source->state ==
      if (ases_pair.source && ases_pair.source->state ==
                                  AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
                                  AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
        ases_pair.source->state =
        ases_pair.source->state =
            AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED;
            AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED;
        ases_pair.source->active = false;
      }
      }
    }
    }


+91 −2
Original line number Original line Diff line number Diff line
@@ -1158,11 +1158,18 @@ class StateMachineTest : public Test {


  void PrepareReleaseHandler(LeAudioDeviceGroup* group,
  void PrepareReleaseHandler(LeAudioDeviceGroup* group,
                             int verify_ase_count = 0,
                             int verify_ase_count = 0,
                             bool inject_disconnect_device = false) {
                             bool inject_disconnect_device = false,
                             LeAudioDevice* dev = nullptr) {
    ase_ctp_handlers[ascs::kAseCtpOpcodeRelease] =
    ase_ctp_handlers[ascs::kAseCtpOpcodeRelease] =
        [group, verify_ase_count, inject_disconnect_device, this](
        [group, verify_ase_count, inject_disconnect_device, dev, this](
            LeAudioDevice* device, std::vector<uint8_t> value,
            LeAudioDevice* device, std::vector<uint8_t> value,
            GATT_WRITE_OP_CB cb, void* cb_data) {
            GATT_WRITE_OP_CB cb, void* cb_data) {
          if (dev != nullptr && device != dev) {
            LOG_INFO("Do nothing for %s",
                     ADDRESS_TO_LOGGABLE_CSTR(dev->address_));
            return;
          }

          auto num_ase = value[1];
          auto num_ase = value[1];


          // Verify ase count if needed
          // Verify ase count if needed
@@ -4449,6 +4456,88 @@ TEST_F(StateMachineTest, StreamReconfigureAfterCisLostTwoDevices) {
  testing::Mock::VerifyAndClearExpectations(&mock_callbacks_);
  testing::Mock::VerifyAndClearExpectations(&mock_callbacks_);
}
}


TEST_F(StateMachineTest, StreamClearAfterReleaseAndConnectionTimeout) {
  auto context_type = kContextTypeMedia;
  const auto leaudio_group_id = 4;
  const auto num_devices = 2;

  /* Scenario
  1. Streaming to 2 device
  2. Stream suspend
  3. One device got to IDLE
  4. Second device Connection Timeout
  */

  // Prepare multiple fake connected devices in a group
  auto* group = PrepareSingleTestDeviceGroup(
      leaudio_group_id, context_type, num_devices,
      kContextTypeConversational | kContextTypeMedia);
  ASSERT_EQ(group->Size(), num_devices);

  PrepareConfigureCodecHandler(group);
  PrepareConfigureQosHandler(group);
  PrepareEnableHandler(group);

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

  InjectInitialIdleNotification(group);

  auto* leAudioDevice = group->GetFirstDevice();
  auto* firstDevice = leAudioDevice;
  auto* lastDevice = leAudioDevice;

  while (leAudioDevice) {
    lastDevice = leAudioDevice;
    leAudioDevice = group->GetNextDevice(leAudioDevice);
  }

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

  // Start the configuration and stream Media content
  context_type = kContextTypeMedia;
  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_STREAMING);
  ASSERT_EQ(1, get_func_call_count("alarm_cancel"));
  testing::Mock::VerifyAndClearExpectations(&mock_callbacks_);

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

  /* Prepare release handler only for first device. */
  PrepareReleaseHandler(group, 0, false, firstDevice);
  LeAudioGroupStateMachine::Get()->StopStream(group);

  /* Second device will disconnect because of timeout. Do not bother
   * with remove data path response from the controller. In test we are doing it
   * in a test thread which breaks things. */
  ON_CALL(*mock_iso_manager_, RemoveIsoDataPath).WillByDefault(Return());
  InjectCisDisconnected(group, lastDevice, HCI_ERR_CONNECTION_TOUT);
  InjectAclDisconnected(group, lastDevice);

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

TEST_F(StateMachineTest, StreamStartWithDifferentContextFromConfiguredState) {
TEST_F(StateMachineTest, StreamStartWithDifferentContextFromConfiguredState) {
  auto context_type = kContextTypeConversational;
  auto context_type = kContextTypeConversational;
  const auto leaudio_group_id = 6;
  const auto leaudio_group_id = 6;