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

Commit 49e12a47 authored by Jakub Pawłowski's avatar Jakub Pawłowski Committed by Gerrit Code Review
Browse files

Merge changes Ibe995a68,I2a2bef79,I05cca4c2

* changes:
  leaudio: Update audio session if not streaming
  leaudio: Add time tracking for setting up the stream in dumpsys
  leaudio: Improve handling audio driver requestes
parents 0b53f5e2 95fc2b76
Loading
Loading
Loading
Loading
+250 −126
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@
#include "btm_iso_api.h"
#include "client_audio.h"
#include "client_parser.h"
#include "common/time_util.h"
#include "device/include/controller.h"
#include "devices.h"
#include "embdrv/lc3/Api/Lc3Decoder.hpp"
@@ -80,6 +81,8 @@ enum class AudioState {
  IDLE = 0x00,
  READY_TO_START,
  STARTED,
  READY_TO_RELEASE,
  RELEASING,
};

std::ostream& operator<<(std::ostream& os, const AudioState& audio_state) {
@@ -93,6 +96,12 @@ std::ostream& operator<<(std::ostream& os, const AudioState& audio_state) {
    case AudioState::STARTED:
      os << "STARTED";
      break;
    case AudioState::READY_TO_RELEASE:
      os << "READY_TO_RELEASE";
      break;
    case AudioState::RELEASING:
      os << "RELEASING";
      break;
    default:
      os << "UNKNOWN";
      break;
@@ -154,9 +163,10 @@ class LeAudioClientImpl : public LeAudioClient {
      : gatt_if_(0),
        callbacks_(callbacks_),
        active_group_id_(bluetooth::groups::kGroupUnknown),
        stream_request_started_(false),
        current_context_type_(LeAudioContextType::MEDIA),
        upcoming_context_type_(LeAudioContextType::MEDIA),
        stream_setup_start_timestamp_(0),
        stream_setup_end_timestamp_(0),
        audio_receiver_state_(AudioState::IDLE),
        audio_sender_state_(AudioState::IDLE),
        current_source_codec_config({0, 0, 0, 0}),
@@ -284,6 +294,8 @@ class LeAudioClientImpl : public LeAudioClient {

    /* Releasement didn't finished in time */
    if (group->GetTargetState() == AseState::BTA_LE_AUDIO_ASE_STATE_IDLE) {
      audio_sender_state_ = AudioState::IDLE;
      audio_receiver_state_ = AudioState::IDLE;
      LeAudioDevice* leAudioDevice = group->GetFirstActiveDevice();
      LOG_ASSERT(leAudioDevice)
          << __func__ << " Shouldn't be called without an active device.";
@@ -511,21 +523,19 @@ class LeAudioClientImpl : public LeAudioClient {
    group_remove_node(group, address, true);
  }

  void GroupStream(const int group_id, const uint16_t context_type) override {
  bool InternalGroupStream(const int group_id, const uint16_t context_type) {
    LeAudioDeviceGroup* group = aseGroups_.FindById(group_id);
    auto final_context_type = context_type;

    if (context_type >= static_cast<uint16_t>(LeAudioContextType::RFU)) {
      LOG(ERROR) << __func__ << ", stream context type is not supported: "
                 << loghex(context_type);
      CancelStreamingRequest();
      return;
      return false;
    }

    if (!group) {
      LOG(ERROR) << __func__ << ", unknown group id: " << group_id;
      CancelStreamingRequest();
      return;
      return false;
    }

    auto supported_context_type = group->GetActiveContexts();
@@ -538,23 +548,27 @@ class LeAudioClientImpl : public LeAudioClient {

    if (!group->IsAnyDeviceConnected()) {
      LOG(ERROR) << __func__ << ", group " << group_id << " is not connected ";
      CancelStreamingRequest();
      return;
      return false;
    }

    /* Check if any group is in the transition state. If so, we don't allow to
     * start new group to stream */
    if (aseGroups_.IsAnyInTransition()) {
      LOG(INFO) << __func__ << " some group is already in the transition state";
      CancelStreamingRequest();
      return;
      return false;
    }

    if (groupStateMachine_->StartStream(
            group, static_cast<LeAudioContextType>(final_context_type)))
      stream_request_started_ = true;
    else
      ClientAudioIntefraceRelease();
    bool result = groupStateMachine_->StartStream(
        group, static_cast<LeAudioContextType>(final_context_type));
    if (result)
      stream_setup_start_timestamp_ =
          bluetooth::common::time_get_os_boottime_us();

    return result;
  }

  void GroupStream(const int group_id, const uint16_t context_type) override {
    InternalGroupStream(group_id, context_type);
  }

  void GroupSuspend(const int group_id) override {
@@ -583,9 +597,6 @@ class LeAudioClientImpl : public LeAudioClient {
      return;
    }

    audio_sender_state_ = AudioState::IDLE;
    audio_receiver_state_ = AudioState::IDLE;

    groupStateMachine_->SuspendStream(group);
  }

@@ -1692,9 +1703,10 @@ class LeAudioClientImpl : public LeAudioClient {
    auto* stream_conf = &group->stream_conf;

    if (!stream_conf->valid) {
      LOG(ERROR) << __func__
                 << " Configuration not valid. (btw not sure we need this "
                    "flag)";
      LOG(INFO) << __func__
                 << " Device not streaming but active. Lets update audio "
                    "session to match needed channel number";
      UpdateCurrentHalSessions(active_group_id_, current_context_type_);
      return;
    }

@@ -2290,6 +2302,10 @@ class LeAudioClientImpl : public LeAudioClient {
    dprintf(fd, "  Active group: %d\n", active_group_id_);
    dprintf(fd, "    current content type: 0x%08hx\n", current_context_type_);
    dprintf(fd, "    upcoming content type: 0x%08hx\n", upcoming_context_type_);
    dprintf(
        fd, "    stream setup time if started: %d ms\n",
        (int)((stream_setup_end_timestamp_ - stream_setup_start_timestamp_) /
              1000));
    printCurrentStreamConfiguration(fd);
    dprintf(fd, "  ----------------\n ");
    dprintf(fd, "  LE Audio Groups:\n");
@@ -2385,27 +2401,20 @@ class LeAudioClientImpl : public LeAudioClient {
    current_context_type_ = upcoming_context_type_;
  }

  void OnAudioResume() {
    if (active_group_id_ == bluetooth::groups::kGroupUnknown) {
      LOG(WARNING) << ", cannot start straming if no active group set";
      return;
    }

    auto group = aseGroups_.FindById(active_group_id_);
    if (!group) {
      LOG(ERROR) << __func__
                 << ", Invalid group: " << static_cast<int>(active_group_id_);
      return;
  bool OnAudioResume(LeAudioDeviceGroup* group) {
    if (upcoming_context_type_ != current_context_type_) {
      return false;
    }

    if (upcoming_context_type_ != current_context_type_) {
      /* Wait until session is updated */
      CancelStreamingRequest();
      return;
    /* Even context type is same, we might need more audio channels e.g. because
     * new device got connected */
    if (ReconfigureHalSessionIfNeeded(group, current_context_type_)) {
      return false;
    }

    /* TODO check if group already started streaming */
    GroupStream(active_group_id_, static_cast<uint16_t>(current_context_type_));
    return InternalGroupStream(active_group_id_,
                               static_cast<uint16_t>(current_context_type_));
  }

  void OnAudioSuspend() {
@@ -2418,18 +2427,38 @@ class LeAudioClientImpl : public LeAudioClient {
  }

  void OnAudioSinkSuspend() {
    LOG(INFO) << __func__;
    DLOG(INFO) << __func__
               << " IN: audio_receiver_state_: " << audio_receiver_state_
               << " audio_sender_state_: " << audio_sender_state_;

    /* Note: This callback is from audio hal driver.
     * Bluetooth peer is a Sink for Audio Framework.
     * e.g. Peer is a speaker
     */
    if (audio_sender_state_ == AudioState::IDLE) return;

    audio_sender_state_ = AudioState::IDLE;
    switch (audio_sender_state_) {
      case AudioState::READY_TO_START:
      case AudioState::STARTED:
        audio_sender_state_ = AudioState::READY_TO_RELEASE;
        break;
      case AudioState::RELEASING:
        return;
      case AudioState::IDLE:
        if (audio_receiver_state_ == AudioState::READY_TO_RELEASE) {
          OnAudioSuspend();
        }
        return;
      case AudioState::READY_TO_RELEASE:
        break;
    }

    /* Last suspends group - triggers group stop */
    if (audio_receiver_state_ == AudioState::IDLE) OnAudioSuspend();
    if ((audio_receiver_state_ == AudioState::IDLE) ||
        (audio_receiver_state_ == AudioState::READY_TO_RELEASE))
      OnAudioSuspend();

    DLOG(INFO) << __func__
               << " OUT: audio_receiver_state_: " << audio_receiver_state_
               << " audio_sender_state_: " << audio_sender_state_;
  }

  void OnAudioSinkResume() {
@@ -2455,49 +2484,93 @@ class LeAudioClientImpl : public LeAudioClient {
      return;
    }

    /* First resume request from sink/source triggers group start */
    if (audio_receiver_state_ == AudioState::IDLE &&
        audio_sender_state_ == AudioState::IDLE) {
      DLOG(INFO) << __func__ << " audio_sender_state_ READY_TO_START";
      audio_sender_state_ = AudioState::READY_TO_START;
      OnAudioResume();

      return;
    }

    if (audio_receiver_state_ >= AudioState::READY_TO_START) {
      LOG(INFO) << __func__ << " audio_receiver_state_ is READY_TO_START";
      audio_sender_state_ = AudioState::READY_TO_START;
      /* If signalling part is completed trigger start reveivin audio here,
       * otherwise it'll be called on group streaming state callback
       */
      if (group->GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING)
        StartSendingAudio(active_group_id_);
    } else {
      /* Ask framework to come back later */
    DLOG(INFO) << __func__ << " active_group_id: " << active_group_id_ << "\n"
               << " audio_receiver_state: " << audio_receiver_state_ << "\n"
               << " audio_sender_state: " << audio_sender_state_ << "\n"
               << " current_context_type_: "
               << static_cast<int>(current_context_type_) << "\n"
                 << " group exist? " << (group ? " yes " : " no ") << "\n";
      CancelStreamingRequest();
               << " upcoming_context_type_: "
               << static_cast<int>(upcoming_context_type_) << "\n"
               << " group " << (group ? " exist " : " does not exist ") << "\n";

    switch (audio_sender_state_) {
      case AudioState::STARTED:
        /* Looks like previous Confirm did not get to the Audio Framework*/
        LeAudioClientAudioSource::ConfirmStreamingRequest();
        break;
      case AudioState::IDLE:
        if (audio_receiver_state_ == AudioState::IDLE) {
          /* Stream is not started. Try to do it.*/
          if (OnAudioResume(group)) {
            audio_sender_state_ = AudioState::READY_TO_START;
          } else {
            LeAudioClientAudioSource::CancelStreamingRequest();
          }
        } else {
          /* Stream has been started by the Source. */
          audio_sender_state_ = AudioState::READY_TO_START;
          if (group->GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
            StartSendingAudio(active_group_id_);
          } else {
            LeAudioClientAudioSource::CancelStreamingRequest();
          }
        }
        break;
      case AudioState::READY_TO_START:
        LOG(WARNING) << __func__
                     << " called in wrong state. \n audio_receiver_state: "
                     << audio_receiver_state_ << "\n"
                     << " audio_sender_state: " << audio_sender_state_ << "\n";
        break;
      case AudioState::READY_TO_RELEASE:
        if (audio_receiver_state_ == AudioState::STARTED) {
          /* Stream is up just restore it */
          audio_sender_state_ = AudioState::STARTED;
          LeAudioClientAudioSource::ConfirmStreamingRequest();
          return;
        }
        LeAudioClientAudioSource::CancelStreamingRequest();
        break;
      case AudioState::RELEASING:
        /* Keep wainting */
        LeAudioClientAudioSource::CancelStreamingRequest();
        break;
    }
  }

  void OnAudioSourceSuspend() {
    LOG(INFO) << __func__;
    DLOG(INFO) << __func__
               << " IN: audio_receiver_state_: " << audio_receiver_state_
               << " audio_sender_state_: " << audio_sender_state_;

    /* Note: This callback is from audio hal driver.
     * Bluetooth peer is a Source for Audio Framework.
     * e.g. Peer is microphone.
     */
    if (audio_receiver_state_ == AudioState::IDLE) return;

    audio_receiver_state_ = AudioState::IDLE;
    switch (audio_receiver_state_) {
      case AudioState::READY_TO_START:
      case AudioState::STARTED:
        audio_receiver_state_ = AudioState::READY_TO_RELEASE;
        break;
      case AudioState::RELEASING:
        return;
      case AudioState::IDLE:
        if (audio_sender_state_ == AudioState::READY_TO_RELEASE) {
          OnAudioSuspend();
        }
        return;
      case AudioState::READY_TO_RELEASE:
        break;
    }

    /* Last suspends group - triggers group stop */
    if (audio_sender_state_ == AudioState::IDLE) OnAudioSuspend();
    if ((audio_sender_state_ == AudioState::IDLE) ||
        (audio_sender_state_ == AudioState::READY_TO_RELEASE))
      OnAudioSuspend();

    DLOG(INFO) << __func__
               << " OUT: audio_receiver_state_: " << audio_receiver_state_
               << " audio_sender_state_: " << audio_sender_state_;
  }

  void OnAudioSourceResume() {
@@ -2523,22 +2596,57 @@ class LeAudioClientImpl : public LeAudioClient {
      return;
    }

    /* First resume request from sink/source triggers group start */
    if ((audio_receiver_state_ == AudioState::IDLE) &&
        (audio_sender_state_ == AudioState::IDLE)) {
      OnAudioResume();
      audio_receiver_state_ = AudioState::READY_TO_START;
    DLOG(INFO) << __func__ << " active_group_id: " << active_group_id_ << "\n"
               << " audio_receiver_state: " << audio_receiver_state_ << "\n"
               << " audio_sender_state: " << audio_sender_state_ << "\n"
               << " current_context_type_: "
               << static_cast<int>(current_context_type_) << "\n"
               << " upcoming_context_type_: "
               << static_cast<int>(upcoming_context_type_) << "\n"
               << " group " << (group ? " exist " : " does not exist ") << "\n";

      return;
    switch (audio_receiver_state_) {
      case AudioState::STARTED:
        LeAudioClientAudioSink::ConfirmStreamingRequest();
        break;
      case AudioState::IDLE:
        if (audio_sender_state_ == AudioState::IDLE) {
          if (OnAudioResume(group)) {
            audio_receiver_state_ = AudioState::READY_TO_START;
          } else {
            LeAudioClientAudioSink::CancelStreamingRequest();
          }

    if (audio_sender_state_ >= AudioState::READY_TO_START) {
        } else {
          audio_receiver_state_ = AudioState::READY_TO_START;
          /* If signalling part is completed trigger start reveivin audio here,
           * otherwise it'll be called on group streaming state callback
           */
      if (group->GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING)
          if (group->GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
            StartReceivingAudio(active_group_id_);
          } else {
            LeAudioClientAudioSink::CancelStreamingRequest();
          }
        }
        break;
      case AudioState::READY_TO_START:
        LOG(WARNING) << __func__
                     << " called in wrong state. \n audio_receiver_state: "
                     << audio_receiver_state_ << "\n"
                     << " audio_sender_state: " << audio_sender_state_ << "\n";
        break;
      case AudioState::READY_TO_RELEASE:
        if (audio_sender_state_ == AudioState::STARTED) {
          /* Just return to started */
          audio_receiver_state_ = AudioState::STARTED;
          LeAudioClientAudioSink::ConfirmStreamingRequest();
          return;
        }

        LeAudioClientAudioSink::CancelStreamingRequest();
        break;
      case AudioState::RELEASING:
        LeAudioClientAudioSink::CancelStreamingRequest();
        break;
    }
  }

@@ -2617,6 +2725,40 @@ class LeAudioClientImpl : public LeAudioClient {
    return available_contents[0];
  }

  bool ReconfigureHalSessionIfNeeded(LeAudioDeviceGroup* group,
                                     LeAudioContextType new_context_type) {
    std::optional<LeAudioCodecConfiguration> source_configuration =
        group->GetCodecConfigurationByDirection(
            new_context_type, le_audio::types::kLeAudioDirectionSink);

    std::optional<LeAudioCodecConfiguration> sink_configuration =
        group->GetCodecConfigurationByDirection(
            new_context_type, le_audio::types::kLeAudioDirectionSource);

    if ((source_configuration &&
         (*source_configuration != current_source_codec_config)) ||
        (sink_configuration &&
         (*sink_configuration != current_sink_codec_config))) {
      LOG(INFO) << __func__
                << " Session reconfiguration needed group: " << group->group_id_
                << "for context type: " << static_cast<int>(new_context_type);

      if (group->GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
        GroupStop(group->group_id_);
      }

      upcoming_context_type_ = new_context_type;
      /* Schedule HAL Session update */
      do_in_main_thread(FROM_HERE,
                        base::Bind(&LeAudioClientImpl::UpdateCurrentHalSessions,
                                   base::Unretained(instance), group->group_id_,
                                   upcoming_context_type_));
      return true;
    }

    return false;
  }

  void OnAudioMetadataUpdate(const source_metadata_t& source_metadata) {
    auto tracks = source_metadata.tracks;
    auto track_count = source_metadata.track_count;
@@ -2668,40 +2810,15 @@ class LeAudioClientImpl : public LeAudioClient {
      return;
    }

    std::optional<LeAudioCodecConfiguration> source_configuration =
        group->GetCodecConfigurationByDirection(
            new_context, le_audio::types::kLeAudioDirectionSink);

    std::optional<LeAudioCodecConfiguration> sink_configuration =
        group->GetCodecConfigurationByDirection(
            new_context, le_audio::types::kLeAudioDirectionSource);

    if ((source_configuration &&
         (*source_configuration != current_source_codec_config)) ||
        (sink_configuration &&
         (*sink_configuration != current_sink_codec_config))) {
      DLOG(INFO) << __func__ << " Will UpdateCurrentHalSessions group"
                 << group->group_id_ << "for context type: "
                 << static_cast<int>(upcoming_context_type_);

      if (group->GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
        GroupStop(group->group_id_);
    if (ReconfigureHalSessionIfNeeded(group, new_context)) {
      return;
    }

      upcoming_context_type_ = new_context;
      /* Schedule HAL Session update */
      do_in_main_thread(FROM_HERE,
                        base::Bind(&LeAudioClientImpl::UpdateCurrentHalSessions,
                                   base::Unretained(instance), group->group_id_,
                                   upcoming_context_type_));

    } else {
    /* Configuration is the same for new context, just will do update
     * metadata of stream
     */
    GroupStream(active_group_id_, static_cast<uint16_t>(new_context));
  }
  }

  static void OnGattReadRspStatic(uint16_t conn_id, tGATT_STATUS status,
                                  uint16_t hdl, uint16_t len, uint8_t* value,
@@ -2848,21 +2965,27 @@ class LeAudioClientImpl : public LeAudioClient {
  void StatusReportCb(int group_id, GroupStreamStatus status) {
    switch (status) {
      case GroupStreamStatus::STREAMING:
        stream_request_started_ = false;
        if (audio_sender_state_ == AudioState::READY_TO_START)
          StartSendingAudio(active_group_id_);
        if (audio_receiver_state_ == AudioState::READY_TO_START)
          StartReceivingAudio(active_group_id_);

        stream_setup_end_timestamp_ =
            bluetooth::common::time_get_os_boottime_us();
        break;
      case GroupStreamStatus::SUSPENDED:
        /** Stop Audio but don't release all the Audio resources */
        SuspendAudio();
        break;
      case GroupStreamStatus::IDLE:
        if (stream_request_started_) {
          stream_request_started_ = false;
        stream_setup_end_timestamp_ = 0;
        stream_setup_start_timestamp_ = 0;
        CancelStreamingRequest();
        }
        break;
      case GroupStreamStatus::RELEASING:
      case GroupStreamStatus::SUSPENDING:
        audio_sender_state_ = AudioState::RELEASING;
        audio_receiver_state_ = AudioState::RELEASING;
        break;
      default:
        break;
@@ -2876,9 +2999,10 @@ class LeAudioClientImpl : public LeAudioClient {
  LeAudioDeviceGroups aseGroups_;
  LeAudioGroupStateMachine* groupStateMachine_;
  int active_group_id_;
  bool stream_request_started_;
  LeAudioContextType current_context_type_;
  LeAudioContextType upcoming_context_type_;
  uint64_t stream_setup_start_timestamp_;
  uint64_t stream_setup_end_timestamp_;

  /* Microphone (s) */
  AudioState audio_receiver_state_;
+5 −7
Original line number Diff line number Diff line
@@ -963,14 +963,15 @@ class UnicastTestNoInit : public Test {
    UpdateMetadata(usage, content_type);

    if (reconfigured_sink) {
      EXPECT_CALL(mock_audio_source_, CancelStreamingRequest()).Times(1);
      audio_sink_receiver_->OnAudioResume();
      SyncOnMainLoop();
    }
    SyncOnMainLoop();
    audio_sink_receiver_->OnAudioResume();

    EXPECT_CALL(mock_audio_source_, ConfirmStreamingRequest()).Times(1);
    audio_sink_receiver_->OnAudioResume();
    state_machine_callbacks_->StatusReportCb(
              group_id, GroupStreamStatus::STREAMING);
    state_machine_callbacks_->StatusReportCb(group_id,
                                             GroupStreamStatus::STREAMING);

    if (usage == AUDIO_USAGE_VOICE_COMMUNICATION) {
      ASSERT_NE(audio_source_receiver_, nullptr);
@@ -2432,7 +2433,6 @@ TEST_F(UnicastTest, TwoEarbudsStreaming) {

  // Start streaming with reconfiguration from default media stream setup
  EXPECT_CALL(mock_audio_source_, Start(_, _)).Times(1);
  EXPECT_CALL(mock_audio_source_, CancelStreamingRequest()).Times(1);
  EXPECT_CALL(mock_audio_source_, Stop()).Times(1);
  EXPECT_CALL(mock_audio_sink_, Start(_, _)).Times(1);

@@ -2519,7 +2519,6 @@ TEST_F(UnicastTest, TwoEarbudsStreamingContextSwitchSimple) {
      StartStream(_, le_audio::types::LeAudioContextType::NOTIFICATIONS))
      .Times(1);
  EXPECT_CALL(mock_audio_source_, Start(_, _)).Times(1);
  EXPECT_CALL(mock_audio_source_, CancelStreamingRequest()).Times(1);
  EXPECT_CALL(mock_audio_source_, Stop()).Times(1);

  StartStreaming(AUDIO_USAGE_NOTIFICATION, AUDIO_CONTENT_TYPE_UNKNOWN, group_id,
@@ -2600,7 +2599,6 @@ TEST_F(UnicastTest, TwoEarbudsStreamingContextSwitchReconfigure) {

  // Start streaming
  EXPECT_CALL(mock_audio_source_, Start(_, _)).Times(1);
  EXPECT_CALL(mock_audio_source_, CancelStreamingRequest()).Times(1);
  EXPECT_CALL(mock_audio_source_, Stop()).Times(1);
  EXPECT_CALL(mock_audio_sink_, Start(_, _)).Times(1);
  LeAudioClient::Get()->GroupSetActive(group_id);
+6 −0
Original line number Diff line number Diff line
@@ -202,6 +202,8 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine {
    /* All ASEs should aim to achieve target state */
    SetTargetState(group, AseState::BTA_LE_AUDIO_ASE_STATE_QOS_CONFIGURED);
    PrepareAndSendDisable(leAudioDevice);
    state_machine_callbacks_->StatusReportCb(group->group_id_,
                                             GroupStreamStatus::SUSPENDING);
  }

  void StopStream(LeAudioDeviceGroup* group) override {
@@ -214,12 +216,16 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine {
    if (leAudioDevice == nullptr) {
      LOG(ERROR) << __func__
                 << " Shouldn't be called without an active device.";
      state_machine_callbacks_->StatusReportCb(group->group_id_,
                                               GroupStreamStatus::IDLE);
      return;
    }

    /* All Ases should aim to achieve target state */
    SetTargetState(group, AseState::BTA_LE_AUDIO_ASE_STATE_IDLE);
    PrepareAndSendRelease(leAudioDevice);
    state_machine_callbacks_->StatusReportCb(group->group_id_,
                                             GroupStreamStatus::RELEASING);
  }

  void ProcessGattNotifEvent(uint8_t* value, uint16_t len, struct ase* ase,
+26 −0
Original line number Diff line number Diff line
@@ -1510,6 +1510,10 @@ TEST_F(StateMachineTest, testDisableSingle) {
            types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);

  // Validate GroupStreamStatus
  EXPECT_CALL(
      mock_callbacks_,
      StatusReportCb(leaudio_group_id,
                     bluetooth::le_audio::GroupStreamStatus::SUSPENDING));
  EXPECT_CALL(
      mock_callbacks_,
      StatusReportCb(leaudio_group_id,
@@ -1569,6 +1573,10 @@ TEST_F(StateMachineTest, testDisableMultiple) {
            types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);

  // Validate GroupStreamStatus
  EXPECT_CALL(
      mock_callbacks_,
      StatusReportCb(leaudio_group_id,
                     bluetooth::le_audio::GroupStreamStatus::SUSPENDING));
  EXPECT_CALL(
      mock_callbacks_,
      StatusReportCb(leaudio_group_id,
@@ -1668,6 +1676,10 @@ TEST_F(StateMachineTest, testReleaseSingle) {
            types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);

  // 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));
@@ -1711,6 +1723,11 @@ TEST_F(StateMachineTest, testReleaseCachingSingle) {
  InjectInitialIdleNotification(group);

  // 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));
@@ -1767,6 +1784,11 @@ TEST_F(StateMachineTest, testStreamCachingSingle) {
  InjectInitialIdleNotification(group);

  // 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));
@@ -1847,6 +1869,10 @@ TEST_F(StateMachineTest, testReleaseMultiple) {
            types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);

  // 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));
+2 −0
Original line number Diff line number Diff line
@@ -40,6 +40,8 @@ enum class GroupStatus {
enum class GroupStreamStatus {
  IDLE = 0,
  STREAMING,
  RELEASING,
  SUSPENDING,
  SUSPENDED,
  RECONFIGURED,
  DESTROYED,