Loading system/gd/rust/topshim/Android.bp +3 −0 Original line number Diff line number Diff line Loading @@ -52,6 +52,7 @@ cc_library_static { "gatt/gatt_ble_scanner_shim.cc", "gatt/gatt_shim.cc", "hfp/hfp_shim.cc", "le_audio/le_audio_shim.cc", ], generated_headers: [ "BluetoothGeneratedDumpsysDataSchema_h", Loading Loading @@ -87,6 +88,7 @@ gensrcs { "src/profiles/avrcp.rs", "src/profiles/gatt.rs", "src/profiles/hfp.rs", "src/profiles/le_audio.rs", ], output_extension: "rs.h", export_include_dirs: ["."], Loading @@ -103,6 +105,7 @@ gensrcs { "src/profiles/avrcp.rs", "src/profiles/gatt.rs", "src/profiles/hfp.rs", "src/profiles/le_audio.rs", ], output_extension: "cc", export_include_dirs: ["."], Loading system/gd/rust/topshim/BUILD.gn +3 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ cxxbridge_header("btif_bridge_header") { "src/profiles/avrcp.rs", "src/profiles/gatt.rs", "src/profiles/hfp.rs", "src/profiles/le_audio.rs", ] all_dependent_configs = [ ":rust_topshim_config" ] deps = [ ":cxxlibheader" ] Loading @@ -43,6 +44,7 @@ cxxbridge_cc("btif_bridge_code") { "src/profiles/avrcp.rs", "src/profiles/gatt.rs", "src/profiles/hfp.rs", "src/profiles/le_audio.rs", ] deps = [ ":btif_bridge_header", Loading @@ -64,6 +66,7 @@ source_set("btif_cxx_bridge_code") { "gatt/gatt_ble_scanner_shim.cc", "gatt/gatt_shim.cc", "hfp/hfp_shim.cc", "le_audio/le_audio_shim.cc", "metrics/metrics_shim.cc", ] Loading system/gd/rust/topshim/le_audio/le_audio_shim.cc 0 → 100644 +564 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "gd/rust/topshim/le_audio/le_audio_shim.h" #include <bluetooth/log.h> #include <hardware/bluetooth.h> #include <vector> #include "bta/le_audio/le_audio_types.h" #include "os/log.h" #include "src/profiles/le_audio.rs.h" #include "types/raw_address.h" namespace rusty = ::bluetooth::topshim::rust; namespace bluetooth { namespace topshim { namespace rust { namespace internal { static LeAudioClientIntf* g_lea_client_if; static uint8_t from_rust_btle_audio_direction(BtLeAudioDirection direction) { switch (direction) { case BtLeAudioDirection::Sink: return le_audio::types::kLeAudioDirectionSink; case BtLeAudioDirection::Source: return le_audio::types::kLeAudioDirectionSource; case BtLeAudioDirection::Both: return le_audio::types::kLeAudioDirectionBoth; default: log::assert_that(false, "Unhandled enum value from C++"); } return 0; } static le_audio::btle_audio_codec_config_t from_rust_btle_audio_codec_config( BtLeAudioCodecConfig codec_config) { switch (codec_config.codec_type) { case static_cast<int>(BtLeAudioCodecIndex::SrcLc3): return le_audio::btle_audio_codec_config_t{ .codec_type = le_audio::btle_audio_codec_index_t::LE_AUDIO_CODEC_INDEX_SOURCE_LC3}; default: log::assert_that(false, "Unhandled enum value from C++"); } return le_audio::btle_audio_codec_config_t{}; } static BtLeAudioCodecConfig to_rust_btle_audio_codec_config( le_audio::btle_audio_codec_config_t codec_config) { switch (codec_config.codec_type) { case le_audio::btle_audio_codec_index_t::LE_AUDIO_CODEC_INDEX_SOURCE_LC3: return BtLeAudioCodecConfig{.codec_type = static_cast<int>(BtLeAudioCodecIndex::SrcLc3)}; default: log::assert_that(false, "Unhandled enum value from C++"); } return BtLeAudioCodecConfig{}; } static ::rust::vec<BtLeAudioCodecConfig> to_rust_btle_audio_codec_config_vec( std::vector<le_audio::btle_audio_codec_config_t> codec_configs) { ::rust::vec<BtLeAudioCodecConfig> rconfigs; for (auto c : codec_configs) { rconfigs.push_back(to_rust_btle_audio_codec_config(c)); } return rconfigs; } static BtLeAudioConnectionState to_rust_btle_audio_connection_state( le_audio::ConnectionState state) { switch (state) { case le_audio::ConnectionState::DISCONNECTED: return BtLeAudioConnectionState::Disconnected; case le_audio::ConnectionState::CONNECTING: return BtLeAudioConnectionState::Connecting; case le_audio::ConnectionState::CONNECTED: return BtLeAudioConnectionState::Connected; case le_audio::ConnectionState::DISCONNECTING: return BtLeAudioConnectionState::Disconnecting; default: log::assert_that(false, "Unhandled enum value from C++"); } return BtLeAudioConnectionState{}; } static BtLeAudioGroupStatus to_rust_btle_audio_group_status(le_audio::GroupStatus status) { switch (status) { case le_audio::GroupStatus::INACTIVE: return BtLeAudioGroupStatus::Inactive; case le_audio::GroupStatus::ACTIVE: return BtLeAudioGroupStatus::Active; case le_audio::GroupStatus::TURNED_IDLE_DURING_CALL: return BtLeAudioGroupStatus::TurnedIdleDuringCall; default: log::assert_that(false, "Unhandled enum value from C++"); } return BtLeAudioGroupStatus{}; } static BtLeAudioGroupNodeStatus to_rust_btle_audio_group_node_status( le_audio::GroupNodeStatus status) { switch (status) { case le_audio::GroupNodeStatus::ADDED: return BtLeAudioGroupNodeStatus::Added; case le_audio::GroupNodeStatus::REMOVED: return BtLeAudioGroupNodeStatus::Removed; default: log::assert_that(false, "Unhandled enum value from C++"); } return BtLeAudioGroupNodeStatus{}; } static BtLeAudioUnicastMonitorModeStatus to_rust_btle_audio_unicast_monitor_mode_status( le_audio::UnicastMonitorModeStatus status) { switch (status) { case le_audio::UnicastMonitorModeStatus::STREAMING_REQUESTED: return BtLeAudioUnicastMonitorModeStatus::StreamingRequested; case le_audio::UnicastMonitorModeStatus::STREAMING: return BtLeAudioUnicastMonitorModeStatus::Streaming; case le_audio::UnicastMonitorModeStatus::STREAMING_SUSPENDED: return BtLeAudioUnicastMonitorModeStatus::StreamingSuspended; default: log::assert_that(false, "Unhandled enum value from C++"); } return BtLeAudioUnicastMonitorModeStatus{}; } static BtLeAudioDirection to_rust_btle_audio_direction(uint8_t direction) { switch (direction) { case le_audio::types::kLeAudioDirectionSink: return BtLeAudioDirection::Sink; case le_audio::types::kLeAudioDirectionSource: return BtLeAudioDirection::Source; case le_audio::types::kLeAudioDirectionBoth: return BtLeAudioDirection::Both; default: log::assert_that(false, "Unhandled enum value from C++"); } return BtLeAudioDirection{}; } static BtLeAudioGroupStreamStatus to_rust_btle_audio_group_stream_status( le_audio::GroupStreamStatus status) { switch (status) { case le_audio::GroupStreamStatus::IDLE: return BtLeAudioGroupStreamStatus::Idle; case le_audio::GroupStreamStatus::STREAMING: return BtLeAudioGroupStreamStatus::Streaming; case le_audio::GroupStreamStatus::RELEASING: return BtLeAudioGroupStreamStatus::Releasing; case le_audio::GroupStreamStatus::SUSPENDING: return BtLeAudioGroupStreamStatus::Suspending; case le_audio::GroupStreamStatus::SUSPENDED: return BtLeAudioGroupStreamStatus::Suspended; case le_audio::GroupStreamStatus::CONFIGURED_AUTONOMOUS: return BtLeAudioGroupStreamStatus::ConfiguredAutonomous; case le_audio::GroupStreamStatus::CONFIGURED_BY_USER: return BtLeAudioGroupStreamStatus::ConfiguredByUser; case le_audio::GroupStreamStatus::DESTROYED: return BtLeAudioGroupStreamStatus::Destroyed; default: log::assert_that(false, "Unhandled enum value from C++"); } return BtLeAudioGroupStreamStatus{}; } static void initialized_cb() { le_audio_initialized_callback(); } static void connection_state_cb(le_audio::ConnectionState state, const RawAddress& address) { le_audio_connection_state_callback(to_rust_btle_audio_connection_state(state), address); } static void group_status_cb(int group_id, le_audio::GroupStatus group_status) { le_audio_group_status_callback(group_id, to_rust_btle_audio_group_status(group_status)); } static void group_node_status_cb( const RawAddress& bd_addr, int group_id, le_audio::GroupNodeStatus node_status) { le_audio_group_node_status_callback( bd_addr, group_id, to_rust_btle_audio_group_node_status(node_status)); } static void unicast_monitor_mode_status_cb( uint8_t direction, le_audio::UnicastMonitorModeStatus status) { le_audio_unicast_monitor_mode_status_callback( to_rust_btle_audio_direction(direction), to_rust_btle_audio_unicast_monitor_mode_status(status)); } static void group_stream_status_cb(int group_id, le_audio::GroupStreamStatus status) { le_audio_group_stream_status_callback(group_id, to_rust_btle_audio_group_stream_status(status)); } static void audio_conf_cb( uint8_t direction, int group_id, uint32_t snk_audio_location, uint32_t src_audio_location, uint16_t avail_cont) { le_audio_audio_conf_callback( direction, group_id, snk_audio_location, src_audio_location, avail_cont); } static void sink_audio_location_available_cb( const RawAddress& address, uint32_t snk_audio_locations) { le_audio_sink_audio_location_available_callback(address, snk_audio_locations); } static void audio_local_codec_capabilities_cb( std::vector<le_audio::btle_audio_codec_config_t> local_input_capa_codec_conf, std::vector<le_audio::btle_audio_codec_config_t> local_output_capa_codec_conf) { le_audio_audio_local_codec_capabilities_callback( to_rust_btle_audio_codec_config_vec(local_input_capa_codec_conf), to_rust_btle_audio_codec_config_vec(local_output_capa_codec_conf)); } static void audio_group_codec_conf_cb( int group_id, le_audio::btle_audio_codec_config_t input_codec_conf, le_audio::btle_audio_codec_config_t output_codec_conf, std::vector<le_audio::btle_audio_codec_config_t> input_selectable_codec_conf, std::vector<le_audio::btle_audio_codec_config_t> output_selectable_codec_conf) { le_audio_audio_group_codec_conf_callback( group_id, to_rust_btle_audio_codec_config(input_codec_conf), to_rust_btle_audio_codec_config(output_codec_conf), to_rust_btle_audio_codec_config_vec(input_selectable_codec_conf), to_rust_btle_audio_codec_config_vec(output_selectable_codec_conf)); } } // namespace internal class DBusLeAudioClientCallbacks : public le_audio::LeAudioClientCallbacks { public: static le_audio::LeAudioClientCallbacks* GetInstance() { static auto instance = new DBusLeAudioClientCallbacks(); return instance; } DBusLeAudioClientCallbacks(){}; void OnInitialized() override { log::info(""); topshim::rust::internal::initialized_cb(); } void OnConnectionState(le_audio::ConnectionState state, const RawAddress& address) override { log::info("state={}, address={}", static_cast<int>(state), ADDRESS_TO_LOGGABLE_CSTR(address)); topshim::rust::internal::connection_state_cb(state, address); } void OnGroupStatus(int group_id, le_audio::GroupStatus group_status) override { log::info("group_id={}, group_status={}", group_id, static_cast<int>(group_status)); topshim::rust::internal::group_status_cb(group_id, group_status); } void OnGroupNodeStatus( const RawAddress& bd_addr, int group_id, le_audio::GroupNodeStatus node_status) { log::info( "bd_addr={}, group_id={}, node_status={}", ADDRESS_TO_LOGGABLE_CSTR(bd_addr), group_id, static_cast<int>(node_status)); topshim::rust::internal::group_node_status_cb(bd_addr, group_id, node_status); } void OnAudioConf( uint8_t direction, int group_id, uint32_t snk_audio_location, uint32_t src_audio_location, uint16_t avail_cont) { log::info( "direction={}, group_id={}, snk_audio_location={}, src_audio_location={}, avail_cont={}", direction, group_id, snk_audio_location, src_audio_location, avail_cont); topshim::rust::internal::audio_conf_cb( direction, group_id, snk_audio_location, src_audio_location, avail_cont); } void OnSinkAudioLocationAvailable(const RawAddress& address, uint32_t snk_audio_locations) { log::info( "address={}, snk_audio_locations={}", ADDRESS_TO_LOGGABLE_CSTR(address), snk_audio_locations); topshim::rust::internal::sink_audio_location_available_cb(address, snk_audio_locations); } void OnAudioLocalCodecCapabilities( std::vector<le_audio::btle_audio_codec_config_t> local_input_capa_codec_conf, std::vector<le_audio::btle_audio_codec_config_t> local_output_capa_codec_conf) { log::info(""); topshim::rust::internal::audio_local_codec_capabilities_cb( local_input_capa_codec_conf, local_output_capa_codec_conf); } void OnAudioGroupCodecConf( int group_id, le_audio::btle_audio_codec_config_t input_codec_conf, le_audio::btle_audio_codec_config_t output_codec_conf, std::vector<le_audio::btle_audio_codec_config_t> input_selectable_codec_conf, std::vector<le_audio::btle_audio_codec_config_t> output_selectable_codec_conf) { log::info("group_id={}", group_id); topshim::rust::internal::audio_group_codec_conf_cb( group_id, input_codec_conf, output_codec_conf, input_selectable_codec_conf, output_selectable_codec_conf); } void OnAudioGroupCurrentCodecConf( int group_id, le_audio::btle_audio_codec_config_t input_codec_conf, le_audio::btle_audio_codec_config_t output_codec_conf) { log::info( "group_id={}, input_codec_conf={}, output_codec_conf={}", group_id, input_codec_conf.ToString(), output_codec_conf.ToString()); } void OnAudioGroupSelectableCodecConf( int group_id, std::vector<le_audio::btle_audio_codec_config_t> input_selectable_codec_conf, std::vector<le_audio::btle_audio_codec_config_t> output_selectable_codec_conf) { log::info( "group_id={}, input_selectable_codec_conf.size={}, output_selectable_codec_conf.size={}", group_id, input_selectable_codec_conf.size(), output_selectable_codec_conf.size()); } void OnHealthBasedRecommendationAction( const RawAddress& address, le_audio::LeAudioHealthBasedAction action) { log::info("address={}, action={}", ADDRESS_TO_LOGGABLE_CSTR(address), static_cast<int>(action)); } void OnHealthBasedGroupRecommendationAction( int group_id, le_audio::LeAudioHealthBasedAction action) { log::info("group_id={}, action={}", group_id, static_cast<int>(action)); } void OnUnicastMonitorModeStatus(uint8_t direction, le_audio::UnicastMonitorModeStatus status) { log::info("direction={}, status={}", direction, static_cast<int>(status)); topshim::rust::internal::unicast_monitor_mode_status_cb(direction, status); } void OnGroupStreamStatus(int group_id, le_audio::GroupStreamStatus status) { log::info("group_id={}, status={}", group_id, static_cast<int>(status)); topshim::rust::internal::group_stream_status_cb(group_id, status); } }; void LeAudioClientIntf::init(/* LeAudioClientCallbacks* callbacks, const std::vector<le_audio::btle_audio_codec_config_t>& offloading_preference */) { return intf_->Initialize(DBusLeAudioClientCallbacks::GetInstance(), {}); } void LeAudioClientIntf::connect(RawAddress addr) { return intf_->Connect(addr); } void LeAudioClientIntf::disconnect(RawAddress addr) { return intf_->Disconnect(addr); } void LeAudioClientIntf::set_enable_state(RawAddress addr, bool enabled) { return intf_->SetEnableState(addr, enabled); } void LeAudioClientIntf::cleanup() { return intf_->Cleanup(); } void LeAudioClientIntf::remove_device(RawAddress addr) { return intf_->RemoveDevice(addr); } void LeAudioClientIntf::group_add_node(int group_id, RawAddress addr) { return intf_->GroupAddNode(group_id, addr); } void LeAudioClientIntf::group_remove_node(int group_id, RawAddress addr) { return intf_->GroupRemoveNode(group_id, addr); } void LeAudioClientIntf::group_set_active(int group_id) { return intf_->GroupSetActive(group_id); } void LeAudioClientIntf::set_codec_config_preference( int group_id, BtLeAudioCodecConfig input_codec_config, BtLeAudioCodecConfig output_codec_config) { return intf_->SetCodecConfigPreference( group_id, internal::from_rust_btle_audio_codec_config(input_codec_config), internal::from_rust_btle_audio_codec_config(output_codec_config)); } void LeAudioClientIntf::set_ccid_information(int ccid, int context_type) { return intf_->SetCcidInformation(ccid, context_type); } void LeAudioClientIntf::set_in_call(bool in_call) { return intf_->SetInCall(in_call); } void LeAudioClientIntf::send_audio_profile_preferences( int group_id, bool is_output_preference_le_audio, bool is_duplex_preference_le_audio) { return intf_->SendAudioProfilePreferences( group_id, is_output_preference_le_audio, is_duplex_preference_le_audio); } void LeAudioClientIntf::set_unicast_monitor_mode(BtLeAudioDirection direction, bool enable) { return intf_->SetUnicastMonitorMode(internal::from_rust_btle_audio_direction(direction), enable); } std::unique_ptr<LeAudioClientIntf> GetLeAudioClientProfile(const unsigned char* btif) { if (internal::g_lea_client_if) std::abort(); const bt_interface_t* btif_ = reinterpret_cast<const bt_interface_t*>(btif); auto lea_client_if = std::make_unique<LeAudioClientIntf>(const_cast<le_audio::LeAudioClientInterface*>( reinterpret_cast<const le_audio::LeAudioClientInterface*>( btif_->get_profile_interface("le_audio")))); internal::g_lea_client_if = lea_client_if.get(); return lea_client_if; } bool LeAudioClientIntf::host_start_audio_request() { return ::bluetooth::audio::le_audio::HostStartRequest(); } void LeAudioClientIntf::host_stop_audio_request() { ::bluetooth::audio::le_audio::HostStopRequest(); } bool LeAudioClientIntf::peer_start_audio_request() { return ::bluetooth::audio::le_audio::PeerStartRequest(); } void LeAudioClientIntf::peer_stop_audio_request() { ::bluetooth::audio::le_audio::PeerStopRequest(); } static BtLePcmConfig to_rust_btle_pcm_params(audio::le_audio::btle_pcm_parameters pcm_params) { return BtLePcmConfig{ .data_interval_us = pcm_params.data_interval_us, .sample_rate = pcm_params.sample_rate, .bits_per_sample = pcm_params.bits_per_sample, .channels_count = pcm_params.channels_count, }; } BtLePcmConfig LeAudioClientIntf::get_host_pcm_config() { return to_rust_btle_pcm_params(::bluetooth::audio::le_audio::GetHostPcmConfig()); } BtLePcmConfig LeAudioClientIntf::get_peer_pcm_config() { return to_rust_btle_pcm_params(::bluetooth::audio::le_audio::GetPeerPcmConfig()); } bool LeAudioClientIntf::get_host_stream_started() { return ::bluetooth::audio::le_audio::GetHostStreamStarted(); } bool LeAudioClientIntf::get_peer_stream_started() { return ::bluetooth::audio::le_audio::GetPeerStreamStarted(); } void LeAudioClientIntf::source_metadata_changed(::rust::Vec<SourceMetadata> metadata) { if (metadata.empty()) { log::warn("Received empty metadata."); return; } // This will be referenced across threads. static std::vector<playback_track_metadata_v7> tracks; tracks.clear(); tracks.reserve(metadata.size()); for (auto m : metadata) { struct playback_track_metadata_v7 track = { .base = { .usage = static_cast<audio_usage_t>(m.usage), .content_type = static_cast<audio_content_type_t>(m.content_type), .gain = static_cast<float>(m.gain), }, .channel_mask = AUDIO_CHANNEL_NONE, // unused .tags = "", }; tracks.push_back(track); } source_metadata_v7_t data = { .track_count = tracks.size(), .tracks = tracks.data(), }; ::bluetooth::audio::le_audio::SourceMetadataChanged(data); } void LeAudioClientIntf::sink_metadata_changed(::rust::Vec<SinkMetadata> metadata) { if (metadata.empty()) { log::warn("Received empty metadata."); return; } // This will be referenced across threads. static std::vector<record_track_metadata_v7> tracks; tracks.clear(); tracks.reserve(metadata.size()); for (auto m : metadata) { struct record_track_metadata_v7 track = { .base = { .source = static_cast<audio_source_t>(m.source), .gain = static_cast<float>(m.gain), .dest_device = AUDIO_DEVICE_IN_DEFAULT, .dest_device_address = "", // unused }, .channel_mask = AUDIO_CHANNEL_NONE, // unused .tags = "", }; tracks.push_back(track); } const sink_metadata_v7_t data = { .track_count = tracks.size(), .tracks = tracks.data(), }; ::bluetooth::audio::le_audio::SinkMetadataChanged(data); } } // namespace rust } // namespace topshim } // namespace bluetooth system/gd/rust/topshim/le_audio/le_audio_shim.h 0 → 100644 +83 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include <memory> #include "audio_hal_interface/le_audio_software_host.h" #include "include/hardware/bt_le_audio.h" #include "rust/cxx.h" #include "types/raw_address.h" namespace bluetooth { namespace topshim { namespace rust { struct BtLeAudioCodecConfig; enum class BtLeAudioDirection : uint8_t; struct BtLePcmConfig; struct SourceMetadata; struct SinkMetadata; class LeAudioClientIntf { public: LeAudioClientIntf(le_audio::LeAudioClientInterface* intf) : intf_(intf){}; void init( /* LeAudioClientCallbacks* callbacks, const std::vector<le_audio::btle_audio_codec_config_t>& offloading_preference */); void connect(RawAddress addr); void disconnect(RawAddress addr); void set_enable_state(RawAddress addr, bool enabled); void cleanup(); void remove_device(RawAddress addr); void group_add_node(int group_id, RawAddress addr); void group_remove_node(int group_id, RawAddress addr); void group_set_active(int group_id); void set_codec_config_preference( int group_id, BtLeAudioCodecConfig input_codec_config, BtLeAudioCodecConfig output_codec_config); void set_ccid_information(int ccid, int context_type); void set_in_call(bool in_call); void send_audio_profile_preferences( int group_id, bool is_output_preference_le_audio, bool is_duplex_preference_le_audio); void set_unicast_monitor_mode(BtLeAudioDirection direction, bool enable); // interface for audio server bool host_start_audio_request(); void host_stop_audio_request(); bool peer_start_audio_request(); void peer_stop_audio_request(); BtLePcmConfig get_host_pcm_config(); BtLePcmConfig get_peer_pcm_config(); bool get_host_stream_started(); bool get_peer_stream_started(); void source_metadata_changed(::rust::Vec<SourceMetadata> metadata); void sink_metadata_changed(::rust::Vec<SinkMetadata> metadata); private: le_audio::LeAudioClientInterface* intf_; }; std::unique_ptr<LeAudioClientIntf> GetLeAudioClientProfile(const unsigned char* btif); } // namespace rust } // namespace topshim } // namespace bluetooth Loading
system/gd/rust/topshim/Android.bp +3 −0 Original line number Diff line number Diff line Loading @@ -52,6 +52,7 @@ cc_library_static { "gatt/gatt_ble_scanner_shim.cc", "gatt/gatt_shim.cc", "hfp/hfp_shim.cc", "le_audio/le_audio_shim.cc", ], generated_headers: [ "BluetoothGeneratedDumpsysDataSchema_h", Loading Loading @@ -87,6 +88,7 @@ gensrcs { "src/profiles/avrcp.rs", "src/profiles/gatt.rs", "src/profiles/hfp.rs", "src/profiles/le_audio.rs", ], output_extension: "rs.h", export_include_dirs: ["."], Loading @@ -103,6 +105,7 @@ gensrcs { "src/profiles/avrcp.rs", "src/profiles/gatt.rs", "src/profiles/hfp.rs", "src/profiles/le_audio.rs", ], output_extension: "cc", export_include_dirs: ["."], Loading
system/gd/rust/topshim/BUILD.gn +3 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ cxxbridge_header("btif_bridge_header") { "src/profiles/avrcp.rs", "src/profiles/gatt.rs", "src/profiles/hfp.rs", "src/profiles/le_audio.rs", ] all_dependent_configs = [ ":rust_topshim_config" ] deps = [ ":cxxlibheader" ] Loading @@ -43,6 +44,7 @@ cxxbridge_cc("btif_bridge_code") { "src/profiles/avrcp.rs", "src/profiles/gatt.rs", "src/profiles/hfp.rs", "src/profiles/le_audio.rs", ] deps = [ ":btif_bridge_header", Loading @@ -64,6 +66,7 @@ source_set("btif_cxx_bridge_code") { "gatt/gatt_ble_scanner_shim.cc", "gatt/gatt_shim.cc", "hfp/hfp_shim.cc", "le_audio/le_audio_shim.cc", "metrics/metrics_shim.cc", ] Loading
system/gd/rust/topshim/le_audio/le_audio_shim.cc 0 → 100644 +564 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "gd/rust/topshim/le_audio/le_audio_shim.h" #include <bluetooth/log.h> #include <hardware/bluetooth.h> #include <vector> #include "bta/le_audio/le_audio_types.h" #include "os/log.h" #include "src/profiles/le_audio.rs.h" #include "types/raw_address.h" namespace rusty = ::bluetooth::topshim::rust; namespace bluetooth { namespace topshim { namespace rust { namespace internal { static LeAudioClientIntf* g_lea_client_if; static uint8_t from_rust_btle_audio_direction(BtLeAudioDirection direction) { switch (direction) { case BtLeAudioDirection::Sink: return le_audio::types::kLeAudioDirectionSink; case BtLeAudioDirection::Source: return le_audio::types::kLeAudioDirectionSource; case BtLeAudioDirection::Both: return le_audio::types::kLeAudioDirectionBoth; default: log::assert_that(false, "Unhandled enum value from C++"); } return 0; } static le_audio::btle_audio_codec_config_t from_rust_btle_audio_codec_config( BtLeAudioCodecConfig codec_config) { switch (codec_config.codec_type) { case static_cast<int>(BtLeAudioCodecIndex::SrcLc3): return le_audio::btle_audio_codec_config_t{ .codec_type = le_audio::btle_audio_codec_index_t::LE_AUDIO_CODEC_INDEX_SOURCE_LC3}; default: log::assert_that(false, "Unhandled enum value from C++"); } return le_audio::btle_audio_codec_config_t{}; } static BtLeAudioCodecConfig to_rust_btle_audio_codec_config( le_audio::btle_audio_codec_config_t codec_config) { switch (codec_config.codec_type) { case le_audio::btle_audio_codec_index_t::LE_AUDIO_CODEC_INDEX_SOURCE_LC3: return BtLeAudioCodecConfig{.codec_type = static_cast<int>(BtLeAudioCodecIndex::SrcLc3)}; default: log::assert_that(false, "Unhandled enum value from C++"); } return BtLeAudioCodecConfig{}; } static ::rust::vec<BtLeAudioCodecConfig> to_rust_btle_audio_codec_config_vec( std::vector<le_audio::btle_audio_codec_config_t> codec_configs) { ::rust::vec<BtLeAudioCodecConfig> rconfigs; for (auto c : codec_configs) { rconfigs.push_back(to_rust_btle_audio_codec_config(c)); } return rconfigs; } static BtLeAudioConnectionState to_rust_btle_audio_connection_state( le_audio::ConnectionState state) { switch (state) { case le_audio::ConnectionState::DISCONNECTED: return BtLeAudioConnectionState::Disconnected; case le_audio::ConnectionState::CONNECTING: return BtLeAudioConnectionState::Connecting; case le_audio::ConnectionState::CONNECTED: return BtLeAudioConnectionState::Connected; case le_audio::ConnectionState::DISCONNECTING: return BtLeAudioConnectionState::Disconnecting; default: log::assert_that(false, "Unhandled enum value from C++"); } return BtLeAudioConnectionState{}; } static BtLeAudioGroupStatus to_rust_btle_audio_group_status(le_audio::GroupStatus status) { switch (status) { case le_audio::GroupStatus::INACTIVE: return BtLeAudioGroupStatus::Inactive; case le_audio::GroupStatus::ACTIVE: return BtLeAudioGroupStatus::Active; case le_audio::GroupStatus::TURNED_IDLE_DURING_CALL: return BtLeAudioGroupStatus::TurnedIdleDuringCall; default: log::assert_that(false, "Unhandled enum value from C++"); } return BtLeAudioGroupStatus{}; } static BtLeAudioGroupNodeStatus to_rust_btle_audio_group_node_status( le_audio::GroupNodeStatus status) { switch (status) { case le_audio::GroupNodeStatus::ADDED: return BtLeAudioGroupNodeStatus::Added; case le_audio::GroupNodeStatus::REMOVED: return BtLeAudioGroupNodeStatus::Removed; default: log::assert_that(false, "Unhandled enum value from C++"); } return BtLeAudioGroupNodeStatus{}; } static BtLeAudioUnicastMonitorModeStatus to_rust_btle_audio_unicast_monitor_mode_status( le_audio::UnicastMonitorModeStatus status) { switch (status) { case le_audio::UnicastMonitorModeStatus::STREAMING_REQUESTED: return BtLeAudioUnicastMonitorModeStatus::StreamingRequested; case le_audio::UnicastMonitorModeStatus::STREAMING: return BtLeAudioUnicastMonitorModeStatus::Streaming; case le_audio::UnicastMonitorModeStatus::STREAMING_SUSPENDED: return BtLeAudioUnicastMonitorModeStatus::StreamingSuspended; default: log::assert_that(false, "Unhandled enum value from C++"); } return BtLeAudioUnicastMonitorModeStatus{}; } static BtLeAudioDirection to_rust_btle_audio_direction(uint8_t direction) { switch (direction) { case le_audio::types::kLeAudioDirectionSink: return BtLeAudioDirection::Sink; case le_audio::types::kLeAudioDirectionSource: return BtLeAudioDirection::Source; case le_audio::types::kLeAudioDirectionBoth: return BtLeAudioDirection::Both; default: log::assert_that(false, "Unhandled enum value from C++"); } return BtLeAudioDirection{}; } static BtLeAudioGroupStreamStatus to_rust_btle_audio_group_stream_status( le_audio::GroupStreamStatus status) { switch (status) { case le_audio::GroupStreamStatus::IDLE: return BtLeAudioGroupStreamStatus::Idle; case le_audio::GroupStreamStatus::STREAMING: return BtLeAudioGroupStreamStatus::Streaming; case le_audio::GroupStreamStatus::RELEASING: return BtLeAudioGroupStreamStatus::Releasing; case le_audio::GroupStreamStatus::SUSPENDING: return BtLeAudioGroupStreamStatus::Suspending; case le_audio::GroupStreamStatus::SUSPENDED: return BtLeAudioGroupStreamStatus::Suspended; case le_audio::GroupStreamStatus::CONFIGURED_AUTONOMOUS: return BtLeAudioGroupStreamStatus::ConfiguredAutonomous; case le_audio::GroupStreamStatus::CONFIGURED_BY_USER: return BtLeAudioGroupStreamStatus::ConfiguredByUser; case le_audio::GroupStreamStatus::DESTROYED: return BtLeAudioGroupStreamStatus::Destroyed; default: log::assert_that(false, "Unhandled enum value from C++"); } return BtLeAudioGroupStreamStatus{}; } static void initialized_cb() { le_audio_initialized_callback(); } static void connection_state_cb(le_audio::ConnectionState state, const RawAddress& address) { le_audio_connection_state_callback(to_rust_btle_audio_connection_state(state), address); } static void group_status_cb(int group_id, le_audio::GroupStatus group_status) { le_audio_group_status_callback(group_id, to_rust_btle_audio_group_status(group_status)); } static void group_node_status_cb( const RawAddress& bd_addr, int group_id, le_audio::GroupNodeStatus node_status) { le_audio_group_node_status_callback( bd_addr, group_id, to_rust_btle_audio_group_node_status(node_status)); } static void unicast_monitor_mode_status_cb( uint8_t direction, le_audio::UnicastMonitorModeStatus status) { le_audio_unicast_monitor_mode_status_callback( to_rust_btle_audio_direction(direction), to_rust_btle_audio_unicast_monitor_mode_status(status)); } static void group_stream_status_cb(int group_id, le_audio::GroupStreamStatus status) { le_audio_group_stream_status_callback(group_id, to_rust_btle_audio_group_stream_status(status)); } static void audio_conf_cb( uint8_t direction, int group_id, uint32_t snk_audio_location, uint32_t src_audio_location, uint16_t avail_cont) { le_audio_audio_conf_callback( direction, group_id, snk_audio_location, src_audio_location, avail_cont); } static void sink_audio_location_available_cb( const RawAddress& address, uint32_t snk_audio_locations) { le_audio_sink_audio_location_available_callback(address, snk_audio_locations); } static void audio_local_codec_capabilities_cb( std::vector<le_audio::btle_audio_codec_config_t> local_input_capa_codec_conf, std::vector<le_audio::btle_audio_codec_config_t> local_output_capa_codec_conf) { le_audio_audio_local_codec_capabilities_callback( to_rust_btle_audio_codec_config_vec(local_input_capa_codec_conf), to_rust_btle_audio_codec_config_vec(local_output_capa_codec_conf)); } static void audio_group_codec_conf_cb( int group_id, le_audio::btle_audio_codec_config_t input_codec_conf, le_audio::btle_audio_codec_config_t output_codec_conf, std::vector<le_audio::btle_audio_codec_config_t> input_selectable_codec_conf, std::vector<le_audio::btle_audio_codec_config_t> output_selectable_codec_conf) { le_audio_audio_group_codec_conf_callback( group_id, to_rust_btle_audio_codec_config(input_codec_conf), to_rust_btle_audio_codec_config(output_codec_conf), to_rust_btle_audio_codec_config_vec(input_selectable_codec_conf), to_rust_btle_audio_codec_config_vec(output_selectable_codec_conf)); } } // namespace internal class DBusLeAudioClientCallbacks : public le_audio::LeAudioClientCallbacks { public: static le_audio::LeAudioClientCallbacks* GetInstance() { static auto instance = new DBusLeAudioClientCallbacks(); return instance; } DBusLeAudioClientCallbacks(){}; void OnInitialized() override { log::info(""); topshim::rust::internal::initialized_cb(); } void OnConnectionState(le_audio::ConnectionState state, const RawAddress& address) override { log::info("state={}, address={}", static_cast<int>(state), ADDRESS_TO_LOGGABLE_CSTR(address)); topshim::rust::internal::connection_state_cb(state, address); } void OnGroupStatus(int group_id, le_audio::GroupStatus group_status) override { log::info("group_id={}, group_status={}", group_id, static_cast<int>(group_status)); topshim::rust::internal::group_status_cb(group_id, group_status); } void OnGroupNodeStatus( const RawAddress& bd_addr, int group_id, le_audio::GroupNodeStatus node_status) { log::info( "bd_addr={}, group_id={}, node_status={}", ADDRESS_TO_LOGGABLE_CSTR(bd_addr), group_id, static_cast<int>(node_status)); topshim::rust::internal::group_node_status_cb(bd_addr, group_id, node_status); } void OnAudioConf( uint8_t direction, int group_id, uint32_t snk_audio_location, uint32_t src_audio_location, uint16_t avail_cont) { log::info( "direction={}, group_id={}, snk_audio_location={}, src_audio_location={}, avail_cont={}", direction, group_id, snk_audio_location, src_audio_location, avail_cont); topshim::rust::internal::audio_conf_cb( direction, group_id, snk_audio_location, src_audio_location, avail_cont); } void OnSinkAudioLocationAvailable(const RawAddress& address, uint32_t snk_audio_locations) { log::info( "address={}, snk_audio_locations={}", ADDRESS_TO_LOGGABLE_CSTR(address), snk_audio_locations); topshim::rust::internal::sink_audio_location_available_cb(address, snk_audio_locations); } void OnAudioLocalCodecCapabilities( std::vector<le_audio::btle_audio_codec_config_t> local_input_capa_codec_conf, std::vector<le_audio::btle_audio_codec_config_t> local_output_capa_codec_conf) { log::info(""); topshim::rust::internal::audio_local_codec_capabilities_cb( local_input_capa_codec_conf, local_output_capa_codec_conf); } void OnAudioGroupCodecConf( int group_id, le_audio::btle_audio_codec_config_t input_codec_conf, le_audio::btle_audio_codec_config_t output_codec_conf, std::vector<le_audio::btle_audio_codec_config_t> input_selectable_codec_conf, std::vector<le_audio::btle_audio_codec_config_t> output_selectable_codec_conf) { log::info("group_id={}", group_id); topshim::rust::internal::audio_group_codec_conf_cb( group_id, input_codec_conf, output_codec_conf, input_selectable_codec_conf, output_selectable_codec_conf); } void OnAudioGroupCurrentCodecConf( int group_id, le_audio::btle_audio_codec_config_t input_codec_conf, le_audio::btle_audio_codec_config_t output_codec_conf) { log::info( "group_id={}, input_codec_conf={}, output_codec_conf={}", group_id, input_codec_conf.ToString(), output_codec_conf.ToString()); } void OnAudioGroupSelectableCodecConf( int group_id, std::vector<le_audio::btle_audio_codec_config_t> input_selectable_codec_conf, std::vector<le_audio::btle_audio_codec_config_t> output_selectable_codec_conf) { log::info( "group_id={}, input_selectable_codec_conf.size={}, output_selectable_codec_conf.size={}", group_id, input_selectable_codec_conf.size(), output_selectable_codec_conf.size()); } void OnHealthBasedRecommendationAction( const RawAddress& address, le_audio::LeAudioHealthBasedAction action) { log::info("address={}, action={}", ADDRESS_TO_LOGGABLE_CSTR(address), static_cast<int>(action)); } void OnHealthBasedGroupRecommendationAction( int group_id, le_audio::LeAudioHealthBasedAction action) { log::info("group_id={}, action={}", group_id, static_cast<int>(action)); } void OnUnicastMonitorModeStatus(uint8_t direction, le_audio::UnicastMonitorModeStatus status) { log::info("direction={}, status={}", direction, static_cast<int>(status)); topshim::rust::internal::unicast_monitor_mode_status_cb(direction, status); } void OnGroupStreamStatus(int group_id, le_audio::GroupStreamStatus status) { log::info("group_id={}, status={}", group_id, static_cast<int>(status)); topshim::rust::internal::group_stream_status_cb(group_id, status); } }; void LeAudioClientIntf::init(/* LeAudioClientCallbacks* callbacks, const std::vector<le_audio::btle_audio_codec_config_t>& offloading_preference */) { return intf_->Initialize(DBusLeAudioClientCallbacks::GetInstance(), {}); } void LeAudioClientIntf::connect(RawAddress addr) { return intf_->Connect(addr); } void LeAudioClientIntf::disconnect(RawAddress addr) { return intf_->Disconnect(addr); } void LeAudioClientIntf::set_enable_state(RawAddress addr, bool enabled) { return intf_->SetEnableState(addr, enabled); } void LeAudioClientIntf::cleanup() { return intf_->Cleanup(); } void LeAudioClientIntf::remove_device(RawAddress addr) { return intf_->RemoveDevice(addr); } void LeAudioClientIntf::group_add_node(int group_id, RawAddress addr) { return intf_->GroupAddNode(group_id, addr); } void LeAudioClientIntf::group_remove_node(int group_id, RawAddress addr) { return intf_->GroupRemoveNode(group_id, addr); } void LeAudioClientIntf::group_set_active(int group_id) { return intf_->GroupSetActive(group_id); } void LeAudioClientIntf::set_codec_config_preference( int group_id, BtLeAudioCodecConfig input_codec_config, BtLeAudioCodecConfig output_codec_config) { return intf_->SetCodecConfigPreference( group_id, internal::from_rust_btle_audio_codec_config(input_codec_config), internal::from_rust_btle_audio_codec_config(output_codec_config)); } void LeAudioClientIntf::set_ccid_information(int ccid, int context_type) { return intf_->SetCcidInformation(ccid, context_type); } void LeAudioClientIntf::set_in_call(bool in_call) { return intf_->SetInCall(in_call); } void LeAudioClientIntf::send_audio_profile_preferences( int group_id, bool is_output_preference_le_audio, bool is_duplex_preference_le_audio) { return intf_->SendAudioProfilePreferences( group_id, is_output_preference_le_audio, is_duplex_preference_le_audio); } void LeAudioClientIntf::set_unicast_monitor_mode(BtLeAudioDirection direction, bool enable) { return intf_->SetUnicastMonitorMode(internal::from_rust_btle_audio_direction(direction), enable); } std::unique_ptr<LeAudioClientIntf> GetLeAudioClientProfile(const unsigned char* btif) { if (internal::g_lea_client_if) std::abort(); const bt_interface_t* btif_ = reinterpret_cast<const bt_interface_t*>(btif); auto lea_client_if = std::make_unique<LeAudioClientIntf>(const_cast<le_audio::LeAudioClientInterface*>( reinterpret_cast<const le_audio::LeAudioClientInterface*>( btif_->get_profile_interface("le_audio")))); internal::g_lea_client_if = lea_client_if.get(); return lea_client_if; } bool LeAudioClientIntf::host_start_audio_request() { return ::bluetooth::audio::le_audio::HostStartRequest(); } void LeAudioClientIntf::host_stop_audio_request() { ::bluetooth::audio::le_audio::HostStopRequest(); } bool LeAudioClientIntf::peer_start_audio_request() { return ::bluetooth::audio::le_audio::PeerStartRequest(); } void LeAudioClientIntf::peer_stop_audio_request() { ::bluetooth::audio::le_audio::PeerStopRequest(); } static BtLePcmConfig to_rust_btle_pcm_params(audio::le_audio::btle_pcm_parameters pcm_params) { return BtLePcmConfig{ .data_interval_us = pcm_params.data_interval_us, .sample_rate = pcm_params.sample_rate, .bits_per_sample = pcm_params.bits_per_sample, .channels_count = pcm_params.channels_count, }; } BtLePcmConfig LeAudioClientIntf::get_host_pcm_config() { return to_rust_btle_pcm_params(::bluetooth::audio::le_audio::GetHostPcmConfig()); } BtLePcmConfig LeAudioClientIntf::get_peer_pcm_config() { return to_rust_btle_pcm_params(::bluetooth::audio::le_audio::GetPeerPcmConfig()); } bool LeAudioClientIntf::get_host_stream_started() { return ::bluetooth::audio::le_audio::GetHostStreamStarted(); } bool LeAudioClientIntf::get_peer_stream_started() { return ::bluetooth::audio::le_audio::GetPeerStreamStarted(); } void LeAudioClientIntf::source_metadata_changed(::rust::Vec<SourceMetadata> metadata) { if (metadata.empty()) { log::warn("Received empty metadata."); return; } // This will be referenced across threads. static std::vector<playback_track_metadata_v7> tracks; tracks.clear(); tracks.reserve(metadata.size()); for (auto m : metadata) { struct playback_track_metadata_v7 track = { .base = { .usage = static_cast<audio_usage_t>(m.usage), .content_type = static_cast<audio_content_type_t>(m.content_type), .gain = static_cast<float>(m.gain), }, .channel_mask = AUDIO_CHANNEL_NONE, // unused .tags = "", }; tracks.push_back(track); } source_metadata_v7_t data = { .track_count = tracks.size(), .tracks = tracks.data(), }; ::bluetooth::audio::le_audio::SourceMetadataChanged(data); } void LeAudioClientIntf::sink_metadata_changed(::rust::Vec<SinkMetadata> metadata) { if (metadata.empty()) { log::warn("Received empty metadata."); return; } // This will be referenced across threads. static std::vector<record_track_metadata_v7> tracks; tracks.clear(); tracks.reserve(metadata.size()); for (auto m : metadata) { struct record_track_metadata_v7 track = { .base = { .source = static_cast<audio_source_t>(m.source), .gain = static_cast<float>(m.gain), .dest_device = AUDIO_DEVICE_IN_DEFAULT, .dest_device_address = "", // unused }, .channel_mask = AUDIO_CHANNEL_NONE, // unused .tags = "", }; tracks.push_back(track); } const sink_metadata_v7_t data = { .track_count = tracks.size(), .tracks = tracks.data(), }; ::bluetooth::audio::le_audio::SinkMetadataChanged(data); } } // namespace rust } // namespace topshim } // namespace bluetooth
system/gd/rust/topshim/le_audio/le_audio_shim.h 0 → 100644 +83 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include <memory> #include "audio_hal_interface/le_audio_software_host.h" #include "include/hardware/bt_le_audio.h" #include "rust/cxx.h" #include "types/raw_address.h" namespace bluetooth { namespace topshim { namespace rust { struct BtLeAudioCodecConfig; enum class BtLeAudioDirection : uint8_t; struct BtLePcmConfig; struct SourceMetadata; struct SinkMetadata; class LeAudioClientIntf { public: LeAudioClientIntf(le_audio::LeAudioClientInterface* intf) : intf_(intf){}; void init( /* LeAudioClientCallbacks* callbacks, const std::vector<le_audio::btle_audio_codec_config_t>& offloading_preference */); void connect(RawAddress addr); void disconnect(RawAddress addr); void set_enable_state(RawAddress addr, bool enabled); void cleanup(); void remove_device(RawAddress addr); void group_add_node(int group_id, RawAddress addr); void group_remove_node(int group_id, RawAddress addr); void group_set_active(int group_id); void set_codec_config_preference( int group_id, BtLeAudioCodecConfig input_codec_config, BtLeAudioCodecConfig output_codec_config); void set_ccid_information(int ccid, int context_type); void set_in_call(bool in_call); void send_audio_profile_preferences( int group_id, bool is_output_preference_le_audio, bool is_duplex_preference_le_audio); void set_unicast_monitor_mode(BtLeAudioDirection direction, bool enable); // interface for audio server bool host_start_audio_request(); void host_stop_audio_request(); bool peer_start_audio_request(); void peer_stop_audio_request(); BtLePcmConfig get_host_pcm_config(); BtLePcmConfig get_peer_pcm_config(); bool get_host_stream_started(); bool get_peer_stream_started(); void source_metadata_changed(::rust::Vec<SourceMetadata> metadata); void sink_metadata_changed(::rust::Vec<SinkMetadata> metadata); private: le_audio::LeAudioClientInterface* intf_; }; std::unique_ptr<LeAudioClientIntf> GetLeAudioClientProfile(const unsigned char* btif); } // namespace rust } // namespace topshim } // namespace bluetooth