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

Commit 46dabbd1 authored by Grzegorz Kolodziejczyk's avatar Grzegorz Kolodziejczyk Committed by Gerrit Code Review
Browse files

Merge "le_audio: Protect from unreliable autonomous disable operation" into main

parents f0e5bee8 80260f99
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -518,6 +518,12 @@ class LeAudioClientImpl : public LeAudioClient {
    } while (leAudioDevice);
  }

  void OnDeviceAutonomousStateTransitionTimeout(LeAudioDevice* leAudioDevice) {
    LOG_ERROR("Device %s, failed to complete autonomous transition",
              ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
    DisconnectDevice(leAudioDevice, true);
  }

  void UpdateLocationsAndContextsAvailability(LeAudioDeviceGroup* group) {
    bool group_conf_changed = group->ReloadAudioLocations();
    group_conf_changed |= group->ReloadAudioDirections();
@@ -5530,6 +5536,12 @@ class CallbacksImpl : public LeAudioGroupStateMachine::Callbacks {
    if (instance) instance->OnLeAudioDeviceSetStateTimeout(group_id);
  }

  void OnDeviceAutonomousStateTransitionTimeout(
      LeAudioDevice* leAudioDevice) override {
    if (instance)
      instance->OnDeviceAutonomousStateTransitionTimeout(leAudioDevice);
  }

  void OnUpdatedCisConfiguration(int group_id, uint8_t direction) {
    if (instance) instance->OnUpdatedCisConfiguration(group_id, direction);
  }
+8 −0
Original line number Diff line number Diff line
@@ -335,6 +335,9 @@ void LeAudioDevice::ClearPACs(void) {

LeAudioDevice::~LeAudioDevice(void) {
  alarm_free(link_quality_timer);
  for (auto& ase : ases_) {
    alarm_free(ase.autonomous_operation_timer_);
  }
  this->ClearPACs();
}

@@ -946,6 +949,11 @@ void LeAudioDevice::DeactivateAllAses(void) {
          ase.cis_conn_hdl, bluetooth::common::ToString(ase.cis_state).c_str(),
          bluetooth::common::ToString(ase.data_path_state).c_str());
    }
    if (alarm_is_scheduled(ase.autonomous_operation_timer_)) {
      alarm_free(ase.autonomous_operation_timer_);
      ase.autonomous_operation_timer_ = NULL;
      ase.autonomous_target_state_ = AseState::BTA_LE_AUDIO_ASE_STATE_IDLE;
    }
    ase.state = AseState::BTA_LE_AUDIO_ASE_STATE_IDLE;
    ase.cis_state = CisState::IDLE;
    ase.data_path_state = DataPathState::IDLE;
+7 −0
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@

#include "bta_le_audio_uuids.h"
#include "btm_iso_api_types.h"
#include "osi/include/alarm.h"

namespace le_audio {

@@ -722,6 +723,8 @@ struct ase {
        pres_delay_max(0),
        preferred_pres_delay_min(0),
        preferred_pres_delay_max(0),
        autonomous_operation_timer_(nullptr),
        autonomous_target_state_(AseState::BTA_LE_AUDIO_ASE_STATE_IDLE),
        state(AseState::BTA_LE_AUDIO_ASE_STATE_IDLE) {}

  struct hdl_pair hdls;
@@ -761,6 +764,10 @@ struct ase {

  std::vector<uint8_t> metadata;

  /* Autonomous change data */
  alarm_t* autonomous_operation_timer_;
  types::AseState autonomous_target_state_;

  AseState state;
};

+58 −2
Original line number Diff line number Diff line
@@ -113,6 +113,7 @@ using le_audio::types::LeAudioContextType;
namespace {

constexpr int linkQualityCheckInterval = 4000;
constexpr int kAutonomousTransitionTimeoutMs = 5000;

static void link_quality_cb(void* data) {
  // very ugly, but we need to pass just two bytes
@@ -2075,6 +2076,12 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine {
        SetAseState(leAudioDevice, ase,
                    AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED);

        /* Remote may autonomously bring ASEs to QoS configured state */
        if (group->GetTargetState() !=
            AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED) {
          ProcessAutonomousDisable(leAudioDevice, ase);
        }

        /* Process the Disable Transition of the rest of group members if no
         * more ASE notifications has to come from this device. */
        if (leAudioDevice->IsReadyToSuspendStream()) ProcessGroupDisable(group);
@@ -2670,6 +2677,22 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine {
    }
  }

  void ScheduleAutonomousOperationTimer(AseState target_state,
                                        LeAudioDevice* leAudioDevice,
                                        struct ase* ase) {
    ase->autonomous_target_state_ = target_state;
    ase->autonomous_operation_timer_ =
        alarm_new("LeAudioAutonomousOperationTimeout");
    alarm_set_on_mloop(
        ase->autonomous_operation_timer_, kAutonomousTransitionTimeoutMs,
        [](void* data) {
          LeAudioDevice* leAudioDevice = static_cast<LeAudioDevice*>(data);
          instance->state_machine_callbacks_
              ->OnDeviceAutonomousStateTransitionTimeout(leAudioDevice);
        },
        leAudioDevice);
  }

  void AseStateMachineProcessDisabling(
      struct le_audio::client_parser::ascs::ase_rsp_hdr& arh, struct ase* ase,
      LeAudioDeviceGroup* group, LeAudioDevice* leAudioDevice) {
@@ -2696,10 +2719,15 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine {
        SetAseState(leAudioDevice, ase,
                    AseState::BTA_LE_AUDIO_ASE_STATE_DISABLING);

        /* Remote may autonomously bring ASEs to QoS configured state */
        if (group->GetTargetState() !=
            AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED) {
          ProcessAutonomousDisable(leAudioDevice, ase);
        }

        /* Process the Disable Transition of the rest of group members if no
         * more ASE notifications has to come from this device. */
        if (leAudioDevice->IsReadyToSuspendStream())
          ProcessGroupDisable(group);
        if (leAudioDevice->IsReadyToSuspendStream()) ProcessGroupDisable(group);

        break;

@@ -2895,6 +2923,34 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine {
      StopStream(group);
    }
  }

  void ProcessAutonomousDisable(LeAudioDevice* leAudioDevice, struct ase* ase) {
    auto bidirection_ase = leAudioDevice->GetAseToMatchBidirectionCis(ase);

    /* ASE is not a part of bi-directional CIS */
    if (!bidirection_ase) return;

    /* ASE is already disabled */
    if (bidirection_ase->state ==
        AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED) {
      /* Bi-direction ASEs are now disabled */
      if ((ase->autonomous_target_state_ ==
           AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED) &&
          alarm_is_scheduled(ase->autonomous_operation_timer_)) {
        alarm_free(ase->autonomous_operation_timer_);
        ase->autonomous_operation_timer_ = NULL;
        ase->autonomous_target_state_ = AseState::BTA_LE_AUDIO_ASE_STATE_IDLE;
      }
      return;
    }

    /* Schedule alarm if first ASE is autonomously disabling */
    if (!alarm_is_scheduled(bidirection_ase->autonomous_operation_timer_)) {
      ScheduleAutonomousOperationTimer(
          AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED, leAudioDevice,
          bidirection_ase);
    }
  }
};
}  // namespace

+2 −0
Original line number Diff line number Diff line
@@ -39,6 +39,8 @@ class LeAudioGroupStateMachine {
        int group_id, bluetooth::le_audio::GroupStreamStatus status) = 0;
    virtual void OnStateTransitionTimeout(int group_id) = 0;
    virtual void OnUpdatedCisConfiguration(int group_id, uint8_t direction) = 0;
    virtual void OnDeviceAutonomousStateTransitionTimeout(
        LeAudioDevice* leAudioDevice) = 0;
  };

  virtual ~LeAudioGroupStateMachine() = default;
Loading