Loading system/gd/rust/linux/client/Cargo.toml +1 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ shell-words = "1.1.0" tokio = { version = "1", features = ['bytes', 'fs', 'io-util', 'libc', 'macros', 'memchr', 'mio', 'net', 'num_cpus', 'rt', 'rt-multi-thread', 'sync', 'time', 'tokio-macros'] } clap = "2.33.3" chrono = "0.4.24" bitflags = "1.2" hex = "0.4.3" [build-dependencies] Loading system/gd/rust/linux/client/src/callbacks.rs +110 −3 Original line number Diff line number Diff line Loading @@ -3,9 +3,9 @@ use crate::dbus_iface::{ export_admin_policy_callback_dbus_intf, export_advertising_set_callback_dbus_intf, export_bluetooth_callback_dbus_intf, export_bluetooth_connection_callback_dbus_intf, export_bluetooth_gatt_callback_dbus_intf, export_bluetooth_manager_callback_dbus_intf, export_gatt_server_callback_dbus_intf, export_qa_callback_dbus_intf, export_scanner_callback_dbus_intf, export_socket_callback_dbus_intf, export_suspend_callback_dbus_intf, export_bluetooth_media_callback_dbus_intf, export_gatt_server_callback_dbus_intf, export_qa_callback_dbus_intf, export_scanner_callback_dbus_intf, export_socket_callback_dbus_intf, export_suspend_callback_dbus_intf, }; use crate::ClientContext; use crate::{console_red, console_yellow, print_error, print_info}; Loading @@ -21,6 +21,7 @@ use btstack::bluetooth_gatt::{ BluetoothGattService, IBluetoothGattCallback, IBluetoothGattServerCallback, IScannerCallback, ScanResult, }; use btstack::bluetooth_media::{BluetoothAudioDevice, IBluetoothMediaCallback}; use btstack::bluetooth_qa::IBluetoothQACallback; use btstack::socket_manager::{ BluetoothServerSocket, BluetoothSocket, IBluetoothSocketManager, Loading @@ -29,10 +30,12 @@ use btstack::socket_manager::{ use btstack::suspend::ISuspendCallback; use btstack::uuid::UuidWrapper; use btstack::{RPCProxy, SuspendMode}; use chrono::{TimeZone, Utc}; use dbus::nonblock::SyncConnection; use dbus_crossroads::Crossroads; use dbus_projection::DisconnectWatcher; use manager_service::iface_bluetooth_manager::IBluetoothManagerCallback; use std::convert::TryFrom; use std::io::{Read, Write}; use std::sync::{Arc, Mutex}; use std::time::Duration; Loading @@ -40,6 +43,10 @@ use std::time::Duration; const SOCKET_TEST_WRITE: &[u8] = b"01234567890123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; // Avoid 32, 40, 64 consecutive hex characters so CrOS feedback redact tool // doesn't trim our dump. const BINARY_PACKET_STATUS_WRAP: usize = 50; /// Callback context for manager interface callbacks. pub(crate) struct BtManagerCallback { objpath: String, Loading Loading @@ -1287,3 +1294,103 @@ impl RPCProxy for QACallback { cr.lock().unwrap().insert(self.get_object_id(), &[iface], Arc::new(Mutex::new(self))); } } pub(crate) struct MediaCallback { objpath: String, dbus_connection: Arc<SyncConnection>, dbus_crossroads: Arc<Mutex<Crossroads>>, } impl MediaCallback { pub(crate) fn new( objpath: String, dbus_connection: Arc<SyncConnection>, dbus_crossroads: Arc<Mutex<Crossroads>>, ) -> Self { Self { objpath, dbus_connection, dbus_crossroads } } } fn timestamp_to_string(ts_in_us: u64) -> String { i64::try_from(ts_in_us) .and_then(|ts| Ok(Utc.timestamp_nanos(ts * 1000).to_rfc3339())) .unwrap_or("UNKNOWN".to_string()) } impl IBluetoothMediaCallback for MediaCallback { fn on_bluetooth_audio_device_added(&mut self, _device: BluetoothAudioDevice) {} fn on_bluetooth_audio_device_removed(&mut self, _addr: String) {} fn on_absolute_volume_supported_changed(&mut self, _supported: bool) {} fn on_absolute_volume_changed(&mut self, _volume: u8) {} fn on_hfp_volume_changed(&mut self, _volume: u8, _addr: String) {} fn on_hfp_audio_disconnected(&mut self, _addr: String) {} fn on_hfp_debug_dump( &mut self, active: bool, wbs: bool, total_num_decoded_frames: i32, pkt_loss_ratio: f64, begin_ts: u64, end_ts: u64, pkt_status_in_hex: String, pkt_status_in_binary: String, ) { let wbs_dump = if active && wbs { let mut to_split_binary = pkt_status_in_binary.clone(); let mut wrapped_binary = String::new(); while to_split_binary.len() > BINARY_PACKET_STATUS_WRAP { let remaining = to_split_binary.split_off(BINARY_PACKET_STATUS_WRAP); wrapped_binary.push_str(&to_split_binary); wrapped_binary.push('\n'); to_split_binary = remaining; } wrapped_binary.push_str(&to_split_binary); format!( "\n--------WBS packet loss--------\n\ Decoded Packets: {}, Packet Loss Ratio: {} \n\ {} [begin]\n\ {} [end]\n\ In Hex format:\n\ {}\n\ In binary format:\n\ {}", total_num_decoded_frames, pkt_loss_ratio, timestamp_to_string(begin_ts), timestamp_to_string(end_ts), pkt_status_in_hex, wrapped_binary ) } else { "".to_string() }; print_info!( "\n--------HFP debug dump---------\n\ HFP SCO: {}, Codec: {}\ {} ", if active { "active" } else { "inactive" }, if wbs { "mSBC" } else { "CVSD" }, wbs_dump ); } } impl RPCProxy for MediaCallback { fn get_object_id(&self) -> String { self.objpath.clone() } fn export_for_rpc(self: Box<Self>) { let cr = self.dbus_crossroads.clone(); let iface = export_bluetooth_media_callback_dbus_intf( self.dbus_connection.clone(), &mut cr.lock().unwrap(), Arc::new(Mutex::new(DisconnectWatcher::new())), ); cr.lock().unwrap().insert(self.get_object_id(), &[iface], Arc::new(Mutex::new(self))); } } system/gd/rust/linux/client/src/command_handler.rs +26 −1 Original line number Diff line number Diff line Loading @@ -15,7 +15,7 @@ use bt_topshim::profiles::sdp::{BtSdpMpsRecord, BtSdpRecord}; use bt_topshim::profiles::{gatt::LePhy, ProfileConnectionState}; use btstack::bluetooth::{BluetoothDevice, IBluetooth}; use btstack::bluetooth_gatt::{GattWriteType, IBluetoothGatt, ScanSettings, ScanType}; use btstack::bluetooth_media::IBluetoothTelephony; use btstack::bluetooth_media::{IBluetoothMedia, IBluetoothTelephony}; use btstack::bluetooth_qa::IBluetoothQA; use btstack::socket_manager::{IBluetoothSocketManager, SocketResult}; use btstack::uuid::{Profile, UuidHelper, UuidWrapper}; Loading Loading @@ -325,6 +325,14 @@ fn build_commands() -> HashMap<String, CommandOption> { function_pointer: CommandHandler::cmd_telephony, }, ); command_options.insert( String::from("media"), CommandOption { rules: vec![String::from("media log")], description: String::from("Audio tools."), function_pointer: CommandHandler::cmd_media, }, ); command_options.insert( String::from("quit"), CommandOption { Loading Loading @@ -1965,6 +1973,23 @@ impl CommandHandler { Ok(()) } fn cmd_media(&mut self, args: &Vec<String>) -> CommandResult { if !self.context.lock().unwrap().adapter_ready { return Err(self.adapter_not_ready()); } match &get_arg(args, 0)?[..] { "log" => { self.context.lock().unwrap().media_dbus.as_mut().unwrap().trigger_debug_dump(); } other => { return Err(format!("Invalid argument '{}'", other).into()); } } Ok(()) } } #[cfg(test)] Loading system/gd/rust/linux/client/src/dbus_iface.rs +237 −1 Original line number Diff line number Diff line Loading @@ -4,7 +4,10 @@ use bt_topshim::btif::{ BtBondState, BtConnectionState, BtDeviceType, BtDiscMode, BtPropertyType, BtSspVariant, BtStatus, BtTransport, BtVendorProductInfo, Uuid, Uuid128Bit, }; use bt_topshim::profiles::a2dp::{A2dpCodecConfig, PresentationPosition}; use bt_topshim::profiles::avrcp::PlayerMetadata; use bt_topshim::profiles::gatt::{AdvertisingStatus, GattStatus, LePhy}; use bt_topshim::profiles::hfp::HfpCodecCapability; use bt_topshim::profiles::hid_host::BthhReportType; use bt_topshim::profiles::sdp::{ BtSdpDipRecord, BtSdpHeaderOverlay, BtSdpMasRecord, BtSdpMnsRecord, BtSdpMpsRecord, Loading @@ -29,7 +32,9 @@ use btstack::bluetooth_gatt::{ IBluetoothGattServerCallback, IScannerCallback, ScanFilter, ScanFilterCondition, ScanFilterPattern, ScanResult, ScanSettings, ScanType, }; use btstack::bluetooth_media::IBluetoothTelephony; use btstack::bluetooth_media::{ BluetoothAudioDevice, IBluetoothMedia, IBluetoothMediaCallback, IBluetoothTelephony, }; use btstack::bluetooth_qa::IBluetoothQA; use btstack::socket_manager::{ BluetoothServerSocket, BluetoothSocket, CallbackId, IBluetoothSocketManager, Loading Loading @@ -397,6 +402,29 @@ struct ScanFilterPatternDBus { content: Vec<u8>, } #[dbus_propmap(A2dpCodecConfig)] pub struct A2dpCodecConfigDBus { codec_type: i32, codec_priority: i32, sample_rate: i32, bits_per_sample: i32, channel_mode: i32, codec_specific_1: i64, codec_specific_2: i64, codec_specific_3: i64, codec_specific_4: i64, } impl_dbus_arg_from_into!(HfpCodecCapability, i32); #[dbus_propmap(BluetoothAudioDevice)] pub struct BluetoothAudioDeviceDBus { address: String, name: String, a2dp_caps: Vec<A2dpCodecConfig>, hfp_cap: HfpCodecCapability, absolute_volume: bool, } // Manually converts enum variant from/into D-Bus. // // The ScanFilterCondition enum variant is represented as a D-Bus dictionary with one and only one Loading Loading @@ -502,6 +530,22 @@ struct ScanResultDBus { adv_data: Vec<u8>, } #[dbus_propmap(PresentationPosition)] struct PresentationPositionDBus { remote_delay_report_ns: u64, total_bytes_read: u64, data_position_sec: i64, data_position_nsec: i32, } #[dbus_propmap(PlayerMetadata)] struct PlayerMetadataDBus { title: String, artist: String, album: String, length_us: i64, } struct IBluetoothCallbackDBus {} impl RPCProxy for IBluetoothCallbackDBus {} Loading Loading @@ -2378,3 +2422,195 @@ impl IBluetoothQACallback for IBluetoothQACallbackDBus { dbus_generated!() } } pub(crate) struct BluetoothMediaDBusRPC { client_proxy: ClientDBusProxy, } pub(crate) struct BluetoothMediaDBus { client_proxy: ClientDBusProxy, pub rpc: BluetoothMediaDBusRPC, } impl BluetoothMediaDBus { fn make_client_proxy(conn: Arc<SyncConnection>, index: i32) -> ClientDBusProxy { ClientDBusProxy::new( conn.clone(), String::from("org.chromium.bluetooth"), make_object_path(index, "media"), String::from("org.chromium.bluetooth.BluetoothMedia"), ) } pub(crate) fn new(conn: Arc<SyncConnection>, index: i32) -> BluetoothMediaDBus { BluetoothMediaDBus { client_proxy: Self::make_client_proxy(conn.clone(), index), rpc: BluetoothMediaDBusRPC { client_proxy: Self::make_client_proxy(conn.clone(), index), }, } } } #[generate_dbus_interface_client(BluetoothMediaDBusRPC)] impl IBluetoothMedia for BluetoothMediaDBus { #[dbus_method("RegisterCallback")] fn register_callback(&mut self, callback: Box<dyn IBluetoothMediaCallback + Send>) -> bool { dbus_generated!() } #[dbus_method("Initialize")] fn initialize(&mut self) -> bool { dbus_generated!() } #[dbus_method("Cleanup")] fn cleanup(&mut self) -> bool { dbus_generated!() } #[dbus_method("Connect")] fn connect(&mut self, address: String) { dbus_generated!() } #[dbus_method("Disconnect")] fn disconnect(&mut self, address: String) { dbus_generated!() } #[dbus_method("SetActiveDevice")] fn set_active_device(&mut self, address: String) { dbus_generated!() } #[dbus_method("ResetActiveDevice")] fn reset_active_device(&mut self) { dbus_generated!() } #[dbus_method("SetHfpActiveDevice")] fn set_hfp_active_device(&mut self, address: String) { dbus_generated!() } #[dbus_method("SetAudioConfig")] fn set_audio_config( &mut self, sample_rate: i32, bits_per_sample: i32, channel_mode: i32, ) -> bool { dbus_generated!() } #[dbus_method("SetVolume")] fn set_volume(&mut self, volume: u8) { dbus_generated!() } #[dbus_method("SetHfpVolume")] fn set_hfp_volume(&mut self, volume: u8, address: String) { dbus_generated!() } #[dbus_method("StartAudioRequest")] fn start_audio_request(&mut self) -> bool { dbus_generated!() } #[dbus_method("GetA2dpAudioStarted")] fn get_a2dp_audio_started(&mut self, address: String) -> bool { dbus_generated!() } #[dbus_method("StopAudioRequest")] fn stop_audio_request(&mut self) { dbus_generated!() } #[dbus_method("StartScoCall")] fn start_sco_call(&mut self, address: String, sco_offload: bool, force_cvsd: bool) -> bool { dbus_generated!() } #[dbus_method("GetHfpAudioFinalCodecs")] fn get_hfp_audio_final_codecs(&mut self, address: String) -> u8 { dbus_generated!() } #[dbus_method("StopScoCall")] fn stop_sco_call(&mut self, address: String) { dbus_generated!() } #[dbus_method("GetPresentationPosition")] fn get_presentation_position(&mut self) -> PresentationPosition { dbus_generated!() } // Temporary AVRCP-related meida DBUS APIs. The following APIs intercept between Chrome CRAS // and cras_server as an expedited solution for AVRCP implementation. The APIs are subject to // change when retiring Chrome CRAS. #[dbus_method("SetPlayerPlaybackStatus")] fn set_player_playback_status(&mut self, status: String) { dbus_generated!() } #[dbus_method("SetPlayerPosition")] fn set_player_position(&mut self, position_us: i64) { dbus_generated!() } #[dbus_method("SetPlayerMetadata")] fn set_player_metadata(&mut self, metadata: PlayerMetadata) { dbus_generated!() } #[dbus_method("TriggerDebugDump")] fn trigger_debug_dump(&mut self) { dbus_generated!() } } struct IBluetoothMediaCallbackDBus {} impl RPCProxy for IBluetoothMediaCallbackDBus {} #[generate_dbus_exporter( export_bluetooth_media_callback_dbus_intf, "org.chromium.bluetooth.BluetoothMediaCallback" )] impl IBluetoothMediaCallback for IBluetoothMediaCallbackDBus { #[dbus_method("OnBluetoothAudioDeviceAdded")] fn on_bluetooth_audio_device_added(&mut self, device: BluetoothAudioDevice) {} #[dbus_method("OnBluetoothAudioDeviceRemoved")] fn on_bluetooth_audio_device_removed(&mut self, addr: String) {} #[dbus_method("OnAbsoluteVolumeSupportedChanged")] fn on_absolute_volume_supported_changed(&mut self, supported: bool) {} #[dbus_method("OnAbsoluteVolumeChanged")] fn on_absolute_volume_changed(&mut self, volume: u8) {} #[dbus_method("OnHfpVolumeChanged")] fn on_hfp_volume_changed(&mut self, volume: u8, addr: String) {} #[dbus_method("OnHfpAudioDisconnected")] fn on_hfp_audio_disconnected(&mut self, addr: String) {} #[dbus_method("OnHfpDebugDump")] fn on_hfp_debug_dump( &mut self, active: bool, wbs: bool, total_num_decoded_frames: i32, pkt_loss_ratio: f64, begin_ts: u64, end_ts: u64, pkt_status_in_hex: String, pkt_status_in_binary: String, ) { } } system/gd/rust/linux/client/src/main.rs +27 −3 Original line number Diff line number Diff line Loading @@ -13,12 +13,13 @@ use crate::bt_adv::AdvSet; use crate::bt_gatt::GattClientContext; use crate::callbacks::{ AdminCallback, AdvertisingSetCallback, BtCallback, BtConnectionCallback, BtManagerCallback, BtSocketManagerCallback, QACallback, ScannerCallback, SuspendCallback, BtSocketManagerCallback, MediaCallback, QACallback, ScannerCallback, SuspendCallback, }; use crate::command_handler::{CommandHandler, SocketSchedule}; use crate::dbus_iface::{ BluetoothAdminDBus, BluetoothDBus, BluetoothGattDBus, BluetoothManagerDBus, BluetoothQADBus, BluetoothQALegacyDBus, BluetoothSocketManagerDBus, BluetoothTelephonyDBus, SuspendDBus, BluetoothAdminDBus, BluetoothDBus, BluetoothGattDBus, BluetoothManagerDBus, BluetoothMediaDBus, BluetoothQADBus, BluetoothQALegacyDBus, BluetoothSocketManagerDBus, BluetoothTelephonyDBus, SuspendDBus, }; use crate::editor::AsyncEditor; use bt_topshim::topstack; Loading Loading @@ -95,6 +96,9 @@ pub(crate) struct ClientContext { /// Proxy for Telephony interface. pub(crate) telephony_dbus: Option<BluetoothTelephonyDBus>, /// Proxy for Media interface. pub(crate) media_dbus: Option<BluetoothMediaDBus>, /// Channel to send actions to take in the foreground fg: mpsc::Sender<ForegroundActions>, Loading Loading @@ -168,6 +172,7 @@ impl ClientContext { suspend_dbus: None, socket_manager_dbus: None, telephony_dbus: None, media_dbus: None, fg: tx, dbus_connection, dbus_crossroads, Loading Loading @@ -228,6 +233,8 @@ impl ClientContext { self.telephony_dbus = Some(BluetoothTelephonyDBus::new(conn.clone(), idx)); self.media_dbus = Some(BluetoothMediaDBus::new(conn.clone(), idx)); // Trigger callback registration in the foreground let fg = self.fg.clone(); tokio::spawn(async move { Loading Loading @@ -470,6 +477,8 @@ async fn start_interactive_shell( format!("/org/chromium/bluetooth/client/{}/socket_manager_callback", adapter); let qa_cb_objpath: String = format!("/org/chromium/bluetooth/client/{}/qa_manager_callback", adapter); let media_cb_objpath: String = format!("/org/chromium/bluetooth/client/{}/bluetooth_media_callback", adapter); let dbus_connection = context.lock().unwrap().dbus_connection.clone(); let dbus_crossroads = context.lock().unwrap().dbus_crossroads.clone(); Loading Loading @@ -601,6 +610,21 @@ async fn start_interactive_shell( ), )); context .lock() .unwrap() .media_dbus .as_mut() .unwrap() .rpc .register_callback(Box::new(MediaCallback::new( media_cb_objpath, dbus_connection.clone(), dbus_crossroads.clone(), ))) .await .expect("D-Bus error on IBluetoothMedia::RegisterCallback"); context.lock().unwrap().adapter_ready = true; let adapter_address = context.lock().unwrap().update_adapter_address(); context.lock().unwrap().update_bonded_devices(); Loading Loading
system/gd/rust/linux/client/Cargo.toml +1 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ shell-words = "1.1.0" tokio = { version = "1", features = ['bytes', 'fs', 'io-util', 'libc', 'macros', 'memchr', 'mio', 'net', 'num_cpus', 'rt', 'rt-multi-thread', 'sync', 'time', 'tokio-macros'] } clap = "2.33.3" chrono = "0.4.24" bitflags = "1.2" hex = "0.4.3" [build-dependencies] Loading
system/gd/rust/linux/client/src/callbacks.rs +110 −3 Original line number Diff line number Diff line Loading @@ -3,9 +3,9 @@ use crate::dbus_iface::{ export_admin_policy_callback_dbus_intf, export_advertising_set_callback_dbus_intf, export_bluetooth_callback_dbus_intf, export_bluetooth_connection_callback_dbus_intf, export_bluetooth_gatt_callback_dbus_intf, export_bluetooth_manager_callback_dbus_intf, export_gatt_server_callback_dbus_intf, export_qa_callback_dbus_intf, export_scanner_callback_dbus_intf, export_socket_callback_dbus_intf, export_suspend_callback_dbus_intf, export_bluetooth_media_callback_dbus_intf, export_gatt_server_callback_dbus_intf, export_qa_callback_dbus_intf, export_scanner_callback_dbus_intf, export_socket_callback_dbus_intf, export_suspend_callback_dbus_intf, }; use crate::ClientContext; use crate::{console_red, console_yellow, print_error, print_info}; Loading @@ -21,6 +21,7 @@ use btstack::bluetooth_gatt::{ BluetoothGattService, IBluetoothGattCallback, IBluetoothGattServerCallback, IScannerCallback, ScanResult, }; use btstack::bluetooth_media::{BluetoothAudioDevice, IBluetoothMediaCallback}; use btstack::bluetooth_qa::IBluetoothQACallback; use btstack::socket_manager::{ BluetoothServerSocket, BluetoothSocket, IBluetoothSocketManager, Loading @@ -29,10 +30,12 @@ use btstack::socket_manager::{ use btstack::suspend::ISuspendCallback; use btstack::uuid::UuidWrapper; use btstack::{RPCProxy, SuspendMode}; use chrono::{TimeZone, Utc}; use dbus::nonblock::SyncConnection; use dbus_crossroads::Crossroads; use dbus_projection::DisconnectWatcher; use manager_service::iface_bluetooth_manager::IBluetoothManagerCallback; use std::convert::TryFrom; use std::io::{Read, Write}; use std::sync::{Arc, Mutex}; use std::time::Duration; Loading @@ -40,6 +43,10 @@ use std::time::Duration; const SOCKET_TEST_WRITE: &[u8] = b"01234567890123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; // Avoid 32, 40, 64 consecutive hex characters so CrOS feedback redact tool // doesn't trim our dump. const BINARY_PACKET_STATUS_WRAP: usize = 50; /// Callback context for manager interface callbacks. pub(crate) struct BtManagerCallback { objpath: String, Loading Loading @@ -1287,3 +1294,103 @@ impl RPCProxy for QACallback { cr.lock().unwrap().insert(self.get_object_id(), &[iface], Arc::new(Mutex::new(self))); } } pub(crate) struct MediaCallback { objpath: String, dbus_connection: Arc<SyncConnection>, dbus_crossroads: Arc<Mutex<Crossroads>>, } impl MediaCallback { pub(crate) fn new( objpath: String, dbus_connection: Arc<SyncConnection>, dbus_crossroads: Arc<Mutex<Crossroads>>, ) -> Self { Self { objpath, dbus_connection, dbus_crossroads } } } fn timestamp_to_string(ts_in_us: u64) -> String { i64::try_from(ts_in_us) .and_then(|ts| Ok(Utc.timestamp_nanos(ts * 1000).to_rfc3339())) .unwrap_or("UNKNOWN".to_string()) } impl IBluetoothMediaCallback for MediaCallback { fn on_bluetooth_audio_device_added(&mut self, _device: BluetoothAudioDevice) {} fn on_bluetooth_audio_device_removed(&mut self, _addr: String) {} fn on_absolute_volume_supported_changed(&mut self, _supported: bool) {} fn on_absolute_volume_changed(&mut self, _volume: u8) {} fn on_hfp_volume_changed(&mut self, _volume: u8, _addr: String) {} fn on_hfp_audio_disconnected(&mut self, _addr: String) {} fn on_hfp_debug_dump( &mut self, active: bool, wbs: bool, total_num_decoded_frames: i32, pkt_loss_ratio: f64, begin_ts: u64, end_ts: u64, pkt_status_in_hex: String, pkt_status_in_binary: String, ) { let wbs_dump = if active && wbs { let mut to_split_binary = pkt_status_in_binary.clone(); let mut wrapped_binary = String::new(); while to_split_binary.len() > BINARY_PACKET_STATUS_WRAP { let remaining = to_split_binary.split_off(BINARY_PACKET_STATUS_WRAP); wrapped_binary.push_str(&to_split_binary); wrapped_binary.push('\n'); to_split_binary = remaining; } wrapped_binary.push_str(&to_split_binary); format!( "\n--------WBS packet loss--------\n\ Decoded Packets: {}, Packet Loss Ratio: {} \n\ {} [begin]\n\ {} [end]\n\ In Hex format:\n\ {}\n\ In binary format:\n\ {}", total_num_decoded_frames, pkt_loss_ratio, timestamp_to_string(begin_ts), timestamp_to_string(end_ts), pkt_status_in_hex, wrapped_binary ) } else { "".to_string() }; print_info!( "\n--------HFP debug dump---------\n\ HFP SCO: {}, Codec: {}\ {} ", if active { "active" } else { "inactive" }, if wbs { "mSBC" } else { "CVSD" }, wbs_dump ); } } impl RPCProxy for MediaCallback { fn get_object_id(&self) -> String { self.objpath.clone() } fn export_for_rpc(self: Box<Self>) { let cr = self.dbus_crossroads.clone(); let iface = export_bluetooth_media_callback_dbus_intf( self.dbus_connection.clone(), &mut cr.lock().unwrap(), Arc::new(Mutex::new(DisconnectWatcher::new())), ); cr.lock().unwrap().insert(self.get_object_id(), &[iface], Arc::new(Mutex::new(self))); } }
system/gd/rust/linux/client/src/command_handler.rs +26 −1 Original line number Diff line number Diff line Loading @@ -15,7 +15,7 @@ use bt_topshim::profiles::sdp::{BtSdpMpsRecord, BtSdpRecord}; use bt_topshim::profiles::{gatt::LePhy, ProfileConnectionState}; use btstack::bluetooth::{BluetoothDevice, IBluetooth}; use btstack::bluetooth_gatt::{GattWriteType, IBluetoothGatt, ScanSettings, ScanType}; use btstack::bluetooth_media::IBluetoothTelephony; use btstack::bluetooth_media::{IBluetoothMedia, IBluetoothTelephony}; use btstack::bluetooth_qa::IBluetoothQA; use btstack::socket_manager::{IBluetoothSocketManager, SocketResult}; use btstack::uuid::{Profile, UuidHelper, UuidWrapper}; Loading Loading @@ -325,6 +325,14 @@ fn build_commands() -> HashMap<String, CommandOption> { function_pointer: CommandHandler::cmd_telephony, }, ); command_options.insert( String::from("media"), CommandOption { rules: vec![String::from("media log")], description: String::from("Audio tools."), function_pointer: CommandHandler::cmd_media, }, ); command_options.insert( String::from("quit"), CommandOption { Loading Loading @@ -1965,6 +1973,23 @@ impl CommandHandler { Ok(()) } fn cmd_media(&mut self, args: &Vec<String>) -> CommandResult { if !self.context.lock().unwrap().adapter_ready { return Err(self.adapter_not_ready()); } match &get_arg(args, 0)?[..] { "log" => { self.context.lock().unwrap().media_dbus.as_mut().unwrap().trigger_debug_dump(); } other => { return Err(format!("Invalid argument '{}'", other).into()); } } Ok(()) } } #[cfg(test)] Loading
system/gd/rust/linux/client/src/dbus_iface.rs +237 −1 Original line number Diff line number Diff line Loading @@ -4,7 +4,10 @@ use bt_topshim::btif::{ BtBondState, BtConnectionState, BtDeviceType, BtDiscMode, BtPropertyType, BtSspVariant, BtStatus, BtTransport, BtVendorProductInfo, Uuid, Uuid128Bit, }; use bt_topshim::profiles::a2dp::{A2dpCodecConfig, PresentationPosition}; use bt_topshim::profiles::avrcp::PlayerMetadata; use bt_topshim::profiles::gatt::{AdvertisingStatus, GattStatus, LePhy}; use bt_topshim::profiles::hfp::HfpCodecCapability; use bt_topshim::profiles::hid_host::BthhReportType; use bt_topshim::profiles::sdp::{ BtSdpDipRecord, BtSdpHeaderOverlay, BtSdpMasRecord, BtSdpMnsRecord, BtSdpMpsRecord, Loading @@ -29,7 +32,9 @@ use btstack::bluetooth_gatt::{ IBluetoothGattServerCallback, IScannerCallback, ScanFilter, ScanFilterCondition, ScanFilterPattern, ScanResult, ScanSettings, ScanType, }; use btstack::bluetooth_media::IBluetoothTelephony; use btstack::bluetooth_media::{ BluetoothAudioDevice, IBluetoothMedia, IBluetoothMediaCallback, IBluetoothTelephony, }; use btstack::bluetooth_qa::IBluetoothQA; use btstack::socket_manager::{ BluetoothServerSocket, BluetoothSocket, CallbackId, IBluetoothSocketManager, Loading Loading @@ -397,6 +402,29 @@ struct ScanFilterPatternDBus { content: Vec<u8>, } #[dbus_propmap(A2dpCodecConfig)] pub struct A2dpCodecConfigDBus { codec_type: i32, codec_priority: i32, sample_rate: i32, bits_per_sample: i32, channel_mode: i32, codec_specific_1: i64, codec_specific_2: i64, codec_specific_3: i64, codec_specific_4: i64, } impl_dbus_arg_from_into!(HfpCodecCapability, i32); #[dbus_propmap(BluetoothAudioDevice)] pub struct BluetoothAudioDeviceDBus { address: String, name: String, a2dp_caps: Vec<A2dpCodecConfig>, hfp_cap: HfpCodecCapability, absolute_volume: bool, } // Manually converts enum variant from/into D-Bus. // // The ScanFilterCondition enum variant is represented as a D-Bus dictionary with one and only one Loading Loading @@ -502,6 +530,22 @@ struct ScanResultDBus { adv_data: Vec<u8>, } #[dbus_propmap(PresentationPosition)] struct PresentationPositionDBus { remote_delay_report_ns: u64, total_bytes_read: u64, data_position_sec: i64, data_position_nsec: i32, } #[dbus_propmap(PlayerMetadata)] struct PlayerMetadataDBus { title: String, artist: String, album: String, length_us: i64, } struct IBluetoothCallbackDBus {} impl RPCProxy for IBluetoothCallbackDBus {} Loading Loading @@ -2378,3 +2422,195 @@ impl IBluetoothQACallback for IBluetoothQACallbackDBus { dbus_generated!() } } pub(crate) struct BluetoothMediaDBusRPC { client_proxy: ClientDBusProxy, } pub(crate) struct BluetoothMediaDBus { client_proxy: ClientDBusProxy, pub rpc: BluetoothMediaDBusRPC, } impl BluetoothMediaDBus { fn make_client_proxy(conn: Arc<SyncConnection>, index: i32) -> ClientDBusProxy { ClientDBusProxy::new( conn.clone(), String::from("org.chromium.bluetooth"), make_object_path(index, "media"), String::from("org.chromium.bluetooth.BluetoothMedia"), ) } pub(crate) fn new(conn: Arc<SyncConnection>, index: i32) -> BluetoothMediaDBus { BluetoothMediaDBus { client_proxy: Self::make_client_proxy(conn.clone(), index), rpc: BluetoothMediaDBusRPC { client_proxy: Self::make_client_proxy(conn.clone(), index), }, } } } #[generate_dbus_interface_client(BluetoothMediaDBusRPC)] impl IBluetoothMedia for BluetoothMediaDBus { #[dbus_method("RegisterCallback")] fn register_callback(&mut self, callback: Box<dyn IBluetoothMediaCallback + Send>) -> bool { dbus_generated!() } #[dbus_method("Initialize")] fn initialize(&mut self) -> bool { dbus_generated!() } #[dbus_method("Cleanup")] fn cleanup(&mut self) -> bool { dbus_generated!() } #[dbus_method("Connect")] fn connect(&mut self, address: String) { dbus_generated!() } #[dbus_method("Disconnect")] fn disconnect(&mut self, address: String) { dbus_generated!() } #[dbus_method("SetActiveDevice")] fn set_active_device(&mut self, address: String) { dbus_generated!() } #[dbus_method("ResetActiveDevice")] fn reset_active_device(&mut self) { dbus_generated!() } #[dbus_method("SetHfpActiveDevice")] fn set_hfp_active_device(&mut self, address: String) { dbus_generated!() } #[dbus_method("SetAudioConfig")] fn set_audio_config( &mut self, sample_rate: i32, bits_per_sample: i32, channel_mode: i32, ) -> bool { dbus_generated!() } #[dbus_method("SetVolume")] fn set_volume(&mut self, volume: u8) { dbus_generated!() } #[dbus_method("SetHfpVolume")] fn set_hfp_volume(&mut self, volume: u8, address: String) { dbus_generated!() } #[dbus_method("StartAudioRequest")] fn start_audio_request(&mut self) -> bool { dbus_generated!() } #[dbus_method("GetA2dpAudioStarted")] fn get_a2dp_audio_started(&mut self, address: String) -> bool { dbus_generated!() } #[dbus_method("StopAudioRequest")] fn stop_audio_request(&mut self) { dbus_generated!() } #[dbus_method("StartScoCall")] fn start_sco_call(&mut self, address: String, sco_offload: bool, force_cvsd: bool) -> bool { dbus_generated!() } #[dbus_method("GetHfpAudioFinalCodecs")] fn get_hfp_audio_final_codecs(&mut self, address: String) -> u8 { dbus_generated!() } #[dbus_method("StopScoCall")] fn stop_sco_call(&mut self, address: String) { dbus_generated!() } #[dbus_method("GetPresentationPosition")] fn get_presentation_position(&mut self) -> PresentationPosition { dbus_generated!() } // Temporary AVRCP-related meida DBUS APIs. The following APIs intercept between Chrome CRAS // and cras_server as an expedited solution for AVRCP implementation. The APIs are subject to // change when retiring Chrome CRAS. #[dbus_method("SetPlayerPlaybackStatus")] fn set_player_playback_status(&mut self, status: String) { dbus_generated!() } #[dbus_method("SetPlayerPosition")] fn set_player_position(&mut self, position_us: i64) { dbus_generated!() } #[dbus_method("SetPlayerMetadata")] fn set_player_metadata(&mut self, metadata: PlayerMetadata) { dbus_generated!() } #[dbus_method("TriggerDebugDump")] fn trigger_debug_dump(&mut self) { dbus_generated!() } } struct IBluetoothMediaCallbackDBus {} impl RPCProxy for IBluetoothMediaCallbackDBus {} #[generate_dbus_exporter( export_bluetooth_media_callback_dbus_intf, "org.chromium.bluetooth.BluetoothMediaCallback" )] impl IBluetoothMediaCallback for IBluetoothMediaCallbackDBus { #[dbus_method("OnBluetoothAudioDeviceAdded")] fn on_bluetooth_audio_device_added(&mut self, device: BluetoothAudioDevice) {} #[dbus_method("OnBluetoothAudioDeviceRemoved")] fn on_bluetooth_audio_device_removed(&mut self, addr: String) {} #[dbus_method("OnAbsoluteVolumeSupportedChanged")] fn on_absolute_volume_supported_changed(&mut self, supported: bool) {} #[dbus_method("OnAbsoluteVolumeChanged")] fn on_absolute_volume_changed(&mut self, volume: u8) {} #[dbus_method("OnHfpVolumeChanged")] fn on_hfp_volume_changed(&mut self, volume: u8, addr: String) {} #[dbus_method("OnHfpAudioDisconnected")] fn on_hfp_audio_disconnected(&mut self, addr: String) {} #[dbus_method("OnHfpDebugDump")] fn on_hfp_debug_dump( &mut self, active: bool, wbs: bool, total_num_decoded_frames: i32, pkt_loss_ratio: f64, begin_ts: u64, end_ts: u64, pkt_status_in_hex: String, pkt_status_in_binary: String, ) { } }
system/gd/rust/linux/client/src/main.rs +27 −3 Original line number Diff line number Diff line Loading @@ -13,12 +13,13 @@ use crate::bt_adv::AdvSet; use crate::bt_gatt::GattClientContext; use crate::callbacks::{ AdminCallback, AdvertisingSetCallback, BtCallback, BtConnectionCallback, BtManagerCallback, BtSocketManagerCallback, QACallback, ScannerCallback, SuspendCallback, BtSocketManagerCallback, MediaCallback, QACallback, ScannerCallback, SuspendCallback, }; use crate::command_handler::{CommandHandler, SocketSchedule}; use crate::dbus_iface::{ BluetoothAdminDBus, BluetoothDBus, BluetoothGattDBus, BluetoothManagerDBus, BluetoothQADBus, BluetoothQALegacyDBus, BluetoothSocketManagerDBus, BluetoothTelephonyDBus, SuspendDBus, BluetoothAdminDBus, BluetoothDBus, BluetoothGattDBus, BluetoothManagerDBus, BluetoothMediaDBus, BluetoothQADBus, BluetoothQALegacyDBus, BluetoothSocketManagerDBus, BluetoothTelephonyDBus, SuspendDBus, }; use crate::editor::AsyncEditor; use bt_topshim::topstack; Loading Loading @@ -95,6 +96,9 @@ pub(crate) struct ClientContext { /// Proxy for Telephony interface. pub(crate) telephony_dbus: Option<BluetoothTelephonyDBus>, /// Proxy for Media interface. pub(crate) media_dbus: Option<BluetoothMediaDBus>, /// Channel to send actions to take in the foreground fg: mpsc::Sender<ForegroundActions>, Loading Loading @@ -168,6 +172,7 @@ impl ClientContext { suspend_dbus: None, socket_manager_dbus: None, telephony_dbus: None, media_dbus: None, fg: tx, dbus_connection, dbus_crossroads, Loading Loading @@ -228,6 +233,8 @@ impl ClientContext { self.telephony_dbus = Some(BluetoothTelephonyDBus::new(conn.clone(), idx)); self.media_dbus = Some(BluetoothMediaDBus::new(conn.clone(), idx)); // Trigger callback registration in the foreground let fg = self.fg.clone(); tokio::spawn(async move { Loading Loading @@ -470,6 +477,8 @@ async fn start_interactive_shell( format!("/org/chromium/bluetooth/client/{}/socket_manager_callback", adapter); let qa_cb_objpath: String = format!("/org/chromium/bluetooth/client/{}/qa_manager_callback", adapter); let media_cb_objpath: String = format!("/org/chromium/bluetooth/client/{}/bluetooth_media_callback", adapter); let dbus_connection = context.lock().unwrap().dbus_connection.clone(); let dbus_crossroads = context.lock().unwrap().dbus_crossroads.clone(); Loading Loading @@ -601,6 +610,21 @@ async fn start_interactive_shell( ), )); context .lock() .unwrap() .media_dbus .as_mut() .unwrap() .rpc .register_callback(Box::new(MediaCallback::new( media_cb_objpath, dbus_connection.clone(), dbus_crossroads.clone(), ))) .await .expect("D-Bus error on IBluetoothMedia::RegisterCallback"); context.lock().unwrap().adapter_ready = true; let adapter_address = context.lock().unwrap().update_adapter_address(); context.lock().unwrap().update_bonded_devices(); Loading