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

Commit ccf2dde7 authored by Łukasz Rymanowski's avatar Łukasz Rymanowski Committed by Łukasz Rymanowski (xWF)
Browse files

leaudio: Fix switching from Live to Game context

Android does assumptions on possible bidirectional context types and use
it when aligning metadata while creating stream.

Some devices uses e.g. Game and Voiceassistant as unidirection which
later breaks a logic when decision is taken about stream
reconfiguration.
E.g. we could incorrectly reconfigure stream from existing LIVE to
unidirectional GAME while recording.

This patch should fix it.

Bug: 375334939
Flag: Exempt, regression tested with unit tests and new test added.
Test: atest bluetooth_le_audio_test
Change-Id: I0ff5513ffb768f6e9e28ef05d87e53761e5259b6
parent 02482f38
Loading
Loading
Loading
Loading
+47 −28
Original line number Diff line number Diff line
@@ -116,8 +116,6 @@ using bluetooth::le_audio::types::BidirectionalPair;
using bluetooth::le_audio::types::DataPathState;
using bluetooth::le_audio::types::hdl_pair;
using bluetooth::le_audio::types::kDefaultScanDurationS;
using bluetooth::le_audio::types::kLeAudioContextAllBidir;
using bluetooth::le_audio::types::kLeAudioContextAllRemoteSinkOnly;
using bluetooth::le_audio::types::kLeAudioContextAllRemoteSource;
using bluetooth::le_audio::types::kLeAudioContextAllTypesArray;
using bluetooth::le_audio::types::LeAudioContextType;
@@ -909,13 +907,6 @@ public:
      return false;
    }

    /* Make sure we do not take the local sink metadata when only the local
     * source scenario is about to be started (e.g. MEDIA).
     */
    if (!kLeAudioContextAllBidir.test(configuration_context_type)) {
      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()) {
@@ -4764,13 +4755,23 @@ public:
        if (contexts_pair.get(dir) == AudioContexts(LeAudioContextType::UNSPECIFIED)) {
          auto is_other_direction_streaming = (*local_hal_state == AudioState::STARTED) ||
                                              (*local_hal_state == AudioState::READY_TO_START);
          if (is_other_direction_streaming &&
              (contexts_pair.get(other_dir) != AudioContexts(LeAudioContextType::UNSPECIFIED))) {
          if (is_other_direction_streaming) {
            auto supported_contexts = group->GetAllSupportedSingleDirectionOnlyContextTypes(dir);
            auto common_part = supported_contexts & contexts_pair.get(other_dir);

            log::info(
                    "Other direction is streaming. Aligning other direction "
                    "metadata to match the current direction context: {}",
                    ToString(contexts_pair.get(other_dir)));
            contexts_pair.get(dir) = contexts_pair.get(other_dir);
                    "Other direction is streaming. Possible aligning other direction "
                    "metadata to match the remote {} direction context: {}. Remote {} supported "
                    "contexts: {} ",
                    other_dir == bluetooth::le_audio::types::kLeAudioDirectionSink ? " Sink"
                                                                                   : " Source",
                    ToString(contexts_pair.get(other_dir)),
                    dir == bluetooth::le_audio::types::kLeAudioDirectionSink ? " Sink" : " Source",
                    ToString(supported_contexts));

            if (common_part.value() != 0) {
              contexts_pair.get(dir) = common_part;
            }
          }
        } else {
          log::debug("Removing UNSPECIFIED from the remote {} context: {}",
@@ -4870,13 +4871,16 @@ public:
            .sink = local_metadata_context_types_.source,
            .source = local_metadata_context_types_.sink};

    auto all_bidirectional_contexts = group->GetAllSupportedBidirectionalContextTypes();
    log::debug("all_bidirectional_contexts {}", ToString(all_bidirectional_contexts));

    /* Make sure we have CONVERSATIONAL when in a call and it is not mixed
     * with any other bidirectional context
     */
    if (IsInCall() || IsInVoipCall()) {
      log::debug("In Call preference used: {}, voip call: {}", IsInCall(), IsInVoipCall());
      remote_metadata.sink.unset_all(kLeAudioContextAllBidir);
      remote_metadata.source.unset_all(kLeAudioContextAllBidir);
      remote_metadata.sink.unset_all(all_bidirectional_contexts);
      remote_metadata.source.unset_all(all_bidirectional_contexts);
      remote_metadata.sink.set(LeAudioContextType::CONVERSATIONAL);
      remote_metadata.source.set(LeAudioContextType::CONVERSATIONAL);
    }
@@ -4907,32 +4911,46 @@ public:
    log::debug("is_ongoing_call_on_other_direction={}",
               is_ongoing_call_on_other_direction ? "True" : "False");

    if (remote_metadata.get(remote_other_direction).test_any(kLeAudioContextAllBidir) &&
    if (remote_metadata.get(remote_other_direction).test_any(all_bidirectional_contexts) &&
        !is_streaming_other_direction) {
      log::debug("The other direction is not streaming bidirectional, ignore that context.");
      remote_metadata.get(remote_other_direction).clear();
    }

    auto single_direction_only_context_types =
            group->GetAllSupportedSingleDirectionOnlyContextTypes(remote_direction);
    auto single_other_direction_only_context_types =
            group->GetAllSupportedSingleDirectionOnlyContextTypes(remote_other_direction);
    log::debug(
            "single direction only contexts : {} for direction {}, single direction contexts {} "
            "for {}",
            ToString(single_direction_only_context_types), remote_direction_str,
            ToString(single_other_direction_only_context_types), remote_other_direction_str);

    /* Mixed contexts in the voiceback channel scenarios can confuse the remote
     * on how to configure each channel. We should align the other direction
     * metadata for the remote device.
     */
    if (remote_metadata.get(remote_direction).test_any(kLeAudioContextAllBidir)) {
    if (remote_metadata.get(remote_direction).test_any(all_bidirectional_contexts)) {
      log::debug("Aligning the other direction remote metadata to add this direction context");

      if (is_ongoing_call_on_other_direction) {
        /* Other direction is streaming and is in call */
        remote_metadata.get(remote_direction).unset_all(kLeAudioContextAllBidir);
        remote_metadata.get(remote_direction).unset_all(all_bidirectional_contexts);
        remote_metadata.get(remote_direction).set(LeAudioContextType::CONVERSATIONAL);
      } else {
        if (!is_streaming_other_direction) {
          // Do not take the obsolete metadata
          remote_metadata.get(remote_other_direction).clear();
        } else {
          remote_metadata.get(remote_other_direction).unset_all(all_bidirectional_contexts);
          remote_metadata.get(remote_other_direction)
                  .unset_all(single_direction_only_context_types);
        }
        remote_metadata.get(remote_other_direction).unset_all(kLeAudioContextAllBidir);
        remote_metadata.get(remote_other_direction).unset_all(kLeAudioContextAllRemoteSinkOnly);

        remote_metadata.get(remote_other_direction)
                .set_all(remote_metadata.get(remote_direction) & ~kLeAudioContextAllRemoteSinkOnly);
                .set_all(remote_metadata.get(remote_direction) &
                         ~single_other_direction_only_context_types);
      }
    }
    log::debug("remote_metadata.source= {}", ToString(remote_metadata.source));
@@ -4946,22 +4964,23 @@ public:
       */
      if ((remote_metadata.get(remote_direction).none() &&
           remote_metadata.get(remote_other_direction).any()) ||
          remote_metadata.get(remote_other_direction).test_any(kLeAudioContextAllBidir)) {
          remote_metadata.get(remote_other_direction).test_any(all_bidirectional_contexts)) {
        log::debug("Aligning this direction remote metadata to add the other direction context");
        /* Turn off bidirectional contexts on this direction to avoid mixing
         * with the other direction bidirectional context
         */
        remote_metadata.get(remote_direction).unset_all(kLeAudioContextAllBidir);
        remote_metadata.get(remote_direction).unset_all(all_bidirectional_contexts);
        remote_metadata.get(remote_direction).set_all(remote_metadata.get(remote_other_direction));
      }
    }

    /* Make sure that after alignment no sink only context leaks into the other
     * direction. */
    remote_metadata.source.unset_all(kLeAudioContextAllRemoteSinkOnly);
    remote_metadata.source.unset_all(group->GetAllSupportedSingleDirectionOnlyContextTypes(
            bluetooth::le_audio::types::kLeAudioDirectionSink));

    log::debug("remote_metadata.source= {}", ToString(remote_metadata.source));
    log::debug("remote_metadata.sink= {}", ToString(remote_metadata.sink));
    log::debug("final remote_metadata.source= {}", ToString(remote_metadata.source));
    log::debug("final remote_metadata.sink= {}", ToString(remote_metadata.sink));
    return remote_metadata;
  }

+55 −5
Original line number Diff line number Diff line
@@ -1061,6 +1061,46 @@ bool LeAudioDeviceGroup::ReloadAudioDirections(void) {
  return true;
}

AudioContexts LeAudioDeviceGroup::GetAllSupportedBidirectionalContextTypes(void) {
  auto result = GetSupportedContexts(types::kLeAudioDirectionSink) &
                GetSupportedContexts(types::kLeAudioDirectionSource);

  result &= types::kLeAudioContextAllBidir;

  return result;
}

AudioContexts LeAudioDeviceGroup::GetAllSupportedSingleDirectionOnlyContextTypes(
        uint8_t remote_direction) {
  AudioContexts result;

  /* Remote device present supported context types on the different directions.
   * It might happen that some "single directional" contexts are exposed on both
   * directions on the remote side.
   * Android takes the decision on the stream configuration based on the contexts therefore
   * there is defined list of host bidirectional and host single directional context
   * types. This function helps to filter out some missconfigurations on the remote side and return
   * single directional context types.
   * One of the use cases we want to handle here is is that usually VoiceAssistant and GAME are
   * bidirectional but some devices might remove it on purpose from one direction.
   */
  auto group_single_dir_only_contexts =
          GetSupportedContexts(remote_direction) & ~GetAllSupportedBidirectionalContextTypes();

  if (remote_direction == types::kLeAudioDirectionSink) {
    auto host_all_sink_contexts =
            types::kLeAudioContextAllRemoteSinkOnly | types::kLeAudioContextAllBidir;
    result = host_all_sink_contexts & group_single_dir_only_contexts;

  } else {
    auto host_all_source_contexts =
            types::kLeAudioContextAllRemoteSource | types::kLeAudioContextAllBidir;
    result = host_all_source_contexts & group_single_dir_only_contexts;
  }

  return result;
}

bool LeAudioDeviceGroup::IsInTransition(void) const { return in_transition_; }

bool LeAudioDeviceGroup::IsStreaming(void) const {
@@ -1197,11 +1237,21 @@ void LeAudioDeviceGroup::CigConfiguration::GenerateCisIds(LeAudioContextType con
  uint8_t cis_count_unidir_source = 0;
  int group_size = group_->DesiredSize();

  set_configurations::get_cis_count(context_type, group_size, group_->GetGroupSinkStrategy(),
  uint8_t expected_remote_directions;
  if (group_->GetAllSupportedBidirectionalContextTypes().test(context_type)) {
    expected_remote_directions = types::kLeAudioDirectionBoth;
  } else if (group_->GetAllSupportedSingleDirectionOnlyContextTypes(types::kLeAudioDirectionSource)
                     .test(context_type)) {
    expected_remote_directions = types::kLeAudioDirectionSource;
  } else {
    expected_remote_directions = types::kLeAudioDirectionSink;
  }

  set_configurations::get_cis_count(
          context_type, expected_remote_directions, group_size, group_->GetGroupSinkStrategy(),
          group_->GetAseCount(types::kLeAudioDirectionSink),
                                    group_->GetAseCount(types::kLeAudioDirectionSource),
                                    cis_count_bidir, cis_count_unidir_sink,
                                    cis_count_unidir_source);
          group_->GetAseCount(types::kLeAudioDirectionSource), cis_count_bidir,
          cis_count_unidir_sink, cis_count_unidir_source);

  uint8_t idx = 0;
  while (cis_count_bidir > 0) {
+2 −0
Original line number Diff line number Diff line
@@ -218,6 +218,8 @@ public:
  void ResetPreferredAudioSetConfiguration(void) const;
  bool ReloadAudioLocations(void);
  bool ReloadAudioDirections(void);
  types::AudioContexts GetAllSupportedBidirectionalContextTypes(void);
  types::AudioContexts GetAllSupportedSingleDirectionOnlyContextTypes(uint8_t direction);
  std::shared_ptr<const set_configurations::AudioSetConfiguration> GetActiveConfiguration(
          void) const;
  bool IsPendingConfiguration(void) const;
+93 −5
Original line number Diff line number Diff line
@@ -6218,6 +6218,93 @@ TEST_F(UnicastTest, SpeakerStreamingNonDefault) {
  LocalAudioSourceResume();
}
TEST_F(UnicastTest, TestUnidirectionalGameAndLiveRecording) {
  com::android::bluetooth::flags::provider_->le_audio_support_unidirectional_voice_assistant(true);
  const RawAddress test_address0 = GetTestAddress(0);
  int group_id = bluetooth::groups::kGroupUnknown;
  /**
   * Scenario test steps
   * 1. Configure group to support GAME only on SINK
   * 2. Configure group to support LIVE
   * 3. Start recording with LIVE
   * 4. Update context type with GAME
   * 5. Verify that Configuration did not changed.
   */
  available_snk_context_types_ =
          (types::LeAudioContextType::GAME | types::LeAudioContextType::MEDIA |
           types::LeAudioContextType::UNSPECIFIED | types::LeAudioContextType::LIVE)
                  .value();
  supported_snk_context_types_ = available_snk_context_types_;
  available_src_context_types_ =
          (types::LeAudioContextType::LIVE | types::LeAudioContextType::UNSPECIFIED).value();
  supported_src_context_types_ = available_src_context_types_;
  default_channel_cnt = 1;
  SetSampleDatabaseEarbudsValid(
          1, test_address0, codec_spec_conf::kLeAudioLocationStereo,
          codec_spec_conf::kLeAudioLocationStereo, default_channel_cnt, default_channel_cnt, 0x0004,
          /* source sample freq 16khz */ false /*add_csis*/, true /*add_cas*/, true /*add_pacs*/,
          default_ase_cnt /*add_ascs_cnt*/, 1 /*set_size*/, 0 /*rank*/);
  EXPECT_CALL(mock_audio_hal_client_callbacks_,
              OnConnectionState(ConnectionState::CONNECTED, test_address0))
          .Times(1);
  EXPECT_CALL(mock_audio_hal_client_callbacks_,
              OnGroupNodeStatus(test_address0, _, GroupNodeStatus::ADDED))
          .WillOnce(DoAll(SaveArg<1>(&group_id)));
  types::BidirectionalPair<types::AudioContexts> metadata_contexts = {
          .sink = types::AudioContexts(types::LeAudioContextType::LIVE),
          .source = types::AudioContexts(types::LeAudioContextType::LIVE)};
  EXPECT_CALL(mock_state_machine_,
              StartStream(_, types::LeAudioContextType::LIVE, metadata_contexts, _))
          .Times(1);
  log::info("Connecting LeAudio to {}", test_address0);
  ConnectLeAudio(test_address0);
  ASSERT_NE(group_id, bluetooth::groups::kGroupUnknown);
  // Audio sessions are started only when device gets active
  EXPECT_CALL(*mock_le_audio_source_hal_client_, Start(_, _, _)).Times(1);
  EXPECT_CALL(*mock_le_audio_sink_hal_client_, Start(_, _, _)).Times(1);
  EXPECT_CALL(*mock_codec_manager_,
              UpdateActiveUnicastAudioHalClient(mock_le_audio_source_hal_client_,
                                                mock_le_audio_sink_hal_client_, true))
          .Times(1);
  LeAudioClient::Get()->GroupSetActive(group_id);
  SyncOnMainLoop();
  UpdateLocalSinkMetadata(AUDIO_SOURCE_MIC);
  LocalAudioSinkResume();
  SyncOnMainLoop();
  Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_);
  Mock::VerifyAndClearExpectations(mock_le_audio_source_hal_client_);
  Mock::VerifyAndClearExpectations(mock_le_audio_sink_hal_client_);
  Mock::VerifyAndClearExpectations(mock_codec_manager_);
  Mock::VerifyAndClearExpectations(&mock_state_machine_);
  SyncOnMainLoop();
  // We do expect only unidirectional CIS
  uint8_t cis_count_out = 0;
  uint8_t cis_count_in = 1;
  // Verify Data transfer on one local audio source cis
  TestAudioDataTransfer(group_id, cis_count_out, cis_count_in, 0, 40);
  SyncOnMainLoop();
  EXPECT_CALL(mock_state_machine_, StopStream(_)).Times(0);
  UpdateLocalSourceMetadata(AUDIO_USAGE_GAME, AUDIO_CONTENT_TYPE_UNKNOWN);
  LocalAudioSourceResume();
  SyncOnMainLoop();
  Mock::VerifyAndClearExpectations(&mock_state_machine_);
}
TEST_F(UnicastTest, TestUnidirectionalVoiceAssistant_Sink) {
  com::android::bluetooth::flags::provider_->le_audio_support_unidirectional_voice_assistant(true);
  const RawAddress test_address0 = GetTestAddress(0);
@@ -6254,7 +6341,7 @@ TEST_F(UnicastTest, TestUnidirectionalVoiceAssistant_Sink) {
  types::BidirectionalPair<types::AudioContexts> metadata_contexts = {
          .sink = types::AudioContexts(types::LeAudioContextType::VOICEASSISTANTS),
          .source = types::AudioContexts(types::LeAudioContextType::UNSPECIFIED)};
          .source = types::AudioContexts()};
  EXPECT_CALL(mock_state_machine_,
              StartStream(_, types::LeAudioContextType::VOICEASSISTANTS, metadata_contexts, _))
          .Times(1);
@@ -6328,7 +6415,7 @@ TEST_F(UnicastTest, TestUnidirectionalVoiceAssistant_Source) {
          .WillOnce(DoAll(SaveArg<1>(&group_id)));
  types::BidirectionalPair<types::AudioContexts> metadata_contexts = {
          .sink = types::AudioContexts(types::LeAudioContextType::UNSPECIFIED),
          .sink = types::AudioContexts(),
          .source = types::AudioContexts(types::LeAudioContextType::VOICEASSISTANTS)};
  EXPECT_CALL(mock_state_machine_,
              StartStream(_, types::LeAudioContextType::VOICEASSISTANTS, metadata_contexts, _))
@@ -10061,7 +10148,7 @@ TEST_F(UnicastTest, UpdateNotSupportedContextTypeUnspecifiedAvailable) {
  EXPECT_CALL(mock_state_machine_, StopStream(_)).Times(0);
  types::BidirectionalPair<types::AudioContexts> contexts = {
          .sink = types::AudioContexts(types::LeAudioContextType::UNSPECIFIED),
          .source = types::AudioContexts(types::LeAudioContextType::UNSPECIFIED)};
          .source = types::AudioContexts()};
  EXPECT_CALL(mock_state_machine_, StartStream(_, types::LeAudioContextType::GAME, contexts, _))
          .Times(1);
  UpdateLocalSourceMetadata(AUDIO_USAGE_GAME, AUDIO_CONTENT_TYPE_UNKNOWN, false);
@@ -10129,7 +10216,7 @@ TEST_F(UnicastTest, UpdateNotSupportedContextTypeUnspecifiedAvailable_SpeedUpRec
  EXPECT_CALL(mock_state_machine_, StopStream(_)).Times(0);
  types::BidirectionalPair<types::AudioContexts> contexts = {
          .sink = types::AudioContexts(types::LeAudioContextType::UNSPECIFIED),
          .source = types::AudioContexts(types::LeAudioContextType::UNSPECIFIED)};
          .source = types::AudioContexts()};
  EXPECT_CALL(mock_state_machine_, StartStream(_, types::LeAudioContextType::GAME, contexts, _))
          .Times(1);
  UpdateLocalSourceMetadata(AUDIO_USAGE_GAME, AUDIO_CONTENT_TYPE_UNKNOWN, false);
@@ -10521,6 +10608,8 @@ TEST_F(UnicastTest, MusicDuringCallContextTypes) {
          available_snk_context_types_ |
          types::AudioContexts(types::LeAudioContextType::UNSPECIFIED).value();
  available_src_context_types_ = available_snk_context_types_;
  available_src_context_types_ &=
          ~((types::LeAudioContextType::NOTIFICATIONS | types::LeAudioContextType::MEDIA).value());
  supported_src_context_types_ =
          available_src_context_types_ |
          types::AudioContexts(types::LeAudioContextType::UNSPECIFIED).value();
@@ -10559,7 +10648,6 @@ TEST_F(UnicastTest, MusicDuringCallContextTypes) {
                          contexts, _))
          .Times(1);
  StartStreaming(AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE, AUDIO_CONTENT_TYPE_UNKNOWN, group_id);
  LocalAudioSourceResume();
  LocalAudioSinkResume();
  // Verify
+16 −8
Original line number Diff line number Diff line
@@ -46,17 +46,21 @@ using types::kLeAudioDirectionSink;
using types::kLeAudioDirectionSource;
using types::LeAudioCoreCodecConfig;

void get_cis_count(LeAudioContextType context_type, int expected_device_cnt,
                   types::LeAudioConfigurationStrategy strategy, int avail_group_ase_snk_cnt,
                   int avail_group_ase_src_count, uint8_t& out_cis_count_bidir,
                   uint8_t& out_cis_count_unidir_sink, uint8_t& out_cis_count_unidir_source) {
void get_cis_count(LeAudioContextType context_type, uint8_t expected_remote_direction,
                   int expected_device_cnt, types::LeAudioConfigurationStrategy strategy,
                   int avail_group_ase_snk_cnt, int avail_group_ase_src_count,
                   uint8_t& out_cis_count_bidir, uint8_t& out_cis_count_unidir_sink,
                   uint8_t& out_cis_count_unidir_source) {
  log::info(
          "{} strategy {}, group avail sink ases: {}, group avail source ases {} "
          "{} expected_remote_direction {}, strategy {}, group avail sink ases: {}, "
          "group avail source ases {} "
          "expected_device_count {}",
          bluetooth::common::ToString(context_type), static_cast<int>(strategy),
          avail_group_ase_snk_cnt, avail_group_ase_src_count, expected_device_cnt);
          bluetooth::common::ToString(context_type), expected_remote_direction,
          static_cast<int>(strategy), avail_group_ase_snk_cnt, avail_group_ase_src_count,
          expected_device_cnt);

  bool is_bidirectional = types::kLeAudioContextAllBidir.test(context_type);
  bool is_bidirectional = expected_remote_direction == types::kLeAudioDirectionBoth;
  bool is_source_only = expected_remote_direction == types::kLeAudioDirectionSource;

  switch (strategy) {
    case types::LeAudioConfigurationStrategy::MONO_ONE_CIS_PER_DEVICE:
@@ -76,6 +80,8 @@ void get_cis_count(LeAudioContextType context_type, int expected_device_cnt,
            out_cis_count_unidir_source = expected_device_cnt;
          }
        }
      } else if (is_source_only) {
        out_cis_count_unidir_source = expected_device_cnt;
      } else {
        out_cis_count_unidir_sink = expected_device_cnt;
      }
@@ -101,6 +107,8 @@ void get_cis_count(LeAudioContextType context_type, int expected_device_cnt,
            out_cis_count_unidir_source = 2 * expected_device_cnt;
          }
        }
      } else if (is_source_only) {
        out_cis_count_unidir_source = 2 * expected_device_cnt;
      } else {
        out_cis_count_unidir_sink = 2 * expected_device_cnt;
      }
Loading