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

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

leaudio: Fix handling remote invalid ASE state am: e906d268

parents fb46da17 e906d268
Loading
Loading
Loading
Loading
+73 −32
Original line number Diff line number Diff line
@@ -407,6 +407,12 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine {

    ParseAseStatusHeader(arh, len, value);

    if (ase->id == 0x00) {
      /* Initial state of Ase - update id */
      LOG_INFO(", discovered ase id: %d", arh.id);
      ase->id = arh.id;
    }

    auto state = static_cast<AseState>(arh.state);

    LOG_INFO(" %s , ASE id: %d, state changed %s -> %s ",
@@ -1649,13 +1655,6 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine {
    switch (ase->state) {
      case AseState::BTA_LE_AUDIO_ASE_STATE_IDLE:
      case AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED:
      case AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED:
        if (ase->id == 0x00) {
          /* Initial state of Ase - update id */
          LOG(INFO) << __func__
                    << ", discovered ase id: " << static_cast<int>(arh.id);
          ase->id = arh.id;
        }
        break;
      case AseState::BTA_LE_AUDIO_ASE_STATE_RELEASING: {
        SetAseState(leAudioDevice, ase, AseState::BTA_LE_AUDIO_ASE_STATE_IDLE);
@@ -1709,10 +1708,25 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine {

        break;
      }
      default:
        LOG(ERROR) << __func__ << ", invalid state transition, from: "
                   << static_cast<int>(ase->state) << ", to: "
                   << static_cast<int>(AseState::BTA_LE_AUDIO_ASE_STATE_IDLE);
      case AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED:
      case AseState::BTA_LE_AUDIO_ASE_STATE_DISABLING:
        LOG_ERROR(
            "Ignore invalid attempt of state transition from  %s to %s, %s, "
            "ase_id: %d",
            ToString(ase->state).c_str(),
            ToString(AseState::BTA_LE_AUDIO_ASE_STATE_IDLE).c_str(),
            ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_), ase->id);
        group->PrintDebugState();
        break;
      case AseState::BTA_LE_AUDIO_ASE_STATE_ENABLING:
      case AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING:
        LOG_ERROR(
            "Invalid state transition from %s to %s, %s, ase_id: "
            "%d. Stopping the stream.",
            ToString(ase->state).c_str(),
            ToString(AseState::BTA_LE_AUDIO_ASE_STATE_IDLE).c_str(),
            ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_), ase->id);
        group->PrintDebugState();
        StopStream(group);
        break;
    }
@@ -1812,13 +1826,6 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine {
    /* ase contain current ASE state. New state is in "arh" */
    switch (ase->state) {
      case AseState::BTA_LE_AUDIO_ASE_STATE_IDLE: {
        if (ase->id == 0x00) {
          /* Initial state of Ase - update id */
          LOG(INFO) << __func__
                    << ", discovered ase id: " << static_cast<int>(arh.id);
          ase->id = arh.id;
        }

        struct le_audio::client_parser::ascs::ase_codec_configured_state_params
            rsp;

@@ -2060,7 +2067,18 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine {
        break;
      }
      case AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED:
        /* TODO: Config Codec */
        SetAseState(leAudioDevice, ase,
                    AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED);
        group->PrintDebugState();
        break;
      case AseState::BTA_LE_AUDIO_ASE_STATE_DISABLING:
        LOG_ERROR(
            "Ignore invalid attempt of state transition from %s to %s, %s, "
            "ase_id: %d",
            ToString(ase->state).c_str(),
            ToString(AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED).c_str(),
            ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_), ase->id);
        group->PrintDebugState();
        break;
      case AseState::BTA_LE_AUDIO_ASE_STATE_RELEASING:
        SetAseState(leAudioDevice, ase,
@@ -2118,11 +2136,15 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine {
        state_machine_callbacks_->StatusReportCb(
            group->group_id_, GroupStreamStatus::CONFIGURED_AUTONOMOUS);
        break;
      default:
        LOG(ERROR) << __func__ << ", invalid state transition, from: "
                   << static_cast<int>(ase->state) << ", to: "
                   << static_cast<int>(
                          AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED);
      case AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING:
      case AseState::BTA_LE_AUDIO_ASE_STATE_ENABLING:
        LOG_ERROR(
            "Invalid state transition from %s to %s, %s, ase_id: %d. Stopping "
            "the stream",
            ToString(ase->state).c_str(),
            ToString(AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED).c_str(),
            ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_), ase->id);
        group->PrintDebugState();
        StopStream(group);
        break;
    }
@@ -2165,9 +2187,6 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine {

        break;
      }
      case AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED:
        /* TODO: Config Codec error/Config Qos/Config QoS error/Enable error */
        break;
      case AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING:
        if (ase->direction == le_audio::types::kLeAudioDirectionSource) {
          /* Source ASE cannot go from Streaming to QoS Configured state */
@@ -2223,11 +2242,33 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine {
        }
        break;
      }
      default:
        LOG(ERROR) << __func__ << ", invalid state transition, from: "
                   << static_cast<int>(ase->state) << ", to: "
                   << static_cast<int>(
                          AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED);

      case AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED:
        LOG_INFO(
            "Unexpected state transition from %s to %s, %s, ase_id: %d",
            ToString(ase->state).c_str(),
            ToString(AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED).c_str(),
            ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_), ase->id);
        group->PrintDebugState();
        break;
      case AseState::BTA_LE_AUDIO_ASE_STATE_IDLE:
      case AseState::BTA_LE_AUDIO_ASE_STATE_RELEASING:
        // Do nothing here, just print an error message
        LOG_ERROR(
            "Ignore invalid attempt of state transition from %s to %s, %s, "
            "ase_id: %d",
            ToString(ase->state).c_str(),
            ToString(AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED).c_str(),
            ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_), ase->id);
        group->PrintDebugState();
        break;
      case AseState::BTA_LE_AUDIO_ASE_STATE_ENABLING:
        LOG_ERROR(
            "Invalid state transition from %s to %s, %s, ase_id: "
            "%d. Stopping the stream.",
            ToString(ase->state).c_str(),
            ToString(AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED).c_str(),
            ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_), ase->id);
        StopStream(group);
        break;
    }
+91 −2
Original line number Diff line number Diff line
@@ -695,7 +695,9 @@ class StateMachineTestBase : public Test {
        auto* p = notif_value.data();

        // Prepare header
        UINT8_TO_STREAM(p, ase->id);
        UINT8_TO_STREAM(p, ase->id == types::ase::kAseIdInvalid
                               ? ++ase_id_last_assigned
                               : ase->id);
        UINT8_TO_STREAM(p, new_state);

        UINT8_TO_STREAM(p, conf->cig_id);
@@ -726,7 +728,10 @@ class StateMachineTestBase : public Test {
        auto* p = notif_value.data();

        // Prepare header
        UINT8_TO_STREAM(p, ase->id);
        UINT8_TO_STREAM(p, ase->id == types::ase::kAseIdInvalid
                               ? ++ase_id_last_assigned
                               : ase->id);

        UINT8_TO_STREAM(p, new_state);

        UINT8_TO_STREAM(p, group->group_id_);
@@ -828,6 +833,27 @@ class StateMachineTestBase : public Test {
    }
  }

  void InjectInitialInvalidNotification(LeAudioDeviceGroup* group) {
    for (auto* device = group->GetFirstDevice(); device != nullptr;
         device = group->GetNextDevice(device)) {
      int i = 0;
      for (auto& ase : device->ases_) {
        if (i % 2 == 1) {
          client_parser::ascs::ase_qos_configured_state_params
              qos_configured_state_params;
          InjectAseStateNotification(&ase, device, group,
                                     ascs::kAseStateQoSConfigured,
                                     &qos_configured_state_params);
        } else {
          client_parser::ascs::ase_transient_state_params enable_params;
          InjectAseStateNotification(&ase, device, group,
                                     ascs::kAseStateEnabling, &enable_params);
        }
        i++;
      }
    }
  }

  void MultipleTestDevicePrepare(int leaudio_group_id,
                                 LeAudioContextType context_type,
                                 uint16_t device_cnt,
@@ -3929,6 +3955,69 @@ TEST_F(StateMachineTest, testHandlingAutonomousCodecConfigStateOnConnection) {
  testing::Mock::VerifyAndClearExpectations(&mock_callbacks_);
}

TEST_F(StateMachineTest, testHandlingInvalidRemoteAseStateHandling) {
  /* Scenario
   * 1. After connection remote device has different ASE configurations
   * 2. Try to start stream and make sure it is configured well.
   */

  const auto context_type = kContextTypeConversational;
  const int leaudio_group_id = 4;
  const int num_of_devices = 2;

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

  auto* firstDevice = group->GetFirstDevice();
  auto* secondDevice = group->GetNextDevice(firstDevice);

  /* Since we prepared device with Conversional context in mind, Sink and Source
   * ASEs should have been configured.
   */
  PrepareConfigureCodecHandler(group, 0, true);
  PrepareConfigureQosHandler(group);
  PrepareEnableHandler(group);
  PrepareDisableHandler(group);
  PrepareReceiverStartReadyHandler(group);
  PrepareReceiverStopReady(group);

  /* Number of control point calls
   * 1. Codec Config
   * 2. QoS Config
   * 3. Enable
   * 4. Receiver Start Ready
   */
  EXPECT_CALL(gatt_queue, WriteCharacteristic(firstDevice->conn_id_,
                                              firstDevice->ctp_hdls_.val_hdl, _,
                                              GATT_WRITE_NO_RSP, _, _))
      .Times(4);

  EXPECT_CALL(gatt_queue, WriteCharacteristic(secondDevice->conn_id_,
                                              secondDevice->ctp_hdls_.val_hdl,
                                              _, GATT_WRITE_NO_RSP, _, _))
      .Times(4);

  /* Inject invalid states*/
  InjectInitialInvalidNotification(group);

  ASSERT_FALSE(group->IsInTransition());

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

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

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

TEST_F(StateMachineTest, testHandlingCachedCodecConfig2Devices) {
  const auto context_type = kContextTypeConversational;
  const int leaudio_group_id = 4;