Loading system/gd/rust/topshim/Android.bp +3 −0 Original line number Diff line number Diff line Loading @@ -53,6 +53,7 @@ cc_library_static { "gatt/gatt_shim.cc", "hfp/hfp_shim.cc", "le_audio/le_audio_shim.cc", "vc/vc_shim.cc", ], generated_headers: [ "BluetoothGeneratedDumpsysDataSchema_h", Loading Loading @@ -89,6 +90,7 @@ gensrcs { "src/profiles/gatt.rs", "src/profiles/hfp.rs", "src/profiles/le_audio.rs", "src/profiles/vc.rs", ], output_extension: "rs.h", export_include_dirs: ["."], Loading @@ -106,6 +108,7 @@ gensrcs { "src/profiles/gatt.rs", "src/profiles/hfp.rs", "src/profiles/le_audio.rs", "src/profiles/vc.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 @@ -30,6 +30,7 @@ cxxbridge_header("btif_bridge_header") { "src/profiles/gatt.rs", "src/profiles/hfp.rs", "src/profiles/le_audio.rs", "src/profiles/vc.rs", ] all_dependent_configs = [ ":rust_topshim_config" ] deps = [ ":cxxlibheader" ] Loading @@ -45,6 +46,7 @@ cxxbridge_cc("btif_bridge_code") { "src/profiles/gatt.rs", "src/profiles/hfp.rs", "src/profiles/le_audio.rs", "src/profiles/vc.rs", ] deps = [ ":btif_bridge_header", Loading @@ -67,6 +69,7 @@ source_set("btif_cxx_bridge_code") { "gatt/gatt_shim.cc", "hfp/hfp_shim.cc", "le_audio/le_audio_shim.cc", "vc/vc_shim.cc", "metrics/metrics_shim.cc", ] Loading system/gd/rust/topshim/src/profiles/mod.rs +1 −0 Original line number Diff line number Diff line Loading @@ -35,3 +35,4 @@ pub mod hid_host; pub mod le_audio; pub mod sdp; pub mod socket; pub mod vc; system/gd/rust/topshim/src/profiles/vc.rs 0 → 100644 +311 −0 Original line number Diff line number Diff line use crate::btif::{BluetoothInterface, RawAddress, ToggleableProfile}; use crate::topstack::get_dispatchers; use std::sync::{Arc, Mutex}; use topshim_macros::{cb_variant, profile_enabled_or}; use log::warn; #[cxx::bridge(namespace = bluetooth::topshim::rust)] pub mod ffi { unsafe extern "C++" { include!("gd/rust/topshim/common/type_alias.h"); type RawAddress = crate::btif::RawAddress; } #[derive(Debug, Copy, Clone)] pub enum BtVcConnectionState { Disconnected = 0, Connecting, Connected, Disconnecting, } unsafe extern "C++" { include!("vc/vc_shim.h"); type VolumeControlIntf; unsafe fn GetVolumeControlProfile(btif: *const u8) -> UniquePtr<VolumeControlIntf>; fn init(self: Pin<&mut VolumeControlIntf>); fn cleanup(self: Pin<&mut VolumeControlIntf>); fn connect(self: Pin<&mut VolumeControlIntf>, addr: RawAddress); fn disconnect(self: Pin<&mut VolumeControlIntf>, addr: RawAddress); fn remove_device(self: Pin<&mut VolumeControlIntf>, addr: RawAddress); fn set_volume(self: Pin<&mut VolumeControlIntf>, group_id: i32, volume: u8); fn mute(self: Pin<&mut VolumeControlIntf>, addr: RawAddress); fn unmute(self: Pin<&mut VolumeControlIntf>, addr: RawAddress); fn get_ext_audio_out_volume_offset( self: Pin<&mut VolumeControlIntf>, addr: RawAddress, ext_output_id: u8, ); fn set_ext_audio_out_volume_offset( self: Pin<&mut VolumeControlIntf>, addr: RawAddress, ext_output_id: u8, offset_val: i16, ); fn get_ext_audio_out_location( self: Pin<&mut VolumeControlIntf>, addr: RawAddress, ext_output_id: u8, ); fn set_ext_audio_out_location( self: Pin<&mut VolumeControlIntf>, addr: RawAddress, ext_output_id: u8, location: u32, ); fn get_ext_audio_out_description( self: Pin<&mut VolumeControlIntf>, addr: RawAddress, ext_output_id: u8, ); unsafe fn set_ext_audio_out_description( self: Pin<&mut VolumeControlIntf>, addr: RawAddress, ext_output_id: u8, descr: *const c_char, ); } extern "Rust" { fn vc_connection_state_callback(state: BtVcConnectionState, addr: RawAddress); fn vc_volume_state_callback( address: RawAddress, volume: u8, mute: bool, is_autonomous: bool, ); fn vc_group_volume_state_callback( group_id: i32, volume: u8, mute: bool, is_autonomous: bool, ); fn vc_device_available_callback(address: RawAddress, num_offset: u8); fn vc_ext_audio_out_volume_offset_callback( address: RawAddress, ext_output_id: u8, offset: i16, ); fn vc_ext_audio_out_location_callback( address: RawAddress, ext_output_id: u8, location: u32, ); fn vc_ext_audio_out_description_callback( address: RawAddress, ext_output_id: u8, descr: String, ); } } pub type BtVcConnectionState = ffi::BtVcConnectionState; #[derive(Debug)] pub enum VolumeControlCallbacks { ConnectionState(BtVcConnectionState, RawAddress), VolumeState(RawAddress, u8, bool, bool), GroupVolumeState(i32, u8, bool, bool), DeviceAvailable(RawAddress, u8), ExtAudioOutVolume(RawAddress, u8, i16), ExtAudioOutLocation(RawAddress, u8, u32), ExtAudioOutDescription(RawAddress, u8, String), } pub struct VolumeControlCallbacksDispatcher { pub dispatch: Box<dyn Fn(VolumeControlCallbacks) + Send>, } type VolumeControlCb = Arc<Mutex<VolumeControlCallbacksDispatcher>>; cb_variant!(VolumeControlCb, vc_connection_state_callback -> VolumeControlCallbacks::ConnectionState, BtVcConnectionState, RawAddress); cb_variant!(VolumeControlCb, vc_volume_state_callback -> VolumeControlCallbacks::VolumeState, RawAddress, u8, bool, bool); cb_variant!(VolumeControlCb, vc_group_volume_state_callback -> VolumeControlCallbacks::GroupVolumeState, i32, u8, bool, bool); cb_variant!(VolumeControlCb, vc_device_available_callback -> VolumeControlCallbacks::DeviceAvailable, RawAddress, u8); cb_variant!(VolumeControlCb, vc_ext_audio_out_volume_offset_callback -> VolumeControlCallbacks::ExtAudioOutVolume, RawAddress, u8, i16); cb_variant!(VolumeControlCb, vc_ext_audio_out_location_callback -> VolumeControlCallbacks::ExtAudioOutLocation, RawAddress, u8, u32); cb_variant!(VolumeControlCb, vc_ext_audio_out_description_callback -> VolumeControlCallbacks::ExtAudioOutDescription, RawAddress, u8, String); pub struct VolumeControl { internal: cxx::UniquePtr<ffi::VolumeControlIntf>, is_init: bool, is_enabled: bool, } // For *const u8 opaque btif // SAFETY: `VolumeControlIntf` is thread-safe to make calls from. unsafe impl Send for VolumeControl {} impl ToggleableProfile for VolumeControl { fn is_enabled(&self) -> bool { self.is_enabled } fn enable(&mut self) -> bool { if self.is_enabled { warn!("VolumeControl is already enabled."); return false; } self.internal.pin_mut().init(); self.is_enabled = true; true } #[profile_enabled_or(false)] fn disable(&mut self) -> bool { if !self.is_enabled { warn!("VolumeControl is already disabled."); return false; } self.internal.pin_mut().cleanup(); self.is_enabled = false; true } } impl VolumeControl { pub fn new(intf: &BluetoothInterface) -> VolumeControl { let vc_if: cxx::UniquePtr<ffi::VolumeControlIntf>; // SAFETY: `intf.as_raw_ptr()` is a valid pointer to a `BluetoothInterface` vc_if = unsafe { ffi::GetVolumeControlProfile(intf.as_raw_ptr()) }; VolumeControl { internal: vc_if, is_init: false, is_enabled: false } } pub fn is_initialized(&self) -> bool { self.is_init } // `internal.init` is invoked during `ToggleableProfile::enable` pub fn initialize(&mut self, callbacks: VolumeControlCallbacksDispatcher) -> bool { if self.is_init { warn!("VolumeControl has already been initialized"); return false; } if get_dispatchers().lock().unwrap().set::<VolumeControlCb>(Arc::new(Mutex::new(callbacks))) { panic!("Tried to set dispatcher for VolumeControl callbacks while it already exists"); } self.is_init = true; true } #[profile_enabled_or] pub fn cleanup(&mut self) { self.internal.pin_mut().cleanup(); } #[profile_enabled_or] pub fn connect(&mut self, addr: RawAddress) { self.internal.pin_mut().connect(addr); } #[profile_enabled_or] pub fn disconnect(&mut self, addr: RawAddress) { self.internal.pin_mut().disconnect(addr); } #[profile_enabled_or] pub fn remove_device(&mut self, addr: RawAddress) { self.internal.pin_mut().remove_device(addr); } #[profile_enabled_or] pub fn set_volume(&mut self, group_id: i32, volume: u8) { self.internal.pin_mut().set_volume(group_id, volume); } #[profile_enabled_or] pub fn mute(&mut self, addr: RawAddress) { self.internal.pin_mut().mute(addr); } #[profile_enabled_or] pub fn unmute(&mut self, addr: RawAddress) { self.internal.pin_mut().unmute(addr); } #[profile_enabled_or] pub fn get_ext_audio_out_volume_offset(&mut self, addr: RawAddress, ext_output_id: u8) { self.internal.pin_mut().get_ext_audio_out_volume_offset(addr, ext_output_id); } #[profile_enabled_or] pub fn set_ext_audio_out_volume_offset( &mut self, addr: RawAddress, ext_output_id: u8, offset_val: i16, ) { self.internal.pin_mut().set_ext_audio_out_volume_offset(addr, ext_output_id, offset_val); } #[profile_enabled_or] pub fn get_ext_audio_out_location(&mut self, addr: RawAddress, ext_output_id: u8) { self.internal.pin_mut().get_ext_audio_out_location(addr, ext_output_id); } #[profile_enabled_or] pub fn set_ext_audio_out_location( &mut self, addr: RawAddress, ext_output_id: u8, location: u32, ) { self.internal.pin_mut().set_ext_audio_out_location(addr, ext_output_id, location); } #[profile_enabled_or] pub fn get_ext_audio_out_description(&mut self, addr: RawAddress, ext_output_id: u8) { self.internal.pin_mut().get_ext_audio_out_description(addr, ext_output_id); } #[profile_enabled_or] pub fn set_ext_audio_out_description( &mut self, addr: RawAddress, ext_output_id: u8, descr: String, ) { let c_descr = std::ffi::CString::new(descr).unwrap(); unsafe { // SAFETY: calling an FFI where the pointer is const, no modification. self.internal.pin_mut().set_ext_audio_out_description( addr, ext_output_id, c_descr.as_ptr(), ); } } } system/gd/rust/topshim/vc/vc_shim.cc 0 → 100644 +229 −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/vc/vc_shim.h" #include <bluetooth/log.h> #include <hardware/bluetooth.h> #include <string> #include "os/log.h" #include "src/profiles/vc.rs.h" #include "types/raw_address.h" namespace rusty = ::bluetooth::topshim::rust; namespace bluetooth { namespace topshim { namespace rust { namespace internal { static VolumeControlIntf* g_vc_if; static BtVcConnectionState to_rust_btvc_connection_state(vc::ConnectionState state) { switch (state) { case vc::ConnectionState::DISCONNECTED: return BtVcConnectionState::Disconnected; case vc::ConnectionState::CONNECTING: return BtVcConnectionState::Connecting; case vc::ConnectionState::CONNECTED: return BtVcConnectionState::Connected; case vc::ConnectionState::DISCONNECTING: return BtVcConnectionState::Disconnecting; default: log::assert_that(false, "Unhandled enum value from C++"); } return BtVcConnectionState{}; } static void connection_state_cb(vc::ConnectionState state, const RawAddress& address) { vc_connection_state_callback(to_rust_btvc_connection_state(state), address); } static void volume_state_cb( const RawAddress& address, uint8_t volume, bool mute, bool is_autonomous) { vc_volume_state_callback(address, volume, mute, is_autonomous); } static void group_volume_state_cb(int group_id, uint8_t volume, bool mute, bool is_autonomous) { vc_group_volume_state_callback(group_id, volume, mute, is_autonomous); } static void device_available_cb(const RawAddress& address, uint8_t num_offset) { vc_device_available_callback(address, num_offset); } static void ext_audio_out_volume_offset_cb( const RawAddress& address, uint8_t ext_output_id, int16_t offset) { vc_ext_audio_out_volume_offset_callback(address, ext_output_id, offset); } static void ext_audio_out_location_cb( const RawAddress& address, uint8_t ext_output_id, uint32_t location) { vc_ext_audio_out_location_callback(address, ext_output_id, location); } static void ext_audio_out_description_cb( const RawAddress& address, uint8_t ext_output_id, std::string descr) { vc_ext_audio_out_description_callback(address, ext_output_id, descr); } } // namespace internal class DBusVolumeControlCallbacks : public vc::VolumeControlCallbacks { public: static vc::VolumeControlCallbacks* GetInstance() { static auto instance = new DBusVolumeControlCallbacks(); return instance; } DBusVolumeControlCallbacks(){}; void OnConnectionState(vc::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 OnVolumeStateChanged( const RawAddress& address, uint8_t volume, bool mute, bool is_autonomous) override { log::info( "address={}, volume={}, mute={}, is_autonomous={}", ADDRESS_TO_LOGGABLE_CSTR(address), volume, mute, is_autonomous); topshim::rust::internal::volume_state_cb(address, volume, mute, is_autonomous); } void OnGroupVolumeStateChanged( int group_id, uint8_t volume, bool mute, bool is_autonomous) override { log::info( "group_id={}, volume={}, mute={}, is_autonomous={}", group_id, volume, mute, is_autonomous); topshim::rust::internal::group_volume_state_cb(group_id, volume, mute, is_autonomous); } void OnDeviceAvailable(const RawAddress& address, uint8_t num_offset) override { log::info("address={}, num_offset={}", ADDRESS_TO_LOGGABLE_CSTR(address), num_offset); topshim::rust::internal::device_available_cb(address, num_offset); } void OnExtAudioOutVolumeOffsetChanged( const RawAddress& address, uint8_t ext_output_id, int16_t offset) override { log::info( "address={}, ext_output_id={}, offset={}", ADDRESS_TO_LOGGABLE_CSTR(address), ext_output_id, offset); topshim::rust::internal::ext_audio_out_volume_offset_cb(address, ext_output_id, offset); } void OnExtAudioOutLocationChanged( const RawAddress& address, uint8_t ext_output_id, uint32_t location) override { log::info( "address={}, ext_output_id, location={}", ADDRESS_TO_LOGGABLE_CSTR(address), ext_output_id, location); topshim::rust::internal::ext_audio_out_location_cb(address, ext_output_id, location); } void OnExtAudioOutDescriptionChanged( const RawAddress& address, uint8_t ext_output_id, std::string descr) override { log::info( "address={}, ext_output_id={}, descr={}", ADDRESS_TO_LOGGABLE_CSTR(address), ext_output_id, descr.c_str()); topshim::rust::internal::ext_audio_out_description_cb(address, ext_output_id, descr); } }; std::unique_ptr<VolumeControlIntf> GetVolumeControlProfile(const unsigned char* btif) { if (internal::g_vc_if) std::abort(); const bt_interface_t* btif_ = reinterpret_cast<const bt_interface_t*>(btif); auto vc_if = std::make_unique<VolumeControlIntf>( const_cast<vc::VolumeControlInterface*>(reinterpret_cast<const vc::VolumeControlInterface*>( btif_->get_profile_interface("volume_control")))); internal::g_vc_if = vc_if.get(); return vc_if; } void VolumeControlIntf::init(/*VolumeControlCallbacks* callbacks*/) { return intf_->Init(DBusVolumeControlCallbacks::GetInstance()); } void VolumeControlIntf::cleanup() { return intf_->Cleanup(); } void VolumeControlIntf::connect(RawAddress addr) { return intf_->Connect(addr); } void VolumeControlIntf::disconnect(RawAddress addr) { return intf_->Disconnect(addr); } void VolumeControlIntf::remove_device(RawAddress addr) { return intf_->RemoveDevice(addr); } void VolumeControlIntf::set_volume(int group_id, uint8_t volume) { return intf_->SetVolume(group_id, volume); } void VolumeControlIntf::mute(RawAddress addr) { return intf_->Mute(addr); } void VolumeControlIntf::unmute(RawAddress addr) { return intf_->Unmute(addr); } void VolumeControlIntf::get_ext_audio_out_volume_offset(RawAddress addr, uint8_t ext_output_id) { return intf_->GetExtAudioOutVolumeOffset(addr, ext_output_id); } void VolumeControlIntf::set_ext_audio_out_volume_offset( RawAddress addr, uint8_t ext_output_id, int16_t offset_val) { return intf_->SetExtAudioOutVolumeOffset(addr, ext_output_id, offset_val); } void VolumeControlIntf::get_ext_audio_out_location(RawAddress addr, uint8_t ext_output_id) { return intf_->GetExtAudioOutLocation(addr, ext_output_id); } void VolumeControlIntf::set_ext_audio_out_location( RawAddress addr, uint8_t ext_output_id, uint32_t location) { return intf_->SetExtAudioOutLocation(addr, ext_output_id, location); } void VolumeControlIntf::get_ext_audio_out_description(RawAddress addr, uint8_t ext_output_id) { return intf_->GetExtAudioOutDescription(addr, ext_output_id); } void VolumeControlIntf::set_ext_audio_out_description( RawAddress addr, uint8_t ext_output_id, const char* descr) { return intf_->SetExtAudioOutDescription(addr, ext_output_id, std::string(descr)); } } // namespace rust } // namespace topshim } // namespace bluetooth Loading
system/gd/rust/topshim/Android.bp +3 −0 Original line number Diff line number Diff line Loading @@ -53,6 +53,7 @@ cc_library_static { "gatt/gatt_shim.cc", "hfp/hfp_shim.cc", "le_audio/le_audio_shim.cc", "vc/vc_shim.cc", ], generated_headers: [ "BluetoothGeneratedDumpsysDataSchema_h", Loading Loading @@ -89,6 +90,7 @@ gensrcs { "src/profiles/gatt.rs", "src/profiles/hfp.rs", "src/profiles/le_audio.rs", "src/profiles/vc.rs", ], output_extension: "rs.h", export_include_dirs: ["."], Loading @@ -106,6 +108,7 @@ gensrcs { "src/profiles/gatt.rs", "src/profiles/hfp.rs", "src/profiles/le_audio.rs", "src/profiles/vc.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 @@ -30,6 +30,7 @@ cxxbridge_header("btif_bridge_header") { "src/profiles/gatt.rs", "src/profiles/hfp.rs", "src/profiles/le_audio.rs", "src/profiles/vc.rs", ] all_dependent_configs = [ ":rust_topshim_config" ] deps = [ ":cxxlibheader" ] Loading @@ -45,6 +46,7 @@ cxxbridge_cc("btif_bridge_code") { "src/profiles/gatt.rs", "src/profiles/hfp.rs", "src/profiles/le_audio.rs", "src/profiles/vc.rs", ] deps = [ ":btif_bridge_header", Loading @@ -67,6 +69,7 @@ source_set("btif_cxx_bridge_code") { "gatt/gatt_shim.cc", "hfp/hfp_shim.cc", "le_audio/le_audio_shim.cc", "vc/vc_shim.cc", "metrics/metrics_shim.cc", ] Loading
system/gd/rust/topshim/src/profiles/mod.rs +1 −0 Original line number Diff line number Diff line Loading @@ -35,3 +35,4 @@ pub mod hid_host; pub mod le_audio; pub mod sdp; pub mod socket; pub mod vc;
system/gd/rust/topshim/src/profiles/vc.rs 0 → 100644 +311 −0 Original line number Diff line number Diff line use crate::btif::{BluetoothInterface, RawAddress, ToggleableProfile}; use crate::topstack::get_dispatchers; use std::sync::{Arc, Mutex}; use topshim_macros::{cb_variant, profile_enabled_or}; use log::warn; #[cxx::bridge(namespace = bluetooth::topshim::rust)] pub mod ffi { unsafe extern "C++" { include!("gd/rust/topshim/common/type_alias.h"); type RawAddress = crate::btif::RawAddress; } #[derive(Debug, Copy, Clone)] pub enum BtVcConnectionState { Disconnected = 0, Connecting, Connected, Disconnecting, } unsafe extern "C++" { include!("vc/vc_shim.h"); type VolumeControlIntf; unsafe fn GetVolumeControlProfile(btif: *const u8) -> UniquePtr<VolumeControlIntf>; fn init(self: Pin<&mut VolumeControlIntf>); fn cleanup(self: Pin<&mut VolumeControlIntf>); fn connect(self: Pin<&mut VolumeControlIntf>, addr: RawAddress); fn disconnect(self: Pin<&mut VolumeControlIntf>, addr: RawAddress); fn remove_device(self: Pin<&mut VolumeControlIntf>, addr: RawAddress); fn set_volume(self: Pin<&mut VolumeControlIntf>, group_id: i32, volume: u8); fn mute(self: Pin<&mut VolumeControlIntf>, addr: RawAddress); fn unmute(self: Pin<&mut VolumeControlIntf>, addr: RawAddress); fn get_ext_audio_out_volume_offset( self: Pin<&mut VolumeControlIntf>, addr: RawAddress, ext_output_id: u8, ); fn set_ext_audio_out_volume_offset( self: Pin<&mut VolumeControlIntf>, addr: RawAddress, ext_output_id: u8, offset_val: i16, ); fn get_ext_audio_out_location( self: Pin<&mut VolumeControlIntf>, addr: RawAddress, ext_output_id: u8, ); fn set_ext_audio_out_location( self: Pin<&mut VolumeControlIntf>, addr: RawAddress, ext_output_id: u8, location: u32, ); fn get_ext_audio_out_description( self: Pin<&mut VolumeControlIntf>, addr: RawAddress, ext_output_id: u8, ); unsafe fn set_ext_audio_out_description( self: Pin<&mut VolumeControlIntf>, addr: RawAddress, ext_output_id: u8, descr: *const c_char, ); } extern "Rust" { fn vc_connection_state_callback(state: BtVcConnectionState, addr: RawAddress); fn vc_volume_state_callback( address: RawAddress, volume: u8, mute: bool, is_autonomous: bool, ); fn vc_group_volume_state_callback( group_id: i32, volume: u8, mute: bool, is_autonomous: bool, ); fn vc_device_available_callback(address: RawAddress, num_offset: u8); fn vc_ext_audio_out_volume_offset_callback( address: RawAddress, ext_output_id: u8, offset: i16, ); fn vc_ext_audio_out_location_callback( address: RawAddress, ext_output_id: u8, location: u32, ); fn vc_ext_audio_out_description_callback( address: RawAddress, ext_output_id: u8, descr: String, ); } } pub type BtVcConnectionState = ffi::BtVcConnectionState; #[derive(Debug)] pub enum VolumeControlCallbacks { ConnectionState(BtVcConnectionState, RawAddress), VolumeState(RawAddress, u8, bool, bool), GroupVolumeState(i32, u8, bool, bool), DeviceAvailable(RawAddress, u8), ExtAudioOutVolume(RawAddress, u8, i16), ExtAudioOutLocation(RawAddress, u8, u32), ExtAudioOutDescription(RawAddress, u8, String), } pub struct VolumeControlCallbacksDispatcher { pub dispatch: Box<dyn Fn(VolumeControlCallbacks) + Send>, } type VolumeControlCb = Arc<Mutex<VolumeControlCallbacksDispatcher>>; cb_variant!(VolumeControlCb, vc_connection_state_callback -> VolumeControlCallbacks::ConnectionState, BtVcConnectionState, RawAddress); cb_variant!(VolumeControlCb, vc_volume_state_callback -> VolumeControlCallbacks::VolumeState, RawAddress, u8, bool, bool); cb_variant!(VolumeControlCb, vc_group_volume_state_callback -> VolumeControlCallbacks::GroupVolumeState, i32, u8, bool, bool); cb_variant!(VolumeControlCb, vc_device_available_callback -> VolumeControlCallbacks::DeviceAvailable, RawAddress, u8); cb_variant!(VolumeControlCb, vc_ext_audio_out_volume_offset_callback -> VolumeControlCallbacks::ExtAudioOutVolume, RawAddress, u8, i16); cb_variant!(VolumeControlCb, vc_ext_audio_out_location_callback -> VolumeControlCallbacks::ExtAudioOutLocation, RawAddress, u8, u32); cb_variant!(VolumeControlCb, vc_ext_audio_out_description_callback -> VolumeControlCallbacks::ExtAudioOutDescription, RawAddress, u8, String); pub struct VolumeControl { internal: cxx::UniquePtr<ffi::VolumeControlIntf>, is_init: bool, is_enabled: bool, } // For *const u8 opaque btif // SAFETY: `VolumeControlIntf` is thread-safe to make calls from. unsafe impl Send for VolumeControl {} impl ToggleableProfile for VolumeControl { fn is_enabled(&self) -> bool { self.is_enabled } fn enable(&mut self) -> bool { if self.is_enabled { warn!("VolumeControl is already enabled."); return false; } self.internal.pin_mut().init(); self.is_enabled = true; true } #[profile_enabled_or(false)] fn disable(&mut self) -> bool { if !self.is_enabled { warn!("VolumeControl is already disabled."); return false; } self.internal.pin_mut().cleanup(); self.is_enabled = false; true } } impl VolumeControl { pub fn new(intf: &BluetoothInterface) -> VolumeControl { let vc_if: cxx::UniquePtr<ffi::VolumeControlIntf>; // SAFETY: `intf.as_raw_ptr()` is a valid pointer to a `BluetoothInterface` vc_if = unsafe { ffi::GetVolumeControlProfile(intf.as_raw_ptr()) }; VolumeControl { internal: vc_if, is_init: false, is_enabled: false } } pub fn is_initialized(&self) -> bool { self.is_init } // `internal.init` is invoked during `ToggleableProfile::enable` pub fn initialize(&mut self, callbacks: VolumeControlCallbacksDispatcher) -> bool { if self.is_init { warn!("VolumeControl has already been initialized"); return false; } if get_dispatchers().lock().unwrap().set::<VolumeControlCb>(Arc::new(Mutex::new(callbacks))) { panic!("Tried to set dispatcher for VolumeControl callbacks while it already exists"); } self.is_init = true; true } #[profile_enabled_or] pub fn cleanup(&mut self) { self.internal.pin_mut().cleanup(); } #[profile_enabled_or] pub fn connect(&mut self, addr: RawAddress) { self.internal.pin_mut().connect(addr); } #[profile_enabled_or] pub fn disconnect(&mut self, addr: RawAddress) { self.internal.pin_mut().disconnect(addr); } #[profile_enabled_or] pub fn remove_device(&mut self, addr: RawAddress) { self.internal.pin_mut().remove_device(addr); } #[profile_enabled_or] pub fn set_volume(&mut self, group_id: i32, volume: u8) { self.internal.pin_mut().set_volume(group_id, volume); } #[profile_enabled_or] pub fn mute(&mut self, addr: RawAddress) { self.internal.pin_mut().mute(addr); } #[profile_enabled_or] pub fn unmute(&mut self, addr: RawAddress) { self.internal.pin_mut().unmute(addr); } #[profile_enabled_or] pub fn get_ext_audio_out_volume_offset(&mut self, addr: RawAddress, ext_output_id: u8) { self.internal.pin_mut().get_ext_audio_out_volume_offset(addr, ext_output_id); } #[profile_enabled_or] pub fn set_ext_audio_out_volume_offset( &mut self, addr: RawAddress, ext_output_id: u8, offset_val: i16, ) { self.internal.pin_mut().set_ext_audio_out_volume_offset(addr, ext_output_id, offset_val); } #[profile_enabled_or] pub fn get_ext_audio_out_location(&mut self, addr: RawAddress, ext_output_id: u8) { self.internal.pin_mut().get_ext_audio_out_location(addr, ext_output_id); } #[profile_enabled_or] pub fn set_ext_audio_out_location( &mut self, addr: RawAddress, ext_output_id: u8, location: u32, ) { self.internal.pin_mut().set_ext_audio_out_location(addr, ext_output_id, location); } #[profile_enabled_or] pub fn get_ext_audio_out_description(&mut self, addr: RawAddress, ext_output_id: u8) { self.internal.pin_mut().get_ext_audio_out_description(addr, ext_output_id); } #[profile_enabled_or] pub fn set_ext_audio_out_description( &mut self, addr: RawAddress, ext_output_id: u8, descr: String, ) { let c_descr = std::ffi::CString::new(descr).unwrap(); unsafe { // SAFETY: calling an FFI where the pointer is const, no modification. self.internal.pin_mut().set_ext_audio_out_description( addr, ext_output_id, c_descr.as_ptr(), ); } } }
system/gd/rust/topshim/vc/vc_shim.cc 0 → 100644 +229 −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/vc/vc_shim.h" #include <bluetooth/log.h> #include <hardware/bluetooth.h> #include <string> #include "os/log.h" #include "src/profiles/vc.rs.h" #include "types/raw_address.h" namespace rusty = ::bluetooth::topshim::rust; namespace bluetooth { namespace topshim { namespace rust { namespace internal { static VolumeControlIntf* g_vc_if; static BtVcConnectionState to_rust_btvc_connection_state(vc::ConnectionState state) { switch (state) { case vc::ConnectionState::DISCONNECTED: return BtVcConnectionState::Disconnected; case vc::ConnectionState::CONNECTING: return BtVcConnectionState::Connecting; case vc::ConnectionState::CONNECTED: return BtVcConnectionState::Connected; case vc::ConnectionState::DISCONNECTING: return BtVcConnectionState::Disconnecting; default: log::assert_that(false, "Unhandled enum value from C++"); } return BtVcConnectionState{}; } static void connection_state_cb(vc::ConnectionState state, const RawAddress& address) { vc_connection_state_callback(to_rust_btvc_connection_state(state), address); } static void volume_state_cb( const RawAddress& address, uint8_t volume, bool mute, bool is_autonomous) { vc_volume_state_callback(address, volume, mute, is_autonomous); } static void group_volume_state_cb(int group_id, uint8_t volume, bool mute, bool is_autonomous) { vc_group_volume_state_callback(group_id, volume, mute, is_autonomous); } static void device_available_cb(const RawAddress& address, uint8_t num_offset) { vc_device_available_callback(address, num_offset); } static void ext_audio_out_volume_offset_cb( const RawAddress& address, uint8_t ext_output_id, int16_t offset) { vc_ext_audio_out_volume_offset_callback(address, ext_output_id, offset); } static void ext_audio_out_location_cb( const RawAddress& address, uint8_t ext_output_id, uint32_t location) { vc_ext_audio_out_location_callback(address, ext_output_id, location); } static void ext_audio_out_description_cb( const RawAddress& address, uint8_t ext_output_id, std::string descr) { vc_ext_audio_out_description_callback(address, ext_output_id, descr); } } // namespace internal class DBusVolumeControlCallbacks : public vc::VolumeControlCallbacks { public: static vc::VolumeControlCallbacks* GetInstance() { static auto instance = new DBusVolumeControlCallbacks(); return instance; } DBusVolumeControlCallbacks(){}; void OnConnectionState(vc::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 OnVolumeStateChanged( const RawAddress& address, uint8_t volume, bool mute, bool is_autonomous) override { log::info( "address={}, volume={}, mute={}, is_autonomous={}", ADDRESS_TO_LOGGABLE_CSTR(address), volume, mute, is_autonomous); topshim::rust::internal::volume_state_cb(address, volume, mute, is_autonomous); } void OnGroupVolumeStateChanged( int group_id, uint8_t volume, bool mute, bool is_autonomous) override { log::info( "group_id={}, volume={}, mute={}, is_autonomous={}", group_id, volume, mute, is_autonomous); topshim::rust::internal::group_volume_state_cb(group_id, volume, mute, is_autonomous); } void OnDeviceAvailable(const RawAddress& address, uint8_t num_offset) override { log::info("address={}, num_offset={}", ADDRESS_TO_LOGGABLE_CSTR(address), num_offset); topshim::rust::internal::device_available_cb(address, num_offset); } void OnExtAudioOutVolumeOffsetChanged( const RawAddress& address, uint8_t ext_output_id, int16_t offset) override { log::info( "address={}, ext_output_id={}, offset={}", ADDRESS_TO_LOGGABLE_CSTR(address), ext_output_id, offset); topshim::rust::internal::ext_audio_out_volume_offset_cb(address, ext_output_id, offset); } void OnExtAudioOutLocationChanged( const RawAddress& address, uint8_t ext_output_id, uint32_t location) override { log::info( "address={}, ext_output_id, location={}", ADDRESS_TO_LOGGABLE_CSTR(address), ext_output_id, location); topshim::rust::internal::ext_audio_out_location_cb(address, ext_output_id, location); } void OnExtAudioOutDescriptionChanged( const RawAddress& address, uint8_t ext_output_id, std::string descr) override { log::info( "address={}, ext_output_id={}, descr={}", ADDRESS_TO_LOGGABLE_CSTR(address), ext_output_id, descr.c_str()); topshim::rust::internal::ext_audio_out_description_cb(address, ext_output_id, descr); } }; std::unique_ptr<VolumeControlIntf> GetVolumeControlProfile(const unsigned char* btif) { if (internal::g_vc_if) std::abort(); const bt_interface_t* btif_ = reinterpret_cast<const bt_interface_t*>(btif); auto vc_if = std::make_unique<VolumeControlIntf>( const_cast<vc::VolumeControlInterface*>(reinterpret_cast<const vc::VolumeControlInterface*>( btif_->get_profile_interface("volume_control")))); internal::g_vc_if = vc_if.get(); return vc_if; } void VolumeControlIntf::init(/*VolumeControlCallbacks* callbacks*/) { return intf_->Init(DBusVolumeControlCallbacks::GetInstance()); } void VolumeControlIntf::cleanup() { return intf_->Cleanup(); } void VolumeControlIntf::connect(RawAddress addr) { return intf_->Connect(addr); } void VolumeControlIntf::disconnect(RawAddress addr) { return intf_->Disconnect(addr); } void VolumeControlIntf::remove_device(RawAddress addr) { return intf_->RemoveDevice(addr); } void VolumeControlIntf::set_volume(int group_id, uint8_t volume) { return intf_->SetVolume(group_id, volume); } void VolumeControlIntf::mute(RawAddress addr) { return intf_->Mute(addr); } void VolumeControlIntf::unmute(RawAddress addr) { return intf_->Unmute(addr); } void VolumeControlIntf::get_ext_audio_out_volume_offset(RawAddress addr, uint8_t ext_output_id) { return intf_->GetExtAudioOutVolumeOffset(addr, ext_output_id); } void VolumeControlIntf::set_ext_audio_out_volume_offset( RawAddress addr, uint8_t ext_output_id, int16_t offset_val) { return intf_->SetExtAudioOutVolumeOffset(addr, ext_output_id, offset_val); } void VolumeControlIntf::get_ext_audio_out_location(RawAddress addr, uint8_t ext_output_id) { return intf_->GetExtAudioOutLocation(addr, ext_output_id); } void VolumeControlIntf::set_ext_audio_out_location( RawAddress addr, uint8_t ext_output_id, uint32_t location) { return intf_->SetExtAudioOutLocation(addr, ext_output_id, location); } void VolumeControlIntf::get_ext_audio_out_description(RawAddress addr, uint8_t ext_output_id) { return intf_->GetExtAudioOutDescription(addr, ext_output_id); } void VolumeControlIntf::set_ext_audio_out_description( RawAddress addr, uint8_t ext_output_id, const char* descr) { return intf_->SetExtAudioOutDescription(addr, ext_output_id, std::string(descr)); } } // namespace rust } // namespace topshim } // namespace bluetooth