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

Commit cc80a791 authored by Łukasz Rymanowski's avatar Łukasz Rymanowski
Browse files

leaudio: Fix group IsInTransition

This should return true only when group is truly in transition, meaning
Target state was set by the stack and this is different from current
state.

Group is not in the transition, when after restart ASEs are autonomously
set to configured state.

Bug: 318709435
Test: atest bluetooth_le_audio_test
Tag: #feature
Flag: Exempt, LeAudio fix verified with unit tests. Also new test added.

Change-Id: I2b07283e3be24ab7e7512a007152c385be5a9743
parent f21ee1ca
Loading
Loading
Loading
Loading
+1 −3
Original line number Diff line number Diff line
@@ -832,9 +832,7 @@ bool LeAudioDeviceGroup::ReloadAudioDirections(void) {
  return true;
}

bool LeAudioDeviceGroup::IsInTransition(void) const {
  return target_state_ != current_state_;
}
bool LeAudioDeviceGroup::IsInTransition(void) const { return in_transition_; }

bool LeAudioDeviceGroup::IsStreaming(void) const {
  return current_state_ == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING;
+18 −5
Original line number Diff line number Diff line
@@ -110,7 +110,8 @@ class LeAudioDeviceGroup {
        pending_group_available_contexts_change_(
            types::LeAudioContextType::UNINITIALIZED),
        target_state_(types::AseState::BTA_LE_AUDIO_ASE_STATE_IDLE),
        current_state_(types::AseState::BTA_LE_AUDIO_ASE_STATE_IDLE) {
        current_state_(types::AseState::BTA_LE_AUDIO_ASE_STATE_IDLE),
        in_transition_(false) {
#ifdef __ANDROID__
    // 22 maps to BluetoothProfile#LE_AUDIO
    is_output_preference_le_audio = android::sysprop::BluetoothProperties::
@@ -227,13 +228,19 @@ class LeAudioDeviceGroup {

  inline types::AseState GetState(void) const { return current_state_; }
  void SetState(types::AseState state) {
    LOG(INFO) << __func__ << " current state: " << current_state_
              << " new state: " << state;
    LOG_INFO(" current state: %s, new state %s, in_transition_ %d",
             bluetooth::common::ToString(current_state_).c_str(),
             bluetooth::common::ToString(state).c_str(), in_transition_);
    LeAudioLogHistory::Get()->AddLogHistory(
        kLogStateMachineTag, group_id_, RawAddress::kEmpty, kLogStateChangedOp,
        bluetooth::common::ToString(current_state_) + "->" +
            bluetooth::common::ToString(state));
    current_state_ = state;

    if (target_state_ == current_state_) {
      in_transition_ = false;
      LOG_INFO("In transition flag cleared");
    }
  }

  inline types::AseState GetTargetState(void) const { return target_state_; }
@@ -244,14 +251,19 @@ class LeAudioDeviceGroup {
    return notify_streaming_when_cises_are_ready_;
  }
  void SetTargetState(types::AseState state) {
    LOG(INFO) << __func__ << " target state: " << target_state_
              << " new target state: " << state;
    LOG_INFO("target state: %s, new target state: %s, in_transition_ %d",
             bluetooth::common::ToString(target_state_).c_str(),
             bluetooth::common::ToString(state).c_str(), in_transition_);
    LeAudioLogHistory::Get()->AddLogHistory(
        kLogStateMachineTag, group_id_, RawAddress::kEmpty,
        kLogTargetStateChangedOp,
        bluetooth::common::ToString(target_state_) + "->" +
            bluetooth::common::ToString(state));

    target_state_ = state;

    in_transition_ = target_state_ != current_state_;
    LOG_INFO("In transition flag  = %d", in_transition_);
  }

  /* Returns context types for which support was recently added or removed */
@@ -396,6 +408,7 @@ class LeAudioDeviceGroup {

  types::AseState target_state_;
  types::AseState current_state_;
  bool in_transition_;
  std::vector<std::weak_ptr<LeAudioDevice>> leAudioDevices_;
};

+7 −3
Original line number Diff line number Diff line
@@ -1871,6 +1871,10 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine {
          /* This is autonomus change of the remote device */
          LOG_DEBUG("Autonomus change for device %s, ase id %d. Just store it.",
                    ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_), ase->id);

          /* Since at least one ASE is in configured state, we should admit
           * group is configured state */
          group->SetState(AseState::BTA_LE_AUDIO_ASE_STATE_CODEC_CONFIGURED);
          return;
        }

@@ -2044,7 +2048,7 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine {
          return;
        }

        LOG_ERROR(", Autonomouse change, from: %s to %s",
        LOG_INFO("Autonomous change, from: %s to %s",
                 ToString(group->GetState()).c_str(),
                 ToString(group->GetTargetState()).c_str());

+86 −0
Original line number Diff line number Diff line
@@ -808,6 +808,26 @@ class StateMachineTestBase : public Test {
    }
  }

  void InjectInitialIdleAndConfiguredNotification(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) {
          InjectAseStateNotification(&ase, device, group, ascs::kAseStateIdle,
                                     nullptr);
        } else {
          client_parser::ascs::ase_codec_configured_state_params
              codec_configured_state_params;
          InjectAseStateNotification(&ase, device, group,
                                     ascs::kAseStateCodecConfigured,
                                     &codec_configured_state_params);
        }
        i++;
      }
    }
  }

  void MultipleTestDevicePrepare(int leaudio_group_id,
                                 LeAudioContextType context_type,
                                 uint16_t device_cnt,
@@ -3843,6 +3863,72 @@ TEST_F(StateMachineTest, testAseAutonomousRelease2Devices) {
  }
}

TEST_F(StateMachineTest, testHandlingAutonomousCodecConfigStateOnConnection) {
  /* 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);

  InjectInitialIdleAndConfiguredNotification(group);
  // Call it second time to make sure we get into state that current_state_ is
  // different then target_state_ even group is not in transition.
  InjectInitialIdleAndConfiguredNotification(group);

  ASSERT_TRUE(group->GetTargetState() != group->GetState());
  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;