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

Commit 23a4ef62 authored by Jakub Tyszkowski's avatar Jakub Tyszkowski Committed by Łukasz Rymanowski
Browse files

LeAudio: Workaround VoIP calls not using Telecom API

When the Telecom API is not used for the VoIP call, we do not
receive the InCall mode callback. We need to figure out that this
is a VoIP call and reconfigure for the CONVERSATIONAL context. However
since Telecom is not involved, TBS does not know about the call and it
will not be able to control the call state. We should also NOT put the
TBS content control ID into the stream metadata, as there will be no
incoming call on the TBS service. Otherwise the remote device would
get confused.

Bug: 284230664
Test: atest bluetooth_le_audio_client_test
Change-Id: I4d320e71055a06657c9dd688a6f873916cf862ec
parent 53c2254a
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -63,6 +63,9 @@ class LeAudioClient {
      bluetooth::le_audio::btle_audio_codec_config_t output_codec_config) = 0;
  virtual void SetCcidInformation(int ccid, int context_type) = 0;
  virtual void SetInCall(bool in_call) = 0;
  virtual bool IsInCall() = 0;
  virtual void SetInVoipCall(bool in_call) = 0;
  virtual bool IsInVoipCall() = 0;
  virtual void SendAudioProfilePreferences(
      const int group_id, bool is_output_preference_le_audio,
      bool is_duplex_preference_le_audio) = 0;
+72 −11
Original line number Diff line number Diff line
@@ -251,6 +251,7 @@ class LeAudioClientImpl : public LeAudioClient {
        audio_receiver_state_(AudioState::IDLE),
        audio_sender_state_(AudioState::IDLE),
        in_call_(false),
        in_voip_call_(false),
        current_source_codec_config({0, 0, 0, 0}),
        current_sink_codec_config({0, 0, 0, 0}),
        lc3_encoder_left_mem(nullptr),
@@ -304,6 +305,10 @@ class LeAudioClientImpl : public LeAudioClient {
  void ReconfigureAfterVbcClose() {
    LOG_DEBUG("VBC close timeout");

    if (IsInVoipCall()) {
      SetInVoipCall(false);
    }

    auto group = aseGroups_.FindById(active_group_id_);
    if (!group) {
      LOG_ERROR("Invalid group: %d", active_group_id_);
@@ -917,11 +922,18 @@ class LeAudioClientImpl : public LeAudioClient {
      remote_contexts.source.clear();
    }

    /* Do not put the TBS CCID when not using Telecom for the VoIP calls. */
    auto ccid_contexts = remote_contexts;
    if (IsInVoipCall() && !IsInCall()) {
      ccid_contexts.sink.unset(LeAudioContextType::CONVERSATIONAL);
      ccid_contexts.source.unset(LeAudioContextType::CONVERSATIONAL);
    }

    BidirectionalPair<std::vector<uint8_t>> ccids = {
        .sink = ContentControlIdKeeper::GetInstance()->GetAllCcids(
            remote_contexts.sink),
            ccid_contexts.sink),
        .source = ContentControlIdKeeper::GetInstance()->GetAllCcids(
            remote_contexts.source)};
            ccid_contexts.source)};
    if (group->IsPendingConfiguration()) {
      return groupStateMachine_->ConfigureStream(
          group, configuration_context_type_, remote_contexts, ccids);
@@ -1037,6 +1049,15 @@ class LeAudioClientImpl : public LeAudioClient {
    in_call_ = in_call;
  }

  bool IsInCall() override { return in_call_; }

  void SetInVoipCall(bool in_call) override {
    LOG_DEBUG("in_voip_call: %d", in_call);
    in_voip_call_ = in_call;
  }

  bool IsInVoipCall() override { return in_voip_call_; }

  void SendAudioProfilePreferences(
      const int group_id, bool is_output_preference_le_audio,
      bool is_duplex_preference_le_audio) override {
@@ -2943,7 +2964,20 @@ class LeAudioClientImpl : public LeAudioClient {
      return;
    }

    if (!groupStateMachine_->AttachToStream(group, leAudioDevice)) {
    /* Do not put the TBS CCID when not using Telecom for the VoIP calls. */
    auto ccid_contexts = group->GetMetadataContexts();
    if (IsInVoipCall() && !IsInCall()) {
      ccid_contexts.sink.unset(LeAudioContextType::CONVERSATIONAL);
      ccid_contexts.source.unset(LeAudioContextType::CONVERSATIONAL);
    }
    BidirectionalPair<std::vector<uint8_t>> ccids = {
        .sink = ContentControlIdKeeper::GetInstance()->GetAllCcids(
            ccid_contexts.sink),
        .source = ContentControlIdKeeper::GetInstance()->GetAllCcids(
            ccid_contexts.source)};

    if (!groupStateMachine_->AttachToStream(group, leAudioDevice,
                                            std::move(ccids))) {
      LOG_WARN("Could not add device %s to the group %d streaming. ",
               ADDRESS_TO_LOGGABLE_CSTR(leAudioDevice->address_),
               group->group_id_);
@@ -3898,7 +3932,7 @@ class LeAudioClientImpl : public LeAudioClient {
    }

    /* Check if the device resume is expected */
    if (!group->GetCachedCodecConfigurationByDirection(
    if (!group->GetCodecConfigurationByDirection(
            configuration_context_type_,
            le_audio::types::kLeAudioDirectionSink)) {
      LOG(ERROR) << __func__ << ", invalid resume request for context type: "
@@ -4324,7 +4358,7 @@ class LeAudioClientImpl : public LeAudioClient {
              bluetooth::common::ToString(available_remote_contexts).c_str(),
              bluetooth::common::ToString(configuration_context_type_).c_str());

    if (in_call_) {
    if (IsInCall()) {
      LOG_DEBUG(" In Call preference used.");
      return LeAudioContextType::CONVERSATIONAL;
    }
@@ -4336,10 +4370,10 @@ class LeAudioClientImpl : public LeAudioClient {
      LeAudioContextType context_priority_list[] = {
          /* Highest priority first */
          LeAudioContextType::CONVERSATIONAL,
          /* Skip the RINGTONE to avoid reconfigurations when adjusting
           * call volume slider while not in a call.
           * LeAudioContextType::RINGTONE,
          /* Handling RINGTONE will cause the ringtone volume slider to trigger
           * reconfiguration. This will be fixed in b/283349711.
           */
          LeAudioContextType::RINGTONE,
          LeAudioContextType::LIVE,
          LeAudioContextType::VOICEASSISTANTS,
          LeAudioContextType::GAME,
@@ -4600,11 +4634,29 @@ class LeAudioClientImpl : public LeAudioClient {

  BidirectionalPair<AudioContexts> DirectionalRealignMetadataAudioContexts(
      LeAudioDeviceGroup* group, int remote_direction) {
    // Inject conversational when ringtone is played - this is required for all
    // the VoIP applications which are not using the telecom API.
    if ((remote_direction == le_audio::types::kLeAudioDirectionSink) &&
        local_metadata_context_types_.source.test(
            LeAudioContextType::RINGTONE)) {
      /* Simulate, we are already in the call. Sending RINGTONE when there is
       * no incoming call to accept or reject on TBS could confuse the remote
       * device and interrupt the stream establish procedure.
       */
      if (!IsInCall()) {
        SetInVoipCall(true);
      }
    } else if (IsInVoipCall()) {
      SetInVoipCall(false);
    }

    /* Make sure we have CONVERSATIONAL when in a call and it is not mixed
     * with any other bidirectional context
     */
    if (in_call_) {
      LOG_DEBUG(" In Call preference used.");
    if (IsInCall() || IsInVoipCall()) {
      LOG_DEBUG(" In Call preference used: %s, voip call: %s",
                (IsInCall() ? "true" : "false"),
                (IsInVoipCall() ? "true" : "false"));
      local_metadata_context_types_.sink.unset_all(kLeAudioContextAllBidir);
      local_metadata_context_types_.source.unset_all(kLeAudioContextAllBidir);
      local_metadata_context_types_.sink.set(
@@ -4634,6 +4686,12 @@ class LeAudioClientImpl : public LeAudioClient {
    BidirectionalPair<AudioContexts> remote_metadata = {
        .sink = local_metadata_context_types_.source,
        .source = local_metadata_context_types_.sink};

    if (IsInVoipCall()) {
      LOG_DEBUG("Unsetting RINGTONE from remote sink ");
      remote_metadata.sink.unset(LeAudioContextType::RINGTONE);
    }

    LOG_DEBUG("local_metadata_context_types_.source= %s",
              ToString(local_metadata_context_types_.source).c_str());
    LOG_DEBUG("local_metadata_context_types_.sink= %s",
@@ -5135,11 +5193,13 @@ class LeAudioClientImpl : public LeAudioClient {
                               false)) {
      return;
    }

    /* If group is inactive, phone is in call and Group is not having CIS
     * connected, notify upper layer about it, so it can decide to create SCO if
     * it is in the handover case
     */
    if (in_call_ && active_group_id_ == bluetooth::groups::kGroupUnknown) {
    if ((IsInCall() || IsInVoipCall()) &&
        active_group_id_ == bluetooth::groups::kGroupUnknown) {
      callbacks_->OnGroupStatus(group_id, GroupStatus::TURNED_IDLE_DURING_CALL);
    }
  }
@@ -5313,6 +5373,7 @@ class LeAudioClientImpl : public LeAudioClient {
  AudioState audio_sender_state_;
  /* Keep in call state. */
  bool in_call_;
  bool in_voip_call_;

  /* Reconnection mode */
  tBTM_BLE_CONN_TYPE reconnection_mode_;
+5 −3
Original line number Diff line number Diff line
@@ -739,16 +739,18 @@ class UnicastTestNoInit : public Test {
              return true;
            });

    ON_CALL(mock_state_machine_, AttachToStream(_, _))
    ON_CALL(mock_state_machine_, AttachToStream(_, _, _))
        .WillByDefault([](LeAudioDeviceGroup* group,
                          LeAudioDevice* leAudioDevice) {
                          LeAudioDevice* leAudioDevice,
                          types::BidirectionalPair<std::vector<uint8_t>>
                              ccids) {
          if (group->GetState() !=
              types::AseState::BTA_LE_AUDIO_ASE_STATE_STREAMING) {
            return false;
          }

          group->Configure(group->GetConfigurationContextType(),
                           group->GetMetadataContexts());
                           group->GetMetadataContexts(), ccids);
          if (!group->CigAssignCisIds(leAudioDevice)) return false;
          group->CigAssignCisConnHandlesToAses(leAudioDevice);

+2 −1
Original line number Diff line number Diff line
@@ -33,7 +33,8 @@ class MockLeAudioGroupStateMachine : public le_audio::LeAudioGroupStateMachine {
      (override));
  MOCK_METHOD((bool), AttachToStream,
              (le_audio::LeAudioDeviceGroup * group,
               le_audio::LeAudioDevice* leAudioDevice),
               le_audio::LeAudioDevice* leAudioDevice,
               le_audio::types::BidirectionalPair<std::vector<uint8_t>> ccids),
              (override));
  MOCK_METHOD((void), SuspendStream, (le_audio::LeAudioDeviceGroup * group),
              (override));
+2 −7
Original line number Diff line number Diff line
@@ -137,8 +137,8 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine {
    log_history_ = nullptr;
  }

  bool AttachToStream(LeAudioDeviceGroup* group,
                      LeAudioDevice* leAudioDevice) override {
  bool AttachToStream(LeAudioDeviceGroup* group, LeAudioDevice* leAudioDevice,
                      BidirectionalPair<std::vector<uint8_t>> ccids) override {
    LOG(INFO) << __func__ << " group id: " << group->group_id_
              << " device: " << ADDRESS_TO_LOGGABLE_STR(leAudioDevice->address_);

@@ -155,11 +155,6 @@ class LeAudioGroupStateMachineImpl : public LeAudioGroupStateMachine {
      return false;
    }

    BidirectionalPair<std::vector<uint8_t>> ccids = {
        .sink = le_audio::ContentControlIdKeeper::GetInstance()->GetAllCcids(
            group->GetMetadataContexts().sink),
        .source = le_audio::ContentControlIdKeeper::GetInstance()->GetAllCcids(
            group->GetMetadataContexts().source)};
    if (!group->Configure(group->GetConfigurationContextType(),
                          group->GetMetadataContexts(), ccids)) {
      LOG_ERROR(" failed to set ASE configuration");
Loading