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

Commit 024f328c authored by Jakub Tyszkowski's avatar Jakub Tyszkowski
Browse files

LeAudio: Fix loading audio locations from storage and AIDL

Two more places where the Mono audio location was not handled properly

Bug: 350875139
Test: atest bluetooth_le_audio_client_test
Flag: Exempt; change covered with unit test
Change-Id: I7367358e58295950e1f1ac4ed35bed153a47fda2
parent cd70bb74
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -440,8 +440,10 @@ GetStackBroadcastConfigurationFromAidlFormat(
      stack_config.params = GetStackLeAudioLtvMapFromAidlFormat(ase_config->codecConfiguration);
      auto cfg = stack_config.params.GetAsCoreCodecConfig();
      if (cfg.audio_channel_allocation.has_value()) {
        auto allocation_bitset = std::bitset<32>(cfg.audio_channel_allocation.value());
        // Value of 0x00000000 means MonoAudio - one channel per iso stream
        stack_config.channel_count_per_iso_stream =
                std::bitset<32>(cfg.audio_channel_allocation.value()).count();
                allocation_bitset.any() ? allocation_bitset.count() : 1;
      }
    }
  }
+57 −12
Original line number Diff line number Diff line
@@ -351,8 +351,11 @@ PrepareReferenceAseDirectionConfigLc3(bool is_left, bool is_right, bool is_low_l
  aidl_ase_config.aseConfiguration.metadata = aidl_metadata;

  auto stack_codec_params = stack_params.GetAsCoreCodecConfig();

  // Value of 0x00000000 means MonoAudio - one channel per iso stream
  auto location_bitset = std::bitset<32>(stack_codec_params.audio_channel_allocation.value_or(0));
  stack_ase_config.codec.channel_count_per_iso_stream =
          std::bitset<32>(stack_codec_params.audio_channel_allocation.value_or(1)).count();
          location_bitset.any() ? location_bitset.count() : 1;

  /* QoS configuration */
  if (has_qos) {
@@ -375,8 +378,11 @@ PrepareReferenceAseDirectionConfigLc3(bool is_left, bool is_right, bool is_low_l
std::pair<::aidl::android::hardware::bluetooth::audio::IBluetoothAudioProvider::
                  LeAudioAseConfigurationSetting,
          ::bluetooth::le_audio::set_configurations::AudioSetConfiguration>
PrepareReferenceAseConfigurationSetting(::bluetooth::le_audio::types::LeAudioContextType ctx_type,
                                        bool has_source = false) {
PrepareReferenceAseConfigurationSetting(
        ::bluetooth::le_audio::types::LeAudioContextType ctx_type,
        std::optional<uint32_t> source_locations =
                le_audio::codec_spec_conf::kLeAudioLocationFrontLeft |
                le_audio::codec_spec_conf::kLeAudioLocationFrontRight) {
  // Prepare the AIDL format config
  ::aidl::android::hardware::bluetooth::audio::IBluetoothAudioProvider::
          LeAudioAseConfigurationSetting aidl_audio_set_config;
@@ -424,8 +430,8 @@ PrepareReferenceAseConfigurationSetting(::bluetooth::le_audio::types::LeAudioCon
                  ALLOW_ASYMMETRIC_CONFIGURATIONS |
          ::aidl::android::hardware::bluetooth::audio::ConfigurationFlags::MONO_MIC_CONFIGURATION;

  /* Low latency, mono microphone - Single source ASE */
  if (has_source) {
  /* Low latency microphone */
  if (source_locations) {
    if (!aidl_audio_set_config.sourceAseConfiguration) {
      log::error("Has no source container");
      aidl_audio_set_config.sourceAseConfiguration = std::vector<
@@ -433,13 +439,36 @@ PrepareReferenceAseConfigurationSetting(::bluetooth::le_audio::types::LeAudioCon
                                    LeAudioAseConfigurationSetting::AseDirectionConfiguration>>();
    }

    // Left ASE config
    if (source_locations.value() & le_audio::codec_spec_conf::kLeAudioLocationFrontLeft) {
      auto [aidl_ase_config_source, stack_ase_config_source] =
              PrepareReferenceAseDirectionConfigLc3(true, false, true);
      // AIDL:
      aidl_audio_set_config.sourceAseConfiguration->push_back(aidl_ase_config_source);
      // STACK:
      stack_audio_set_config.confs.source.push_back(stack_ase_config_source);
    }

    // Right ASE config
    if (source_locations.value() & le_audio::codec_spec_conf::kLeAudioLocationFrontRight) {
      auto [aidl_ase_config_source, stack_ase_config_source] =
              PrepareReferenceAseDirectionConfigLc3(false, true, true);
      // AIDL:
      aidl_audio_set_config.sourceAseConfiguration->push_back(aidl_ase_config_source);
      // STACK:
      stack_audio_set_config.confs.source.push_back(stack_ase_config_source);
    }

    // Use Mono ASE config if not Left nor Right
    if (stack_audio_set_config.confs.source.empty()) {
      auto [aidl_ase_config_source, stack_ase_config_source] =
            PrepareReferenceAseDirectionConfigLc3(true, true, true);
              PrepareReferenceAseDirectionConfigLc3(false, false, true);
      // AIDL:
      aidl_audio_set_config.sourceAseConfiguration->push_back(aidl_ase_config_source);
      // STACK:
      stack_audio_set_config.confs.source.push_back(stack_ase_config_source);
    }
  }

  return {aidl_audio_set_config, stack_audio_set_config};
}
@@ -886,13 +915,29 @@ TEST(BluetoothAudioClientInterfaceAidlTest, testGetStackMetadataFromAidlFormat)
}

TEST(BluetoothAudioClientInterfaceAidlTest, testGetStackUnicastConfigurationFromAidlFormat) {
  auto source_allocations = le_audio::codec_spec_conf::kLeAudioLocationFrontLeft |
                            le_audio::codec_spec_conf::kLeAudioLocationFrontRight;
  auto [aidl_config, expected_stack_config] = test_utils::PrepareReferenceAseConfigurationSetting(
          ::bluetooth::le_audio::types::LeAudioContextType::GAME, true);
          ::bluetooth::le_audio::types::LeAudioContextType::GAME, source_allocations);

  auto stack_config = GetStackUnicastConfigurationFromAidlFormat(
          ::bluetooth::le_audio::types::LeAudioContextType::GAME, aidl_config);
  ASSERT_TRUE(stack_config.has_value());
  ASSERT_EQ(stack_config->confs.sink.size(), 2ul);
  ASSERT_EQ(stack_config->confs.source.size(), 2ul);
  ASSERT_EQ(*stack_config, expected_stack_config);
}

TEST(BluetoothAudioClientInterfaceAidlTest, testGetStackUnicastConfigurationFromAidlFormatMonoLoc) {
  auto source_allocations = le_audio::codec_spec_conf::kLeAudioLocationMonoAudio;
  auto [aidl_config, expected_stack_config] = test_utils::PrepareReferenceAseConfigurationSetting(
          ::bluetooth::le_audio::types::LeAudioContextType::CONVERSATIONAL, source_allocations);

  auto stack_config = GetStackUnicastConfigurationFromAidlFormat(
          ::bluetooth::le_audio::types::LeAudioContextType::CONVERSATIONAL, aidl_config);
  ASSERT_TRUE(stack_config.has_value());
  ASSERT_EQ(stack_config->confs.sink.size(), 2ul);
  ASSERT_EQ(stack_config->confs.source.size(), 1ul);
  ASSERT_EQ(*stack_config, expected_stack_config);
}

+9 −9
Original line number Diff line number Diff line
@@ -1463,19 +1463,11 @@ public:
      group_add_node(group_id, address);
    }

    leAudioDevice->src_audio_locations_ = source_audio_location;
    leAudioDevice->snk_audio_locations_ = sink_audio_location;
    if (sink_audio_location != 0) {
      leAudioDevice->audio_directions_ |= bluetooth::le_audio::types::kLeAudioDirectionSink;
    }

    callbacks_->OnSinkAudioLocationAvailable(leAudioDevice->address_,
                                             leAudioDevice->snk_audio_locations_.to_ulong());

    leAudioDevice->src_audio_locations_ = source_audio_location;
    if (source_audio_location != 0) {
      leAudioDevice->audio_directions_ |= bluetooth::le_audio::types::kLeAudioDirectionSource;
    }

    BidirectionalPair<AudioContexts> supported_contexts = {
            .sink = AudioContexts(sink_supported_context_types),
            .source = AudioContexts(source_supported_context_types),
@@ -1490,6 +1482,14 @@ public:
      log::warn("Could not load Handles");
    }

    /* Presence of PAC characteristic for a direction means support for that direction */
    if (leAudioDevice->src_audio_locations_hdls_.val_hdl != 0) {
      leAudioDevice->audio_directions_ |= bluetooth::le_audio::types::kLeAudioDirectionSource;
    }
    if (leAudioDevice->snk_audio_locations_hdls_.val_hdl != 0) {
      leAudioDevice->audio_directions_ |= bluetooth::le_audio::types::kLeAudioDirectionSink;
    }

    if (!DeserializeSinkPacs(leAudioDevice, sink_pacs)) {
      /* If PACs are invalid, just say whole cache is invalid */
      leAudioDevice->known_service_handles_ = false;
+109 −0
Original line number Diff line number Diff line
@@ -3738,6 +3738,115 @@ TEST_F(UnicastTestNoInit, LoadStoredEarbudsCsisGrouped) {
  DisconnectLeAudioWithAclClose(test_address1, 2);
}

TEST_F(UnicastTest, LoadStoredBandedHeadphones) {
  const RawAddress test_address0 = GetTestAddress(0);
  uint16_t conn_id = 1;

  SetSampleDatabaseEarbudsValid(
          conn_id, test_address0,
          codec_spec_conf::kLeAudioLocationFrontLeft | codec_spec_conf::kLeAudioLocationFrontRight,
          codec_spec_conf::kLeAudioLocationMonoAudio, 2, 1, 0x0004,
          /* source sample freq 16khz */ false, /*add_csis*/
          true,                                 /*add_cas*/
          true,                                 /*add_pacs*/
          true,                                 /*add_ascs*/
          0, 0);
  EXPECT_CALL(mock_audio_hal_client_callbacks_,
              OnConnectionState(ConnectionState::CONNECTED, test_address0))
          .Times(1);

  /* Connect and fill the device storage */
  ConnectLeAudio(test_address0);

  std::vector<uint8_t> handles;
  LeAudioClient::GetHandlesForStorage(test_address0, handles);

  std::vector<uint8_t> ases;
  LeAudioClient::GetAsesForStorage(test_address0, ases);

  std::vector<uint8_t> src_pacs;
  LeAudioClient::GetSourcePacsForStorage(test_address0, src_pacs);

  std::vector<uint8_t> snk_pacs;
  LeAudioClient::GetSinkPacsForStorage(test_address0, snk_pacs);

  /* Disconnect & Cleanup */
  DisconnectLeAudioWithAclClose(test_address0, conn_id);
  if (LeAudioClient::IsLeAudioClientRunning()) {
    LeAudioClient::Cleanup();
    ASSERT_FALSE(LeAudioClient::IsLeAudioClientRunning());
  }

  Mock::VerifyAndClearExpectations(&mock_hal_2_1_verifier);
  Mock::VerifyAndClearExpectations(&mock_storage_load);

  // Load devices from the storage when storage API is called
  bool autoconnect = true;
  EXPECT_CALL(mock_storage_load, Call()).WillOnce([&]() {
    do_in_main_thread(base::BindOnce(&LeAudioClient::AddFromStorage, test_address0, autoconnect,
                                     codec_spec_conf::kLeAudioLocationFrontLeft |
                                             codec_spec_conf::kLeAudioLocationFrontRight,
                                     codec_spec_conf::kLeAudioLocationMonoAudio, 0xff, 0xff,
                                     std::move(handles), std::move(snk_pacs), std::move(src_pacs),
                                     std::move(ases)));
    SyncOnMainLoop();
  });

  /* Prepare  mock to not inject connect event so the device can stay in
   * CONNECTING state*/
  ON_CALL(mock_gatt_interface_, Open(_, _, BTM_BLE_DIRECT_CONNECTION, false))
          .WillByDefault(DoAll(Return()));

  // Re-Initialize & load from storage
  BtaAppRegisterCallback app_register_callback;
  ON_CALL(mock_gatt_interface_, AppRegister(_, _, _))
          .WillByDefault(DoAll(SaveArg<0>(&gatt_callback), SaveArg<1>(&app_register_callback)));
  std::vector<::bluetooth::le_audio::btle_audio_codec_config_t> framework_encode_preference;
  LeAudioClient::Initialize(
          &mock_audio_hal_client_callbacks_,
          base::Bind([](MockFunction<void()>* foo) { foo->Call(); }, &mock_storage_load),
          base::Bind([](MockFunction<bool()>* foo) { return foo->Call(); }, &mock_hal_2_1_verifier),
          framework_encode_preference);
  if (app_register_callback) {
    app_register_callback.Run(gatt_if, GATT_SUCCESS);
  }

  InjectConnectedEvent(test_address0, conn_id);
  SyncOnMainLoop();
  Mock::VerifyAndClearExpectations(&mock_gatt_interface_);

  // Verify if all went well and we got the proper group
  auto group_id = MockDeviceGroups::DeviceGroups::Get()->GetGroupId(test_address0);
  std::vector<RawAddress> devs = LeAudioClient::Get()->GetGroupDevices(group_id);
  ASSERT_NE(std::find(devs.begin(), devs.end(), test_address0), devs.end());

  SetUpMockCodecManager(::bluetooth::le_audio::types::CodecLocation::HOST);

  // Start streaming
  LeAudioClient::Get()->GroupSetActive(group_id);
  SyncOnMainLoop();

  EXPECT_CALL(mock_state_machine_, StartStream(_, _, _, _)).Times(1);
  EXPECT_CALL(mock_audio_hal_client_callbacks_, OnAudioGroupCurrentCodecConf(group_id, _, _))
          .Times(1);
  StartStreaming(AUDIO_USAGE_MEDIA, AUDIO_CONTENT_TYPE_MUSIC, group_id);

  SyncOnMainLoop();
  Mock::VerifyAndClearExpectations(&mock_audio_hal_client_callbacks_);
  Mock::VerifyAndClearExpectations(&mock_state_machine_);

  ASSERT_NE(0lu, streaming_groups.count(group_id));
  auto group = streaming_groups.at(group_id);
  ASSERT_NE(group, nullptr);

  auto device = group->GetFirstDevice();
  ASSERT_NE(device, nullptr);
  ASSERT_EQ(device->audio_directions_, bluetooth::le_audio::types::kLeAudioDirectionSink |
                                               bluetooth::le_audio::types::kLeAudioDirectionSource);

  DisconnectLeAudioWithAclClose(test_address0, conn_id);
}

TEST_F(UnicastTestNoInit, ServiceChangedBeforeServiceIsConnected) {
  // Prepare two devices
  uint8_t group_size = 2;