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

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

Merge "leaudio: Do not trigger QoS config if device is not ready" into main

parents 2d318a59 a5133f49
Loading
Loading
Loading
Loading
+26 −2
Original line number Diff line number Diff line
@@ -1797,7 +1797,19 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine {

        if (group->GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
          /* We are here because of the reconnection of the single device. */
          /* Make sure that device is ready to be configured as we could also
           * get here triggered by the remote device. If device is not connected
           * yet, we should wait for the stack to trigger adding device to the
           * stream */
          if (leAudioDevice->GetConnectionState() ==
              le_audio::DeviceConnectState::CONNECTED) {
            PrepareAndSendConfigQos(group, leAudioDevice);
          } else {
            LOG_DEBUG(
                "Device %s initiated configured state but it is not yet ready "
                "to be configured",
                ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
          }
          return;
        }

@@ -1896,7 +1908,19 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine {

        if (group->GetState() == AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
          /* We are here because of the reconnection of the single device. */
          /* Make sure that device is ready to be configured as we could also
           * get here triggered by the remote device. If device is not connected
           * yet, we should wait for the stack to trigger adding device to the
           * stream */
          if (leAudioDevice->GetConnectionState() ==
              le_audio::DeviceConnectState::CONNECTED) {
            PrepareAndSendConfigQos(group, leAudioDevice);
          } else {
            LOG_DEBUG(
                "Device %s initiated configured state but it is not yet ready "
                "to be configured",
                ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_));
          }
          return;
        }

+147 −0
Original line number Diff line number Diff line
@@ -3942,6 +3942,153 @@ TEST_F(StateMachineTest, testAttachDeviceToTheStream) {
  ASSERT_NE(ase->retrans_nb, 0);
}

TEST_F(StateMachineTest, testAutonomousConfiguredAndAttachToStream) {
  const auto context_type = kContextTypeMedia;
  const auto leaudio_group_id = 6;
  const auto num_devices = 2;

  /* Scenario
   * 1. Start streaming
   * 2. Stop stream on one device
   * 3. Reconnect
   * 4. Autonomous Configured state
   * 5. Make sure QoS Configure is not send out
   * 6. Trigger attach the stream
   * 7. Make sure stream is up
   */

  ContentControlIdKeeper::GetInstance()->SetCcid(media_context, media_ccid);

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

  PrepareConfigureCodecHandler(group, 0, true);
  PrepareConfigureQosHandler(group);
  PrepareEnableHandler(group);
  PrepareDisableHandler(group);
  PrepareReleaseHandler(group);

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

  auto expected_devices_written = 0;
  while (leAudioDevice) {
    /* Three Writes:
     * 1: Codec Config
     * 2: Codec QoS
     * 3: Enabling
     */
    lastDevice = leAudioDevice;
    EXPECT_CALL(gatt_queue,
                WriteCharacteristic(leAudioDevice->conn_id_,
                                    leAudioDevice->ctp_hdls_.val_hdl, _,
                                    GATT_WRITE_NO_RSP, _, _))
        .Times(AtLeast(3));
    expected_devices_written++;
    leAudioDevice = group->GetNextDevice(leAudioDevice);
  }
  ASSERT_EQ(expected_devices_written, num_devices);

  EXPECT_CALL(*mock_iso_manager_, CreateCig(_, _)).Times(1);
  EXPECT_CALL(*mock_iso_manager_, EstablishCis(_)).Times(1);
  EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath(_, _)).Times(2);

  InjectInitialIdleNotification(group);

  // Start the configuration and stream Media content
  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);
  testing::Mock::VerifyAndClearExpectations(&mock_iso_manager_);

  // Inject CIS and ACL disconnection of first device
  InjectCisDisconnected(group, lastDevice, HCI_ERR_CONNECTION_TOUT);
  InjectAclDisconnected(group, lastDevice);

  // Check if group keeps streaming
  ASSERT_EQ(group->GetState(),
            types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);

  /* Set device is getting ready for the connection */
  lastDevice->conn_id_ = 3;
  lastDevice->SetConnectionState(
      DeviceConnectState::CONNECTED_AUTOCONNECT_GETTING_READY);

  // Make sure ASE with disconnected CIS are not left in STREAMING
  ASSERT_EQ(lastDevice->GetFirstAseWithState(
                ::le_audio::types::kLeAudioDirectionSink,
                types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING),
            nullptr);
  ASSERT_EQ(lastDevice->GetFirstAseWithState(
                ::le_audio::types::kLeAudioDirectionSource,
                types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING),
            nullptr);

  // Symulate remote autonomous CONFIGURE state
  EXPECT_CALL(gatt_queue, WriteCharacteristic(lastDevice->conn_id_,
                                              lastDevice->ctp_hdls_.val_hdl, _,
                                              GATT_WRITE_NO_RSP, _, _))
      .Times(0);

  int num_of_notifications = 0;
  for (auto& ase : lastDevice->ases_) {
    if (ase.id == le_audio::types::ase::kAseIdInvalid) {
      continue;
    }
    LOG_ERROR("ID : %d,  status %s", ase.id,
              bluetooth::common::ToString(ase.state).c_str());
    num_of_notifications++;
    InjectAseStateNotification(&ase, lastDevice, group,
                               ascs::kAseStateCodecConfigured,
                               &cached_codec_configuration_map_[ase.id]);
    break;
  }
  ASSERT_EQ(num_of_notifications, 1);

  testing::Mock::VerifyAndClearExpectations(&gatt_queue);
  // Now device is connected. Attach it to the stream

  lastDevice->SetConnectionState(DeviceConnectState::CONNECTED);

  EXPECT_CALL(gatt_queue, WriteCharacteristic(lastDevice->conn_id_,
                                              lastDevice->ctp_hdls_.val_hdl, _,
                                              GATT_WRITE_NO_RSP, _, _))
      .Times(AtLeast(3));

  EXPECT_CALL(*mock_iso_manager_, EstablishCis(_)).Times(1);
  EXPECT_CALL(*mock_iso_manager_, SetupIsoDataPath(_, _)).Times(1);
  LeAudioGroupStateMachine::Get()->AttachToStream(
      group, lastDevice, {.sink = {media_ccid}, .source = {}});

  // Check if group keeps streaming
  ASSERT_EQ(group->GetState(),
            types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING);

  // Verify that the joining device receives the right CCID list
  auto lastMeta = lastDevice->GetFirstActiveAse()->metadata;
  bool parsedOk = false;
  auto ltv = le_audio::types::LeAudioLtvMap::Parse(lastMeta.data(),
                                                   lastMeta.size(), parsedOk);
  ASSERT_TRUE(parsedOk);

  auto ccids = ltv.Find(le_audio::types::kLeAudioMetadataTypeCcidList);
  ASSERT_TRUE(ccids.has_value());
  ASSERT_NE(std::find(ccids->begin(), ccids->end(), media_ccid), ccids->end());

  /* Verify that ASE of first device are still good*/
  auto ase = fistDevice->GetFirstActiveAse();
  ASSERT_NE(ase->max_transport_latency, 0);
  ASSERT_NE(ase->retrans_nb, 0);
}

TEST_F(StateMachineTest,
       testAttachDeviceToTheStream_autonomusQoSConfiguredState) {
  const auto context_type = kContextTypeMedia;
+4 −1
Original line number Diff line number Diff line
@@ -303,7 +303,10 @@ struct iso_impl {
      LOG_ASSERT(cis) << "No such cis: " << +el.cis_conn_handle;
      LOG_ASSERT(!(cis->state_flags &
                   (kStateFlagIsConnected | kStateFlagIsConnecting)))
          << "Already connected or connecting";
          << "cis: " << +el.cis_conn_handle
          << " is already connected or connecting flags: " << +cis->state_flags
          << ", num of cis params: " << +conn_params.conn_pairs.size();

      cis->state_flags |= kStateFlagIsConnecting;

      tBTM_SEC_DEV_REC* p_rec = btm_find_dev_by_handle(el.acl_conn_handle);
+1 −1
Original line number Diff line number Diff line
@@ -1028,7 +1028,7 @@ TEST_F(IsoManagerDeathTest, ConnectSameCisTwice) {
  ASSERT_EXIT(
      IsoManager::GetInstance()->IsoManager::GetInstance()->EstablishCis(
          params),
      ::testing::KilledBySignal(SIGABRT), "Already connected or connecting");
      ::testing::KilledBySignal(SIGABRT), "already connected or connecting");
}

TEST_F(IsoManagerDeathTest, EstablishCisInvalidResponsePacket) {