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

Commit 759a1a06 authored by Łukasz Rymanowski's avatar Łukasz Rymanowski Committed by Automerger Merge Worker
Browse files

Merge "le_audio: Clear stream configuration on a prior ACL disconnection" am: 39420fcc

parents 35eaad5b 39420fcc
Loading
Loading
Loading
Loading
+90 −0
Original line number Diff line number Diff line
@@ -1821,6 +1821,20 @@ bool LeAudioDeviceGroup::IsMetadataChanged(
  return false;
}

bool LeAudioDeviceGroup::IsCisPartOfCurrentStream(uint16_t cis_conn_hdl) {
  auto iter = std::find_if(
      stream_conf.sink_streams.begin(), stream_conf.sink_streams.end(),
      [cis_conn_hdl](auto& pair) { return cis_conn_hdl == pair.first; });

  if (iter != stream_conf.sink_streams.end()) return true;

  iter = std::find_if(
      stream_conf.source_streams.begin(), stream_conf.source_streams.end(),
      [cis_conn_hdl](auto& pair) { return cis_conn_hdl == pair.first; });

  return iter != stream_conf.sink_streams.end();
}

void LeAudioDeviceGroup::StreamOffloaderUpdated(uint8_t direction) {
  if (direction == le_audio::types::kLeAudioDirectionSource) {
    stream_conf.source_is_initial = false;
@@ -1829,6 +1843,82 @@ void LeAudioDeviceGroup::StreamOffloaderUpdated(uint8_t direction) {
  }
}

void LeAudioDeviceGroup::RemoveCisFromStreamIfNeeded(
    LeAudioDevice* leAudioDevice, uint16_t cis_conn_hdl) {
  LOG_INFO(" CIS Connection Handle: %d", cis_conn_hdl);

  if (!IsCisPartOfCurrentStream(cis_conn_hdl)) return;

  auto sink_channels = stream_conf.sink_num_of_channels;
  auto source_channels = stream_conf.source_num_of_channels;

  if (!stream_conf.sink_streams.empty() ||
      !stream_conf.source_streams.empty()) {
    stream_conf.sink_streams.erase(
        std::remove_if(
            stream_conf.sink_streams.begin(), stream_conf.sink_streams.end(),
            [leAudioDevice, &cis_conn_hdl, this](auto& pair) {
              if (!cis_conn_hdl) {
                cis_conn_hdl = pair.first;
              }
              auto ases_pair = leAudioDevice->GetAsesByCisConnHdl(cis_conn_hdl);
              if (ases_pair.sink && cis_conn_hdl == pair.first) {
                stream_conf.sink_num_of_devices--;
                stream_conf.sink_num_of_channels -=
                    ases_pair.sink->codec_config.channel_count;
                stream_conf.sink_audio_channel_allocation &= ~pair.second;
              }
              return (ases_pair.sink && cis_conn_hdl == pair.first);
            }),
        stream_conf.sink_streams.end());

    stream_conf.source_streams.erase(
        std::remove_if(
            stream_conf.source_streams.begin(),
            stream_conf.source_streams.end(),
            [leAudioDevice, &cis_conn_hdl, this](auto& pair) {
              if (!cis_conn_hdl) {
                cis_conn_hdl = pair.first;
              }
              auto ases_pair = leAudioDevice->GetAsesByCisConnHdl(cis_conn_hdl);
              if (ases_pair.source && cis_conn_hdl == pair.first) {
                stream_conf.source_num_of_devices--;
                stream_conf.source_num_of_channels -=
                    ases_pair.source->codec_config.channel_count;
                stream_conf.source_audio_channel_allocation &= ~pair.second;
              }
              return (ases_pair.source && cis_conn_hdl == pair.first);
            }),
        stream_conf.source_streams.end());

    LOG_INFO(
        " Sink Number Of Devices: %d"
        ", Sink Number Of Channels: %d"
        ", Source Number Of Devices: %d"
        ", Source Number Of Channels: %d",
        stream_conf.sink_num_of_devices, stream_conf.sink_num_of_channels,
        stream_conf.source_num_of_devices, stream_conf.source_num_of_channels);
  }

  if (stream_conf.sink_num_of_channels == 0) {
    ClearSinksFromConfiguration();
  }

  if (stream_conf.source_num_of_channels == 0) {
    ClearSourcesFromConfiguration();
  }

  /* Update offloader streams if needed */
  if (sink_channels > stream_conf.sink_num_of_channels) {
    CreateStreamVectorForOffloader(le_audio::types::kLeAudioDirectionSink);
  }
  if (source_channels > stream_conf.source_num_of_channels) {
    CreateStreamVectorForOffloader(le_audio::types::kLeAudioDirectionSource);
  }

  CigUnassignCis(leAudioDevice);
}

void LeAudioDeviceGroup::CreateStreamVectorForOffloader(uint8_t direction) {
  if (CodecManager::GetInstance()->GetCodecLocation() !=
      le_audio::types::CodecLocation::ADSP) {
+3 −0
Original line number Diff line number Diff line
@@ -385,6 +385,8 @@ class LeAudioDeviceGroup {
  void CreateStreamVectorForOffloader(uint8_t direction);
  void StreamOffloaderUpdated(uint8_t direction);
  bool IsConfiguredForContext(types::LeAudioContextType context_type);
  void RemoveCisFromStreamIfNeeded(LeAudioDevice* leAudioDevice,
                                   uint16_t cis_conn_hdl);

  inline types::AseState GetState(void) const { return current_state_; }
  void SetState(types::AseState state) {
@@ -461,6 +463,7 @@ class LeAudioDeviceGroup {
      types::LeAudioContextType context_type,
      types::LeAudioConfigurationStrategy required_snk_strategy);
  uint32_t GetTransportLatencyUs(uint8_t direction);
  bool IsCisPartOfCurrentStream(uint16_t cis_conn_hdl);

  /* Current configuration and metadata context types */
  types::LeAudioContextType configuration_context_type_;
+13 −89
Original line number Diff line number Diff line
@@ -583,7 +583,7 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine {
    }

    if (do_disconnect) {
      RemoveCisFromStreamConfiguration(group, leAudioDevice, conn_hdl);
      group->RemoveCisFromStreamIfNeeded(leAudioDevice, conn_hdl);
      IsoManager::GetInstance()->DisconnectCis(conn_hdl, HCI_ERR_PEER_USER);

      log_history_->AddLogHistory(
@@ -647,17 +647,24 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine {
  void ProcessHciNotifAclDisconnected(LeAudioDeviceGroup* group,
                                      LeAudioDevice* leAudioDevice) {
    FreeLinkQualityReports(leAudioDevice);
    /* mark ASEs as not used. */
    leAudioDevice->DeactivateAllAses();

    if (!group) {
      LOG(ERROR) << __func__
                 << " group is null for device: "
                 << ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_)
                 << " group_id: " << leAudioDevice->group_id_;
      /* mark ASEs as not used. */
      leAudioDevice->DeactivateAllAses();
      return;
    }

    /* It is possible that ACL disconnection came before CIS disconnect event */
    for (auto& ase : leAudioDevice->ases_) {
      group->RemoveCisFromStreamIfNeeded(leAudioDevice, ase.cis_conn_hdl);
    }

    /* mark ASEs as not used. */
    leAudioDevice->DeactivateAllAses();

    /* If group is in Idle and not transitioning, update the current group
     * audio context availability which could change due to disconnected group
     * member.
@@ -905,7 +912,7 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine {
          AudioStreamDataPathState::CIS_ASSIGNED;
    }

    RemoveCisFromStreamConfiguration(group, leAudioDevice, event->cis_conn_hdl);
    group->RemoveCisFromStreamIfNeeded(leAudioDevice, event->cis_conn_hdl);

    auto target_state = group->GetTargetState();
    switch (target_state) {
@@ -1230,89 +1237,6 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine {
    group->CreateStreamVectorForOffloader(ase->direction);
  }

  void RemoveCisFromStreamConfiguration(LeAudioDeviceGroup* group,
                                        LeAudioDevice* leAudioDevice,
                                        uint16_t cis_conn_hdl) {
    auto* stream_conf = &group->stream_conf;

    LOG_INFO(" CIS Connection Handle: %d", cis_conn_hdl);

    auto sink_channels = stream_conf->sink_num_of_channels;
    auto source_channels = stream_conf->source_num_of_channels;

    if (!stream_conf->sink_streams.empty() ||
        !stream_conf->source_streams.empty()) {
      stream_conf->sink_streams.erase(
          std::remove_if(
              stream_conf->sink_streams.begin(),
              stream_conf->sink_streams.end(),
              [leAudioDevice, &cis_conn_hdl, &stream_conf](auto& pair) {
                if (!cis_conn_hdl) {
                  cis_conn_hdl = pair.first;
                }
                auto ases_pair =
                    leAudioDevice->GetAsesByCisConnHdl(cis_conn_hdl);
                if (ases_pair.sink && cis_conn_hdl == pair.first) {
                  stream_conf->sink_num_of_devices--;
                  stream_conf->sink_num_of_channels -=
                      ases_pair.sink->codec_config.channel_count;
                  stream_conf->sink_audio_channel_allocation &= ~pair.second;
                }
                return (ases_pair.sink && cis_conn_hdl == pair.first);
              }),
          stream_conf->sink_streams.end());

      stream_conf->source_streams.erase(
          std::remove_if(
              stream_conf->source_streams.begin(),
              stream_conf->source_streams.end(),
              [leAudioDevice, &cis_conn_hdl, &stream_conf](auto& pair) {
                if (!cis_conn_hdl) {
                  cis_conn_hdl = pair.first;
                }
                auto ases_pair =
                    leAudioDevice->GetAsesByCisConnHdl(cis_conn_hdl);
                if (ases_pair.source && cis_conn_hdl == pair.first) {
                  stream_conf->source_num_of_devices--;
                  stream_conf->source_num_of_channels -=
                      ases_pair.source->codec_config.channel_count;
                  stream_conf->source_audio_channel_allocation &= ~pair.second;
                }
                return (ases_pair.source && cis_conn_hdl == pair.first);
              }),
          stream_conf->source_streams.end());

      LOG_INFO(
          " Sink Number Of Devices: %d"
          ", Sink Number Of Channels: %d"
          ", Source Number Of Devices: %d"
          ", Source Number Of Channels: %d",
          stream_conf->sink_num_of_devices, stream_conf->sink_num_of_channels,
          stream_conf->source_num_of_devices,
          stream_conf->source_num_of_channels);
    }

    if (stream_conf->sink_num_of_channels == 0) {
      group->ClearSinksFromConfiguration();
    }

    if (stream_conf->source_num_of_channels == 0) {
      group->ClearSourcesFromConfiguration();
    }

    /* Update offloader streams if needed */
    if (sink_channels > stream_conf->sink_num_of_channels) {
      group->CreateStreamVectorForOffloader(
          le_audio::types::kLeAudioDirectionSink);
    }
    if (source_channels > stream_conf->source_num_of_channels) {
      group->CreateStreamVectorForOffloader(
          le_audio::types::kLeAudioDirectionSource);
    }

    group->CigUnassignCis(leAudioDevice);
  }

  bool CigCreate(LeAudioDeviceGroup* group) {
    uint32_t sdu_interval_mtos, sdu_interval_stom;
    uint16_t max_trans_lat_mtos, max_trans_lat_stom;
@@ -2762,7 +2686,7 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine {
      return;
    }

    RemoveCisFromStreamConfiguration(group, leAudioDevice, ase->cis_conn_hdl);
    group->RemoveCisFromStreamIfNeeded(leAudioDevice, ase->cis_conn_hdl);
    IsoManager::GetInstance()->DisconnectCis(ase->cis_conn_hdl,
                                             HCI_ERR_PEER_USER);
    log_history_->AddLogHistory(
+71 −0
Original line number Diff line number Diff line
@@ -4633,5 +4633,76 @@ TEST_F(StateMachineTest, testAttachDeviceToTheStreamCisFailure) {
  ASSERT_NE(ase->retrans_nb, 0);
}

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

  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);
  PrepareConfigureQosHandler(group);
  PrepareEnableHandler(group);
  PrepareDisableHandler(group);
  PrepareReleaseHandler(group);

  auto* leAudioDevice = group->GetFirstDevice();
  LeAudioDevice* firstDevice = leAudioDevice;
  LeAudioDevice* lastDevice = 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_);

  /* Separate CIS  for dual CIS device is treated as sink device */
  ASSERT_EQ(group->stream_conf.sink_num_of_devices, 2);
  ASSERT_EQ(group->stream_conf.sink_num_of_channels, 2);

  // Inject CIS and ACL disconnection of first device
  InjectAclDisconnected(group, firstDevice);

  InjectCisDisconnected(group, lastDevice, HCI_ERR_CONN_CAUSE_LOCAL_HOST);
  InjectAclDisconnected(group, lastDevice);

  ASSERT_EQ(group->stream_conf.sink_num_of_devices, 0);
  ASSERT_EQ(group->stream_conf.sink_num_of_channels, 0);
}

}  // namespace internal
}  // namespace le_audio