| //! Anything related to the GATT API (IBluetoothGatt). |
| |
| use btif_macros::{btif_callback, btif_callbacks_dispatcher}; |
| |
| use bt_topshim::bindings::root::bluetooth::Uuid; |
| use bt_topshim::btif::{BluetoothInterface, BtStatus, BtTransport, RawAddress, Uuid128Bit}; |
| use bt_topshim::profiles::gatt::{ |
| AdvertisingStatus, BtGattDbElement, BtGattNotifyParams, BtGattReadParams, Gatt, |
| GattAdvCallbacks, GattAdvCallbacksDispatcher, GattAdvInbandCallbacksDispatcher, |
| GattClientCallbacks, GattClientCallbacksDispatcher, GattScannerCallbacks, |
| GattScannerCallbacksDispatcher, GattScannerInbandCallbacks, |
| GattScannerInbandCallbacksDispatcher, GattServerCallbacksDispatcher, GattStatus, LePhy, |
| MsftAdvMonitor, MsftAdvMonitorPattern, |
| }; |
| use bt_topshim::topstack; |
| use bt_utils::adv_parser; |
| |
| use crate::async_helper::{AsyncHelper, CallbackSender}; |
| use crate::bluetooth::{Bluetooth, IBluetooth}; |
| use crate::bluetooth_adv::{ |
| AdvertiseData, Advertisers, AdvertisingSetInfo, AdvertisingSetParameters, |
| IAdvertisingSetCallback, PeriodicAdvertisingParameters, INVALID_REG_ID, |
| }; |
| use crate::callbacks::Callbacks; |
| use crate::uuid::UuidHelper; |
| use crate::{Message, RPCProxy, SuspendMode}; |
| use log::{debug, warn}; |
| use num_traits::cast::{FromPrimitive, ToPrimitive}; |
| use num_traits::clamp; |
| use rand::rngs::SmallRng; |
| use rand::{RngCore, SeedableRng}; |
| use std::collections::{HashMap, HashSet}; |
| use std::convert::TryInto; |
| use std::sync::{Arc, Mutex, MutexGuard}; |
| use tokio::sync::mpsc::Sender; |
| |
| struct Client { |
| id: Option<i32>, |
| cbid: u32, |
| uuid: Uuid128Bit, |
| is_congested: bool, |
| |
| // Queued on_characteristic_write callback. |
| congestion_queue: Vec<(String, GattStatus, i32)>, |
| } |
| |
| struct Connection { |
| conn_id: i32, |
| address: String, |
| client_id: i32, |
| } |
| |
| struct ContextMap { |
| // TODO(b/196635530): Consider using `multimap` for a more efficient implementation of get by |
| // multiple keys. |
| callbacks: Callbacks<dyn IBluetoothGattCallback + Send>, |
| clients: Vec<Client>, |
| connections: Vec<Connection>, |
| } |
| |
| type GattClientCallback = Box<dyn IBluetoothGattCallback + Send>; |
| |
| impl ContextMap { |
| fn new(tx: Sender<Message>) -> ContextMap { |
| ContextMap { |
| callbacks: Callbacks::new(tx, Message::GattClientCallbackDisconnected), |
| clients: vec![], |
| connections: vec![], |
| } |
| } |
| |
| fn get_by_uuid(&self, uuid: &Uuid128Bit) -> Option<&Client> { |
| self.clients.iter().find(|client| client.uuid == *uuid) |
| } |
| |
| fn get_by_client_id(&self, client_id: i32) -> Option<&Client> { |
| self.clients.iter().find(|client| client.id.is_some() && client.id.unwrap() == client_id) |
| } |
| |
| fn get_by_client_id_mut(&mut self, client_id: i32) -> Option<&mut Client> { |
| self.clients |
| .iter_mut() |
| .find(|client| client.id.is_some() && client.id.unwrap() == client_id) |
| } |
| |
| fn get_by_callback_id(&self, callback_id: u32) -> Option<&Client> { |
| self.clients.iter().find(|client| client.cbid == callback_id) |
| } |
| |
| fn get_address_by_conn_id(&self, conn_id: i32) -> Option<String> { |
| match self.connections.iter().find(|conn| conn.conn_id == conn_id) { |
| None => None, |
| Some(conn) => Some(conn.address.clone()), |
| } |
| } |
| |
| fn get_client_by_conn_id(&self, conn_id: i32) -> Option<&Client> { |
| match self.connections.iter().find(|conn| conn.conn_id == conn_id) { |
| None => None, |
| Some(conn) => self.get_by_client_id(conn.client_id), |
| } |
| } |
| |
| fn get_client_by_conn_id_mut(&mut self, conn_id: i32) -> Option<&mut Client> { |
| let client_id = match self.connections.iter().find(|conn| conn.conn_id == conn_id) { |
| None => return None, |
| Some(conn) => conn.client_id, |
| }; |
| |
| self.get_by_client_id_mut(client_id) |
| } |
| |
| fn add(&mut self, uuid: &Uuid128Bit, callback: GattClientCallback) { |
| if self.get_by_uuid(uuid).is_some() { |
| return; |
| } |
| |
| let cbid = self.callbacks.add_callback(callback); |
| |
| self.clients.push(Client { |
| id: None, |
| cbid, |
| uuid: uuid.clone(), |
| is_congested: false, |
| congestion_queue: vec![], |
| }); |
| } |
| |
| fn remove(&mut self, id: i32) { |
| // Remove any callbacks |
| if let Some(c) = self.get_by_client_id(id) { |
| let cbid = c.cbid; |
| self.remove_callback(cbid); |
| } |
| |
| self.clients.retain(|client| !(client.id.is_some() && client.id.unwrap() == id)); |
| } |
| |
| fn remove_callback(&mut self, callback_id: u32) { |
| self.callbacks.remove_callback(callback_id); |
| } |
| |
| fn set_client_id(&mut self, uuid: &Uuid128Bit, id: i32) { |
| let client = self.clients.iter_mut().find(|client| client.uuid == *uuid); |
| if client.is_none() { |
| return; |
| } |
| |
| client.unwrap().id = Some(id); |
| } |
| |
| fn add_connection(&mut self, client_id: i32, conn_id: i32, address: &String) { |
| if self.get_conn_id_from_address(client_id, address).is_some() { |
| return; |
| } |
| |
| self.connections.push(Connection { conn_id, address: address.clone(), client_id }); |
| } |
| |
| fn remove_connection(&mut self, _client_id: i32, conn_id: i32) { |
| self.connections.retain(|conn| conn.conn_id != conn_id); |
| } |
| |
| fn get_conn_id_from_address(&self, client_id: i32, address: &String) -> Option<i32> { |
| match self |
| .connections |
| .iter() |
| .find(|conn| conn.client_id == client_id && conn.address == *address) |
| { |
| None => None, |
| Some(conn) => Some(conn.conn_id), |
| } |
| } |
| |
| fn get_callback_from_callback_id( |
| &mut self, |
| callback_id: u32, |
| ) -> Option<&mut GattClientCallback> { |
| self.callbacks.get_by_id(callback_id) |
| } |
| } |
| |
| /// Defines the GATT API. |
| // TODO(242083290): Split out interfaces. |
| pub trait IBluetoothGatt { |
| // Scanning |
| |
| /// Returns whether LE Scan can be performed by hardware offload defined by |
| /// [MSFT HCI Extension](https://learn.microsoft.com/en-us/windows-hardware/drivers/bluetooth/microsoft-defined-bluetooth-hci-commands-and-events). |
| fn is_msft_supported(&self) -> bool; |
| |
| /// Registers an LE scanner callback. |
| /// |
| /// Returns the callback id. |
| fn register_scanner_callback(&mut self, callback: Box<dyn IScannerCallback + Send>) -> u32; |
| |
| /// Unregisters an LE scanner callback identified by the given id. |
| fn unregister_scanner_callback(&mut self, callback_id: u32) -> bool; |
| |
| /// Registers LE scanner. |
| /// |
| /// `callback_id`: The callback to receive updates about the scanner state. |
| /// Returns the UUID of the registered scanner. |
| fn register_scanner(&mut self, callback_id: u32) -> Uuid128Bit; |
| |
| /// Unregisters an LE scanner identified by the given scanner id. |
| fn unregister_scanner(&mut self, scanner_id: u8) -> bool; |
| |
| /// Activate scan of the given scanner id. |
| fn start_scan( |
| &mut self, |
| scanner_id: u8, |
| settings: ScanSettings, |
| filter: Option<ScanFilter>, |
| ) -> BtStatus; |
| |
| /// Deactivate scan of the given scanner id. |
| fn stop_scan(&mut self, scanner_id: u8) -> BtStatus; |
| |
| /// Returns the current suspend mode. |
| fn get_scan_suspend_mode(&self) -> SuspendMode; |
| |
| // Advertising |
| |
| /// Registers callback for BLE advertising. |
| fn register_advertiser_callback( |
| &mut self, |
| callback: Box<dyn IAdvertisingSetCallback + Send>, |
| ) -> u32; |
| |
| /// Unregisters callback for BLE advertising. |
| fn unregister_advertiser_callback(&mut self, callback_id: u32); |
| |
| /// Creates a new BLE advertising set and start advertising. |
| /// |
| /// Returns the reg_id for the advertising set, which is used in the callback |
| /// `on_advertising_set_started` to identify the advertising set started. |
| /// |
| /// * `parameters` - Advertising set parameters. |
| /// * `advertise_data` - Advertisement data to be broadcasted. |
| /// * `scan_response` - Scan response. |
| /// * `periodic_parameters` - Periodic advertising parameters. If None, periodic advertising |
| /// will not be started. |
| /// * `periodic_data` - Periodic advertising data. |
| /// * `duration` - Advertising duration, in 10 ms unit. Valid range is from 1 (10 ms) to |
| /// 65535 (655.35 sec). 0 means no advertising timeout. |
| /// * `max_ext_adv_events` - Maximum number of extended advertising events the controller |
| /// shall attempt to send before terminating the extended advertising, even if the |
| /// duration has not expired. Valid range is from 1 to 255. 0 means event count limitation. |
| /// * `callback_id` - Identifies callback registered in register_advertiser_callback. |
| fn start_advertising_set( |
| &mut self, |
| parameters: AdvertisingSetParameters, |
| advertise_data: AdvertiseData, |
| scan_response: Option<AdvertiseData>, |
| periodic_parameters: Option<PeriodicAdvertisingParameters>, |
| periodic_data: Option<AdvertiseData>, |
| duration: i32, |
| max_ext_adv_events: i32, |
| callback_id: u32, |
| ) -> i32; |
| |
| /// Disposes a BLE advertising set. |
| fn stop_advertising_set(&mut self, advertiser_id: i32); |
| |
| /// Queries address associated with the advertising set. |
| fn get_own_address(&mut self, advertiser_id: i32); |
| |
| /// Enables or disables an advertising set. |
| fn enable_advertising_set( |
| &mut self, |
| advertiser_id: i32, |
| enable: bool, |
| duration: i32, |
| max_ext_adv_events: i32, |
| ); |
| |
| /// Updates advertisement data of the advertising set. |
| fn set_advertising_data(&mut self, advertiser_id: i32, data: AdvertiseData); |
| |
| /// Updates scan response of the advertising set. |
| fn set_scan_response_data(&mut self, advertiser_id: i32, data: AdvertiseData); |
| |
| /// Updates advertising parameters of the advertising set. |
| /// |
| /// It must be called when advertising is not active. |
| fn set_advertising_parameters( |
| &mut self, |
| advertiser_id: i32, |
| parameters: AdvertisingSetParameters, |
| ); |
| |
| /// Updates periodic advertising parameters. |
| fn set_periodic_advertising_parameters( |
| &mut self, |
| advertiser_id: i32, |
| parameters: PeriodicAdvertisingParameters, |
| ); |
| |
| /// Updates periodic advertisement data. |
| /// |
| /// It must be called after `set_periodic_advertising_parameters`, or after |
| /// advertising was started with periodic advertising data set. |
| fn set_periodic_advertising_data(&mut self, advertiser_id: i32, data: AdvertiseData); |
| |
| /// Enables or disables periodic advertising. |
| fn set_periodic_advertising_enable( |
| &mut self, |
| advertiser_id: i32, |
| enable: bool, |
| include_adi: bool, |
| ); |
| |
| // GATT Client |
| |
| /// Registers a GATT Client. |
| fn register_client( |
| &mut self, |
| app_uuid: String, |
| callback: Box<dyn IBluetoothGattCallback + Send>, |
| eatt_support: bool, |
| ); |
| |
| /// Unregisters a GATT Client. |
| fn unregister_client(&mut self, client_id: i32); |
| |
| /// Initiates a GATT connection to a peer device. |
| fn client_connect( |
| &self, |
| client_id: i32, |
| addr: String, |
| is_direct: bool, |
| transport: BtTransport, |
| opportunistic: bool, |
| phy: LePhy, |
| ); |
| |
| /// Disconnects a GATT connection. |
| fn client_disconnect(&self, client_id: i32, addr: String); |
| |
| /// Clears the attribute cache of a device. |
| fn refresh_device(&self, client_id: i32, addr: String); |
| |
| /// Enumerates all GATT services on a connected device. |
| fn discover_services(&self, client_id: i32, addr: String); |
| |
| /// Search a GATT service on a connected device based on a UUID. |
| fn discover_service_by_uuid(&self, client_id: i32, addr: String, uuid: String); |
| |
| /// Reads a characteristic on a remote device. |
| fn read_characteristic(&self, client_id: i32, addr: String, handle: i32, auth_req: i32); |
| |
| /// Reads a characteristic on a remote device. |
| fn read_using_characteristic_uuid( |
| &self, |
| client_id: i32, |
| addr: String, |
| uuid: String, |
| start_handle: i32, |
| end_handle: i32, |
| auth_req: i32, |
| ); |
| |
| /// Writes a remote characteristic. |
| fn write_characteristic( |
| &self, |
| client_id: i32, |
| addr: String, |
| handle: i32, |
| write_type: GattWriteType, |
| auth_req: i32, |
| value: Vec<u8>, |
| ) -> GattWriteRequestStatus; |
| |
| /// Reads the descriptor for a given characteristic. |
| fn read_descriptor(&self, client_id: i32, addr: String, handle: i32, auth_req: i32); |
| |
| /// Writes a remote descriptor for a given characteristic. |
| fn write_descriptor( |
| &self, |
| client_id: i32, |
| addr: String, |
| handle: i32, |
| auth_req: i32, |
| value: Vec<u8>, |
| ); |
| |
| /// Registers to receive notifications or indications for a given characteristic. |
| fn register_for_notification(&self, client_id: i32, addr: String, handle: i32, enable: bool); |
| |
| /// Begins reliable write. |
| fn begin_reliable_write(&mut self, client_id: i32, addr: String); |
| |
| /// Ends reliable write. |
| fn end_reliable_write(&mut self, client_id: i32, addr: String, execute: bool); |
| |
| /// Requests RSSI for a given remote device. |
| fn read_remote_rssi(&self, client_id: i32, addr: String); |
| |
| /// Configures the MTU of a given connection. |
| fn configure_mtu(&self, client_id: i32, addr: String, mtu: i32); |
| |
| /// Requests a connection parameter update. |
| fn connection_parameter_update( |
| &self, |
| client_id: i32, |
| addr: String, |
| min_interval: i32, |
| max_interval: i32, |
| latency: i32, |
| timeout: i32, |
| min_ce_len: u16, |
| max_ce_len: u16, |
| ); |
| |
| /// Sets preferred PHY. |
| fn client_set_preferred_phy( |
| &self, |
| client_id: i32, |
| addr: String, |
| tx_phy: LePhy, |
| rx_phy: LePhy, |
| phy_options: i32, |
| ); |
| |
| /// Reads the PHY used by a peer. |
| fn client_read_phy(&mut self, client_id: i32, addr: String); |
| } |
| |
| #[derive(Debug, Default)] |
| /// Represents a GATT Descriptor. |
| pub struct BluetoothGattDescriptor { |
| pub uuid: Uuid128Bit, |
| pub instance_id: i32, |
| pub permissions: i32, |
| } |
| |
| impl BluetoothGattDescriptor { |
| fn new(uuid: Uuid128Bit, instance_id: i32, permissions: i32) -> BluetoothGattDescriptor { |
| BluetoothGattDescriptor { uuid, instance_id, permissions } |
| } |
| } |
| |
| #[derive(Debug, Default)] |
| /// Represents a GATT Characteristic. |
| pub struct BluetoothGattCharacteristic { |
| pub uuid: Uuid128Bit, |
| pub instance_id: i32, |
| pub properties: i32, |
| pub permissions: i32, |
| pub key_size: i32, |
| pub write_type: GattWriteType, |
| pub descriptors: Vec<BluetoothGattDescriptor>, |
| } |
| |
| impl BluetoothGattCharacteristic { |
| pub const PROPERTY_BROADCAST: i32 = 0x01; |
| pub const PROPERTY_READ: i32 = 0x02; |
| pub const PROPERTY_WRITE_NO_RESPONSE: i32 = 0x04; |
| pub const PROPERTY_WRITE: i32 = 0x08; |
| pub const PROPERTY_NOTIFY: i32 = 0x10; |
| pub const PROPERTY_INDICATE: i32 = 0x20; |
| pub const PROPERTY_SIGNED_WRITE: i32 = 0x40; |
| pub const PROPERTY_EXTENDED_PROPS: i32 = 0x80; |
| |
| fn new( |
| uuid: Uuid128Bit, |
| instance_id: i32, |
| properties: i32, |
| permissions: i32, |
| ) -> BluetoothGattCharacteristic { |
| BluetoothGattCharacteristic { |
| uuid, |
| instance_id, |
| properties, |
| permissions, |
| write_type: if properties & BluetoothGattCharacteristic::PROPERTY_WRITE_NO_RESPONSE != 0 |
| { |
| GattWriteType::WriteNoRsp |
| } else { |
| GattWriteType::Write |
| }, |
| key_size: 16, |
| descriptors: vec![], |
| } |
| } |
| } |
| |
| #[derive(Debug, Default)] |
| /// Represents a GATT Service. |
| pub struct BluetoothGattService { |
| pub uuid: Uuid128Bit, |
| pub instance_id: i32, |
| pub service_type: i32, |
| pub characteristics: Vec<BluetoothGattCharacteristic>, |
| pub included_services: Vec<BluetoothGattService>, |
| } |
| |
| impl BluetoothGattService { |
| fn new(uuid: Uuid128Bit, instance_id: i32, service_type: i32) -> BluetoothGattService { |
| BluetoothGattService { |
| uuid, |
| instance_id, |
| service_type, |
| characteristics: vec![], |
| included_services: vec![], |
| } |
| } |
| } |
| |
| /// Callback for GATT Client API. |
| pub trait IBluetoothGattCallback: RPCProxy { |
| /// When the `register_client` request is done. |
| fn on_client_registered(&self, _status: GattStatus, _client_id: i32); |
| |
| /// When there is a change in the state of a GATT client connection. |
| fn on_client_connection_state( |
| &self, |
| _status: GattStatus, |
| _client_id: i32, |
| _connected: bool, |
| _addr: String, |
| ); |
| |
| /// When there is a change of PHY. |
| fn on_phy_update(&self, _addr: String, _tx_phy: LePhy, _rx_phy: LePhy, _status: GattStatus); |
| |
| /// The completion of IBluetoothGatt::read_phy. |
| fn on_phy_read(&self, _addr: String, _tx_phy: LePhy, _rx_phy: LePhy, _status: GattStatus); |
| |
| /// When GATT db is available. |
| fn on_search_complete( |
| &self, |
| _addr: String, |
| _services: Vec<BluetoothGattService>, |
| _status: GattStatus, |
| ); |
| |
| /// The completion of IBluetoothGatt::read_characteristic. |
| fn on_characteristic_read( |
| &self, |
| _addr: String, |
| _status: GattStatus, |
| _handle: i32, |
| _value: Vec<u8>, |
| ); |
| |
| /// The completion of IBluetoothGatt::write_characteristic. |
| fn on_characteristic_write(&self, _addr: String, _status: GattStatus, _handle: i32); |
| |
| /// When a reliable write is completed. |
| fn on_execute_write(&self, _addr: String, _status: GattStatus); |
| |
| /// The completion of IBluetoothGatt::read_descriptor. |
| fn on_descriptor_read(&self, _addr: String, _status: GattStatus, _handle: i32, _value: Vec<u8>); |
| |
| /// The completion of IBluetoothGatt::write_descriptor. |
| fn on_descriptor_write(&self, _addr: String, _status: GattStatus, _handle: i32); |
| |
| /// When notification or indication is received. |
| fn on_notify(&self, _addr: String, _handle: i32, _value: Vec<u8>); |
| |
| /// The completion of IBluetoothGatt::read_remote_rssi. |
| fn on_read_remote_rssi(&self, _addr: String, _rssi: i32, _status: GattStatus); |
| |
| /// The completion of IBluetoothGatt::configure_mtu. |
| fn on_configure_mtu(&self, _addr: String, _mtu: i32, _status: GattStatus); |
| |
| /// When a connection parameter changes. |
| fn on_connection_updated( |
| &self, |
| _addr: String, |
| _interval: i32, |
| _latency: i32, |
| _timeout: i32, |
| _status: GattStatus, |
| ); |
| |
| /// When there is an addition, removal, or change of a GATT service. |
| fn on_service_changed(&self, _addr: String); |
| } |
| |
| /// Interface for scanner callbacks to clients, passed to |
| /// `IBluetoothGatt::register_scanner_callback`. |
| pub trait IScannerCallback: RPCProxy { |
| /// When the `register_scanner` request is done. |
| fn on_scanner_registered(&self, uuid: Uuid128Bit, scanner_id: u8, status: GattStatus); |
| |
| /// When an LE advertisement matching aggregate filters is detected. Since this callback is |
| /// shared among all scanner callbacks, clients may receive more advertisements than what is |
| /// requested to be filtered in. |
| fn on_scan_result(&self, scan_result: ScanResult); |
| |
| /// When an LE advertisement matching aggregate filters is no longer detected. The criteria of |
| /// how a device is considered lost is specified by ScanFilter. |
| fn on_scan_result_lost(&self, scan_result: ScanResult); |
| |
| /// When LE Scan module changes suspend mode due to system suspend/resume. |
| fn on_suspend_mode_change(&self, suspend_mode: SuspendMode); |
| } |
| |
| #[derive(Debug, FromPrimitive, ToPrimitive)] |
| #[repr(u8)] |
| /// GATT write type. |
| enum GattDbElementType { |
| PrimaryService = 0, |
| SecondaryService = 1, |
| IncludedService = 2, |
| Characteristic = 3, |
| Descriptor = 4, |
| } |
| |
| #[derive(Debug, FromPrimitive, ToPrimitive, Copy, Clone)] |
| #[repr(u8)] |
| /// GATT write type. |
| pub enum GattWriteType { |
| Invalid = 0, |
| WriteNoRsp = 1, |
| Write = 2, |
| WritePrepare = 3, |
| } |
| |
| impl Default for GattWriteType { |
| fn default() -> Self { |
| GattWriteType::Write |
| } |
| } |
| |
| #[derive(Debug, FromPrimitive, ToPrimitive)] |
| #[repr(u32)] |
| /// Scan type configuration. |
| pub enum ScanType { |
| Active = 0, |
| Passive = 1, |
| } |
| |
| impl Default for ScanType { |
| fn default() -> Self { |
| ScanType::Active |
| } |
| } |
| |
| /// Represents scanning configurations to be passed to `IBluetoothGatt::start_scan`. |
| /// |
| /// This configuration is general and supported on all Bluetooth hardware, irrelevant of the |
| /// hardware filter offload (APCF or MSFT). |
| #[derive(Debug, Default)] |
| pub struct ScanSettings { |
| pub interval: i32, |
| pub window: i32, |
| pub scan_type: ScanType, |
| } |
| |
| /// Represents scan result |
| #[derive(Debug)] |
| pub struct ScanResult { |
| pub name: String, |
| pub address: String, |
| pub addr_type: u8, |
| pub event_type: u16, |
| pub primary_phy: u8, |
| pub secondary_phy: u8, |
| pub advertising_sid: u8, |
| pub tx_power: i8, |
| pub rssi: i8, |
| pub periodic_adv_int: u16, |
| pub flags: u8, |
| pub service_uuids: Vec<Uuid128Bit>, |
| /// A map of 128-bit UUID and its corresponding service data. |
| pub service_data: HashMap<String, Vec<u8>>, |
| pub manufacturer_data: HashMap<u16, Vec<u8>>, |
| pub adv_data: Vec<u8>, |
| } |
| |
| #[derive(Debug, Clone)] |
| pub struct ScanFilterPattern { |
| /// Specifies the starting byte position of the pattern immediately following AD Type. |
| pub start_position: u8, |
| |
| /// Advertising Data type (https://www.bluetooth.com/specifications/assigned-numbers/). |
| pub ad_type: u8, |
| |
| /// The pattern to be matched for the specified AD Type within the advertisement packet from |
| /// the specified starting byte. |
| pub content: Vec<u8>, |
| } |
| |
| /// Represents the condition for matching advertisements. |
| /// |
| /// Only pattern-based matching is implemented. |
| #[derive(Debug, Clone)] |
| pub enum ScanFilterCondition { |
| /// All advertisements are matched. |
| All, |
| |
| /// Match by pattern anywhere in the advertisement data. Multiple patterns are "OR"-ed. |
| Patterns(Vec<ScanFilterPattern>), |
| |
| /// Match by UUID (not implemented). |
| Uuid, |
| |
| /// Match if the IRK resolves an advertisement (not implemented). |
| Irk, |
| |
| /// Match by Bluetooth address (not implemented). |
| BluetoothAddress, |
| } |
| |
| /// Represents a scan filter to be passed to `IBluetoothGatt::start_scan`. |
| /// |
| /// This filter is intentionally modelled close to the MSFT hardware offload filter. |
| /// Reference: |
| /// https://learn.microsoft.com/en-us/windows-hardware/drivers/bluetooth/microsoft-defined-bluetooth-hci-commands-and-events |
| #[derive(Debug, Clone)] |
| pub struct ScanFilter { |
| /// Advertisements with RSSI above or equal this value is considered "found". |
| pub rssi_high_threshold: u8, |
| |
| /// Advertisements with RSSI below or equal this value (for a period of rssi_low_timeout) is |
| /// considered "lost". |
| pub rssi_low_threshold: u8, |
| |
| /// The time in seconds over which the RSSI value should be below rssi_low_threshold before |
| /// being considered "lost". |
| pub rssi_low_timeout: u8, |
| |
| /// The sampling interval in milliseconds. |
| pub rssi_sampling_period: u8, |
| |
| /// The condition to match advertisements with. |
| pub condition: ScanFilterCondition, |
| } |
| |
| type ScannersMap = HashMap<Uuid, ScannerInfo>; |
| |
| const DEFAULT_ASYNC_TIMEOUT_MS: u64 = 5000; |
| |
| /// Abstraction for async GATT operations. Contains async methods for coordinating async operations |
| /// more conveniently. |
| struct GattAsyncIntf { |
| scanners: Arc<Mutex<ScannersMap>>, |
| gatt: Option<Arc<Mutex<Gatt>>>, |
| |
| async_helper_msft_adv_monitor_add: AsyncHelper<(u8, u8)>, |
| async_helper_msft_adv_monitor_remove: AsyncHelper<u8>, |
| async_helper_msft_adv_monitor_enable: AsyncHelper<u8>, |
| } |
| |
| impl GattAsyncIntf { |
| /// Adds an advertisement monitor. Returns monitor handle and status. |
| async fn msft_adv_monitor_add(&mut self, monitor: MsftAdvMonitor) -> Result<(u8, u8), ()> { |
| let gatt = self.gatt.as_ref().unwrap().clone(); |
| |
| self.async_helper_msft_adv_monitor_add |
| .call_method( |
| move |call_id| { |
| gatt.lock().unwrap().scanner.msft_adv_monitor_add(call_id, &monitor); |
| }, |
| Some(DEFAULT_ASYNC_TIMEOUT_MS), |
| ) |
| .await |
| } |
| |
| /// Removes an advertisement monitor. Returns status. |
| async fn msft_adv_monitor_remove(&mut self, monitor_handle: u8) -> Result<u8, ()> { |
| let gatt = self.gatt.as_ref().unwrap().clone(); |
| |
| self.async_helper_msft_adv_monitor_remove |
| .call_method( |
| move |call_id| { |
| gatt.lock().unwrap().scanner.msft_adv_monitor_remove(call_id, monitor_handle); |
| }, |
| Some(DEFAULT_ASYNC_TIMEOUT_MS), |
| ) |
| .await |
| } |
| |
| /// Enables/disables an advertisement monitor. Returns status. |
| async fn msft_adv_monitor_enable(&mut self, enable: bool) -> Result<u8, ()> { |
| let gatt = self.gatt.as_ref().unwrap().clone(); |
| |
| self.async_helper_msft_adv_monitor_enable |
| .call_method( |
| move |call_id| { |
| gatt.lock().unwrap().scanner.msft_adv_monitor_enable(call_id, enable); |
| }, |
| Some(DEFAULT_ASYNC_TIMEOUT_MS), |
| ) |
| .await |
| } |
| |
| /// Updates the topshim's scan state depending on the states of registered scanners. Scan is |
| /// enabled if there is at least 1 active registered scanner. |
| /// |
| /// Note: this does not need to be async, but declared as async for consistency in this struct. |
| /// May be converted into real async in the future if btif supports it. |
| async fn update_scan(&mut self) { |
| if self.scanners.lock().unwrap().values().find(|scanner| scanner.is_active).is_some() { |
| self.gatt.as_ref().unwrap().lock().unwrap().scanner.start_scan(); |
| } else { |
| self.gatt.as_ref().unwrap().lock().unwrap().scanner.stop_scan(); |
| } |
| } |
| } |
| |
| /// Implementation of the GATT API (IBluetoothGatt). |
| pub struct BluetoothGatt { |
| intf: Arc<Mutex<BluetoothInterface>>, |
| // TODO(b/254870880): Wrapping in an `Option` makes the code unnecessarily verbose. Find a way |
| // to not wrap this in `Option` since we know that we can't function without `gatt` being |
| // initialized anyway. |
| gatt: Option<Arc<Mutex<Gatt>>>, |
| adapter: Option<Arc<Mutex<Box<Bluetooth>>>>, |
| |
| context_map: ContextMap, |
| reliable_queue: HashSet<String>, |
| scanner_callbacks: Callbacks<dyn IScannerCallback + Send>, |
| scanners: Arc<Mutex<ScannersMap>>, |
| advertisers: Advertisers, |
| |
| adv_mon_add_cb_sender: CallbackSender<(u8, u8)>, |
| adv_mon_remove_cb_sender: CallbackSender<u8>, |
| adv_mon_enable_cb_sender: CallbackSender<u8>, |
| |
| // Used for generating random UUIDs. SmallRng is chosen because it is fast, don't use this for |
| // cryptography. |
| small_rng: SmallRng, |
| |
| gatt_async: Arc<tokio::sync::Mutex<GattAsyncIntf>>, |
| } |
| |
| impl BluetoothGatt { |
| /// Constructs a new IBluetoothGatt implementation. |
| pub fn new(intf: Arc<Mutex<BluetoothInterface>>, tx: Sender<Message>) -> BluetoothGatt { |
| let scanners = Arc::new(Mutex::new(HashMap::new())); |
| |
| let async_helper_msft_adv_monitor_add = AsyncHelper::new("MsftAdvMonitorAdd"); |
| let async_helper_msft_adv_monitor_remove = AsyncHelper::new("MsftAdvMonitorRemove"); |
| let async_helper_msft_adv_monitor_enable = AsyncHelper::new("MsftAdvMonitorEnable"); |
| BluetoothGatt { |
| intf, |
| gatt: None, |
| adapter: None, |
| context_map: ContextMap::new(tx.clone()), |
| reliable_queue: HashSet::new(), |
| scanner_callbacks: Callbacks::new(tx.clone(), Message::ScannerCallbackDisconnected), |
| scanners: scanners.clone(), |
| small_rng: SmallRng::from_entropy(), |
| advertisers: Advertisers::new(tx.clone()), |
| adv_mon_add_cb_sender: async_helper_msft_adv_monitor_add.get_callback_sender(), |
| adv_mon_remove_cb_sender: async_helper_msft_adv_monitor_remove.get_callback_sender(), |
| adv_mon_enable_cb_sender: async_helper_msft_adv_monitor_enable.get_callback_sender(), |
| gatt_async: Arc::new(tokio::sync::Mutex::new(GattAsyncIntf { |
| scanners, |
| gatt: None, |
| async_helper_msft_adv_monitor_add, |
| async_helper_msft_adv_monitor_remove, |
| async_helper_msft_adv_monitor_enable, |
| })), |
| } |
| } |
| |
| pub fn init_profiles(&mut self, tx: Sender<Message>, adapter: Arc<Mutex<Box<Bluetooth>>>) { |
| self.gatt = Gatt::new(&self.intf.lock().unwrap()).map(|gatt| Arc::new(Mutex::new(gatt))); |
| self.adapter = Some(adapter); |
| |
| let tx_clone = tx.clone(); |
| let gatt_client_callbacks_dispatcher = GattClientCallbacksDispatcher { |
| dispatch: Box::new(move |cb| { |
| let tx_clone = tx_clone.clone(); |
| topstack::get_runtime().spawn(async move { |
| let _ = tx_clone.send(Message::GattClient(cb)).await; |
| }); |
| }), |
| }; |
| |
| let gatt_server_callbacks_dispatcher = GattServerCallbacksDispatcher { |
| dispatch: Box::new(move |cb| { |
| // TODO(b/193685149): Implement the callbacks |
| debug!("received Gatt server callback: {:?}", cb); |
| }), |
| }; |
| |
| let tx_clone = tx.clone(); |
| let gatt_scanner_callbacks_dispatcher = GattScannerCallbacksDispatcher { |
| dispatch: Box::new(move |cb| { |
| let tx_clone = tx_clone.clone(); |
| topstack::get_runtime().spawn(async move { |
| let _ = tx_clone.send(Message::LeScanner(cb)).await; |
| }); |
| }), |
| }; |
| |
| let tx_clone = tx.clone(); |
| let gatt_scanner_inband_callbacks_dispatcher = GattScannerInbandCallbacksDispatcher { |
| dispatch: Box::new(move |cb| { |
| let tx_clone = tx_clone.clone(); |
| topstack::get_runtime().spawn(async move { |
| let _ = tx_clone.send(Message::LeScannerInband(cb)).await; |
| }); |
| }), |
| }; |
| |
| let tx_clone = tx.clone(); |
| let gatt_adv_inband_callbacks_dispatcher = GattAdvInbandCallbacksDispatcher { |
| dispatch: Box::new(move |cb| { |
| let tx_clone = tx_clone.clone(); |
| topstack::get_runtime().spawn(async move { |
| let _ = tx_clone.send(Message::LeAdvInband(cb)).await; |
| }); |
| }), |
| }; |
| |
| let tx_clone = tx.clone(); |
| let gatt_adv_callbacks_dispatcher = GattAdvCallbacksDispatcher { |
| dispatch: Box::new(move |cb| { |
| let tx_clone = tx_clone.clone(); |
| topstack::get_runtime().spawn(async move { |
| let _ = tx_clone.send(Message::LeAdv(cb)).await; |
| }); |
| }), |
| }; |
| |
| self.gatt.as_ref().unwrap().lock().unwrap().initialize( |
| gatt_client_callbacks_dispatcher, |
| gatt_server_callbacks_dispatcher, |
| gatt_scanner_callbacks_dispatcher, |
| gatt_scanner_inband_callbacks_dispatcher, |
| gatt_adv_inband_callbacks_dispatcher, |
| gatt_adv_callbacks_dispatcher, |
| ); |
| |
| let gatt = self.gatt.clone(); |
| let gatt_async = self.gatt_async.clone(); |
| tokio::spawn(async move { |
| gatt_async.lock().await.gatt = gatt; |
| }); |
| } |
| |
| /// Remove a scanner callback and unregisters all scanners associated with that callback. |
| pub fn remove_scanner_callback(&mut self, callback_id: u32) -> bool { |
| let affected_scanner_ids: Vec<u8> = self |
| .scanners |
| .lock() |
| .unwrap() |
| .iter() |
| .filter(|(_uuid, scanner)| scanner.callback_id == callback_id) |
| .filter_map(|(_uuid, scanner)| { |
| if let Some(scanner_id) = scanner.scanner_id { |
| Some(scanner_id) |
| } else { |
| None |
| } |
| }) |
| .collect(); |
| |
| // All scanners associated with the callback must be also unregistered. |
| for scanner_id in affected_scanner_ids { |
| self.unregister_scanner(scanner_id); |
| } |
| |
| self.scanner_callbacks.remove_callback(callback_id) |
| } |
| |
| /// Enters suspend mode for LE Scan. |
| /// |
| /// This "pauses" all operations managed by this module to prepare for system suspend. A |
| /// callback is triggered to let clients know that this module is in suspend mode and some |
| /// subsequent API calls will be blocked in this mode. |
| pub fn scan_enter_suspend(&mut self) { |
| // TODO(b/224603540): Implement |
| log::error!("TODO - scan_enter_suspend"); |
| } |
| |
| /// Exits suspend mode for LE Scan. |
| /// |
| /// To be called after system resume/wake up. This "unpauses" the operations that were "paused" |
| /// due to suspend. A callback is triggered to let clients when this module has exited suspend |
| /// mode. |
| pub fn scan_exit_suspend(&mut self) { |
| // TODO(b/224603540): Implement |
| log::error!("TODO - scan_exit_suspend"); |
| } |
| |
| fn find_scanner_by_id<'a>( |
| scanners: &'a mut MutexGuard<ScannersMap>, |
| scanner_id: u8, |
| ) -> Option<&'a mut ScannerInfo> { |
| scanners.values_mut().find(|scanner| scanner.scanner_id == Some(scanner_id)) |
| } |
| |
| /// Remove an advertiser callback and unregisters all advertising sets associated with that callback. |
| pub fn remove_adv_callback(&mut self, callback_id: u32) -> bool { |
| self.advertisers |
| .remove_callback(callback_id, &mut self.gatt.as_ref().unwrap().lock().unwrap()) |
| } |
| |
| fn get_adapter_name(&self) -> String { |
| if let Some(adapter) = &self.adapter { |
| adapter.lock().unwrap().get_name() |
| } else { |
| String::new() |
| } |
| } |
| |
| pub fn remove_client_callback(&mut self, callback_id: u32) { |
| // Unregister client if client id exists. |
| if let Some(client) = self.context_map.get_by_callback_id(callback_id) { |
| if let Some(id) = client.id { |
| self.unregister_client(id); |
| } |
| } |
| |
| // Always remove callback. |
| self.context_map.remove_callback(callback_id); |
| } |
| |
| /// Enters suspend mode for LE advertising. |
| pub fn advertising_enter_suspend(&mut self) { |
| self.advertisers.set_suspend_mode(SuspendMode::Suspending); |
| |
| let mut pausing_cnt = 0; |
| for s in self.advertisers.enabled_sets_mut() { |
| s.set_paused(true); |
| self.gatt.as_ref().unwrap().lock().unwrap().advertiser.enable( |
| s.adv_id(), |
| false, |
| s.adv_timeout(), |
| s.adv_events(), |
| ); |
| pausing_cnt += 1; |
| } |
| |
| if pausing_cnt == 0 { |
| self.advertisers.set_suspend_mode(SuspendMode::Suspended); |
| } |
| } |
| |
| /// Exits suspend mode for LE advertising. |
| pub fn advertising_exit_suspend(&mut self) { |
| for s in self.advertisers.paused_sets_mut() { |
| s.set_paused(false); |
| self.gatt.as_ref().unwrap().lock().unwrap().advertiser.enable( |
| s.adv_id(), |
| true, |
| s.adv_timeout(), |
| s.adv_events(), |
| ); |
| } |
| |
| self.advertisers.set_suspend_mode(SuspendMode::Normal); |
| } |
| } |
| |
| #[derive(Debug, FromPrimitive, ToPrimitive)] |
| #[repr(u8)] |
| /// Status of WriteCharacteristic methods. |
| pub enum GattWriteRequestStatus { |
| Success = 0, |
| Fail = 1, |
| Busy = 2, |
| } |
| |
| // This structure keeps track of the lifecycle of a scanner. |
| struct ScannerInfo { |
| // The callback to which events about this scanner needs to be sent to. |
| // Another purpose of keeping track of the callback id is that when a callback is disconnected |
| // or unregistered we need to also unregister all scanners associated with that callback to |
| // prevent dangling unowned scanners. |
| callback_id: u32, |
| // If the scanner is registered successfully, this contains the scanner id, otherwise None. |
| scanner_id: Option<u8>, |
| // If one of scanners is active, we scan. |
| is_active: bool, |
| // Scan filter. |
| filter: Option<ScanFilter>, |
| // Adv monitor handle, if exists. |
| monitor_handle: Option<u8>, |
| } |
| |
| impl ScannerInfo { |
| fn new(callback_id: u32) -> Self { |
| Self { callback_id, scanner_id: None, is_active: false, filter: None, monitor_handle: None } |
| } |
| } |
| |
| impl Into<MsftAdvMonitorPattern> for &ScanFilterPattern { |
| fn into(self) -> MsftAdvMonitorPattern { |
| MsftAdvMonitorPattern { |
| ad_type: self.ad_type, |
| start_byte: self.start_position, |
| pattern: self.content.clone(), |
| } |
| } |
| } |
| |
| impl Into<Vec<MsftAdvMonitorPattern>> for &ScanFilterCondition { |
| fn into(self) -> Vec<MsftAdvMonitorPattern> { |
| match self { |
| ScanFilterCondition::Patterns(patterns) => { |
| patterns.iter().map(|pattern| pattern.into()).collect() |
| } |
| _ => vec![], |
| } |
| } |
| } |
| |
| impl Into<MsftAdvMonitor> for &ScanFilter { |
| fn into(self) -> MsftAdvMonitor { |
| MsftAdvMonitor { |
| rssi_high_threshold: self.rssi_high_threshold.try_into().unwrap(), |
| rssi_low_threshold: self.rssi_low_threshold.try_into().unwrap(), |
| rssi_low_timeout: self.rssi_low_timeout.try_into().unwrap(), |
| rssi_sampling_period: self.rssi_sampling_period.try_into().unwrap(), |
| patterns: (&self.condition).into(), |
| } |
| } |
| } |
| |
| impl IBluetoothGatt for BluetoothGatt { |
| fn is_msft_supported(&self) -> bool { |
| // TODO(b/244505567): Wire the real capability from lower layer. |
| false |
| } |
| |
| fn register_scanner_callback(&mut self, callback: Box<dyn IScannerCallback + Send>) -> u32 { |
| self.scanner_callbacks.add_callback(callback) |
| } |
| |
| fn unregister_scanner_callback(&mut self, callback_id: u32) -> bool { |
| self.remove_scanner_callback(callback_id) |
| } |
| |
| fn register_scanner(&mut self, callback_id: u32) -> Uuid128Bit { |
| let mut bytes: [u8; 16] = [0; 16]; |
| self.small_rng.fill_bytes(&mut bytes); |
| let uuid = Uuid::from(bytes); |
| |
| self.scanners.lock().unwrap().insert(uuid, ScannerInfo::new(callback_id)); |
| |
| // libbluetooth's register_scanner takes a UUID of the scanning application. This UUID does |
| // not correspond to higher level concept of "application" so we use random UUID that |
| // functions as a unique identifier of the scanner. |
| self.gatt.as_ref().unwrap().lock().unwrap().scanner.register_scanner(uuid); |
| |
| uuid.uu |
| } |
| |
| fn unregister_scanner(&mut self, scanner_id: u8) -> bool { |
| self.gatt.as_ref().unwrap().lock().unwrap().scanner.unregister(scanner_id); |
| |
| // The unregistered scanner must also be stopped. |
| self.stop_scan(scanner_id); |
| |
| self.scanners |
| .lock() |
| .unwrap() |
| .retain(|_uuid, scanner| scanner.scanner_id != Some(scanner_id)); |
| |
| true |
| } |
| |
| fn start_scan( |
| &mut self, |
| scanner_id: u8, |
| _settings: ScanSettings, |
| filter: Option<ScanFilter>, |
| ) -> BtStatus { |
| // Multiplexing scanners happens at this layer. The implementations of start_scan |
| // and stop_scan maintains the state of all registered scanners and based on the states |
| // update the scanning and/or filter states of libbluetooth. |
| // TODO(b/217274432): Honor settings and filters. |
| { |
| let mut scanners_lock = self.scanners.lock().unwrap(); |
| |
| if let Some(scanner) = Self::find_scanner_by_id(&mut scanners_lock, scanner_id) { |
| scanner.is_active = true; |
| scanner.filter = filter.clone(); |
| } else { |
| log::warn!("Scanner {} not found", scanner_id); |
| return BtStatus::Fail; |
| } |
| } |
| |
| let gatt_async = self.gatt_async.clone(); |
| tokio::spawn(async move { |
| // The three operations below (monitor add, monitor enable, update scan) happen one |
| // after another, and cannot be interleaved with other GATT async operations. |
| // So acquire the GATT async lock in the beginning of this block and will be released |
| // at the end of this block. |
| // TODO(b/217274432): Consider not using async model but instead add actions when |
| // handling callbacks. |
| let mut gatt_async = gatt_async.lock().await; |
| |
| if let Some(filter) = filter { |
| let monitor_handle = match gatt_async.msft_adv_monitor_add((&filter).into()).await { |
| Ok((handle, 0)) => handle, |
| _ => { |
| log::error!("Error adding advertisement monitor"); |
| return; |
| } |
| }; |
| |
| log::debug!("Added adv monitor handle = {}", monitor_handle); |
| |
| if !gatt_async |
| .msft_adv_monitor_enable(true) |
| .await |
| .map_or(false, |status| status == 0) |
| { |
| log::error!("Error enabling Advertisement Monitor"); |
| } |
| } |
| |
| gatt_async.update_scan().await; |
| }); |
| |
| BtStatus::Success |
| } |
| |
| fn stop_scan(&mut self, scanner_id: u8) -> BtStatus { |
| let monitor_handle = { |
| let mut scanners_lock = self.scanners.lock().unwrap(); |
| |
| if let Some(scanner) = Self::find_scanner_by_id(&mut scanners_lock, scanner_id) { |
| scanner.is_active = false; |
| scanner.monitor_handle |
| } else { |
| log::warn!("Scanner {} not found", scanner_id); |
| // Clients can assume success of the removal since the scanner does not exist. |
| return BtStatus::Success; |
| } |
| }; |
| |
| let gatt_async = self.gatt_async.clone(); |
| tokio::spawn(async move { |
| // The two operations below (monitor remove, update scan) happen one after another, and |
| // cannot be interleaved with other GATT async operations. |
| // So acquire the GATT async lock in the beginning of this block and will be released |
| // at the end of this block. |
| let mut gatt_async = gatt_async.lock().await; |
| |
| if let Some(handle) = monitor_handle { |
| let _res = gatt_async.msft_adv_monitor_remove(handle).await; |
| } |
| |
| gatt_async.update_scan().await; |
| }); |
| |
| BtStatus::Success |
| } |
| |
| fn get_scan_suspend_mode(&self) -> SuspendMode { |
| // TODO(b/224603540): Implement. |
| return SuspendMode::Normal; |
| } |
| |
| // Advertising |
| |
| fn register_advertiser_callback( |
| &mut self, |
| callback: Box<dyn IAdvertisingSetCallback + Send>, |
| ) -> u32 { |
| self.advertisers.add_callback(callback) |
| } |
| |
| fn unregister_advertiser_callback(&mut self, callback_id: u32) { |
| self.advertisers |
| .remove_callback(callback_id, &mut self.gatt.as_ref().unwrap().lock().unwrap()); |
| } |
| |
| fn start_advertising_set( |
| &mut self, |
| parameters: AdvertisingSetParameters, |
| advertise_data: AdvertiseData, |
| scan_response: Option<AdvertiseData>, |
| periodic_parameters: Option<PeriodicAdvertisingParameters>, |
| periodic_data: Option<AdvertiseData>, |
| duration: i32, |
| max_ext_adv_events: i32, |
| callback_id: u32, |
| ) -> i32 { |
| if self.advertisers.suspend_mode() != SuspendMode::Normal { |
| return INVALID_REG_ID; |
| } |
| |
| let device_name = self.get_adapter_name(); |
| let params = parameters.into(); |
| let adv_bytes = advertise_data.make_with(&device_name); |
| let scan_bytes = |
| if let Some(d) = scan_response { d.make_with(&device_name) } else { Vec::<u8>::new() }; |
| let periodic_params = if let Some(p) = periodic_parameters { |
| p.into() |
| } else { |
| bt_topshim::profiles::gatt::PeriodicAdvertisingParameters::default() |
| }; |
| let periodic_bytes = |
| if let Some(d) = periodic_data { d.make_with(&device_name) } else { Vec::<u8>::new() }; |
| let adv_timeout = clamp(duration, 0, 0xffff) as u16; |
| let adv_events = clamp(max_ext_adv_events, 0, 0xff) as u8; |
| |
| let s = AdvertisingSetInfo::new(callback_id, adv_timeout, adv_events); |
| let reg_id = s.reg_id(); |
| self.advertisers.add(s); |
| |
| self.gatt.as_ref().unwrap().lock().unwrap().advertiser.start_advertising_set( |
| reg_id, |
| params, |
| adv_bytes, |
| scan_bytes, |
| periodic_params, |
| periodic_bytes, |
| adv_timeout, |
| adv_events, |
| ); |
| reg_id |
| } |
| |
| fn stop_advertising_set(&mut self, advertiser_id: i32) { |
| if self.advertisers.suspend_mode() != SuspendMode::Normal { |
| return; |
| } |
| |
| let s = self.advertisers.get_by_advertiser_id(advertiser_id); |
| if None == s { |
| return; |
| } |
| let s = s.unwrap().clone(); |
| |
| self.gatt.as_ref().unwrap().lock().unwrap().advertiser.unregister(s.adv_id()); |
| |
| if let Some(cb) = self.advertisers.get_callback(&s) { |
| cb.on_advertising_set_stopped(advertiser_id); |
| } |
| self.advertisers.remove_by_advertiser_id(advertiser_id); |
| } |
| |
| fn get_own_address(&mut self, advertiser_id: i32) { |
| if self.advertisers.suspend_mode() != SuspendMode::Normal { |
| return; |
| } |
| |
| if let Some(s) = self.advertisers.get_by_advertiser_id(advertiser_id) { |
| self.gatt.as_ref().unwrap().lock().unwrap().advertiser.get_own_address(s.adv_id()); |
| } |
| } |
| |
| fn enable_advertising_set( |
| &mut self, |
| advertiser_id: i32, |
| enable: bool, |
| duration: i32, |
| max_ext_adv_events: i32, |
| ) { |
| if self.advertisers.suspend_mode() != SuspendMode::Normal { |
| return; |
| } |
| |
| let adv_timeout = clamp(duration, 0, 0xffff) as u16; |
| let adv_events = clamp(max_ext_adv_events, 0, 0xff) as u8; |
| |
| if let Some(s) = self.advertisers.get_by_advertiser_id(advertiser_id) { |
| self.gatt.as_ref().unwrap().lock().unwrap().advertiser.enable( |
| s.adv_id(), |
| enable, |
| adv_timeout, |
| adv_events, |
| ); |
| } |
| } |
| |
| fn set_advertising_data(&mut self, advertiser_id: i32, data: AdvertiseData) { |
| if self.advertisers.suspend_mode() != SuspendMode::Normal { |
| return; |
| } |
| |
| let device_name = self.get_adapter_name(); |
| let bytes = data.make_with(&device_name); |
| |
| if let Some(s) = self.advertisers.get_by_advertiser_id(advertiser_id) { |
| self.gatt.as_ref().unwrap().lock().unwrap().advertiser.set_data( |
| s.adv_id(), |
| false, |
| bytes, |
| ); |
| } |
| } |
| |
| fn set_scan_response_data(&mut self, advertiser_id: i32, data: AdvertiseData) { |
| if self.advertisers.suspend_mode() != SuspendMode::Normal { |
| return; |
| } |
| |
| let device_name = self.get_adapter_name(); |
| let bytes = data.make_with(&device_name); |
| |
| if let Some(s) = self.advertisers.get_by_advertiser_id(advertiser_id) { |
| self.gatt.as_ref().unwrap().lock().unwrap().advertiser.set_data( |
| s.adv_id(), |
| true, |
| bytes, |
| ); |
| } |
| } |
| |
| fn set_advertising_parameters( |
| &mut self, |
| advertiser_id: i32, |
| parameters: AdvertisingSetParameters, |
| ) { |
| if self.advertisers.suspend_mode() != SuspendMode::Normal { |
| return; |
| } |
| |
| let params = parameters.into(); |
| |
| if let Some(s) = self.advertisers.get_by_advertiser_id(advertiser_id) { |
| let was_enabled = s.is_enabled(); |
| if was_enabled { |
| self.gatt.as_ref().unwrap().lock().unwrap().advertiser.enable( |
| s.adv_id(), |
| false, |
| s.adv_timeout(), |
| s.adv_events(), |
| ); |
| } |
| self.gatt |
| .as_ref() |
| .unwrap() |
| .lock() |
| .unwrap() |
| .advertiser |
| .set_parameters(s.adv_id(), params); |
| if was_enabled { |
| self.gatt.as_ref().unwrap().lock().unwrap().advertiser.enable( |
| s.adv_id(), |
| true, |
| s.adv_timeout(), |
| s.adv_events(), |
| ); |
| } |
| } |
| } |
| |
| fn set_periodic_advertising_parameters( |
| &mut self, |
| advertiser_id: i32, |
| parameters: PeriodicAdvertisingParameters, |
| ) { |
| if self.advertisers.suspend_mode() != SuspendMode::Normal { |
| return; |
| } |
| |
| let params = parameters.into(); |
| |
| if let Some(s) = self.advertisers.get_by_advertiser_id(advertiser_id) { |
| self.gatt |
| .as_ref() |
| .unwrap() |
| .lock() |
| .unwrap() |
| .advertiser |
| .set_periodic_advertising_parameters(s.adv_id(), params); |
| } |
| } |
| |
| fn set_periodic_advertising_data(&mut self, advertiser_id: i32, data: AdvertiseData) { |
| if self.advertisers.suspend_mode() != SuspendMode::Normal { |
| return; |
| } |
| |
| let device_name = self.get_adapter_name(); |
| let bytes = data.make_with(&device_name); |
| |
| if let Some(s) = self.advertisers.get_by_advertiser_id(advertiser_id) { |
| self.gatt |
| .as_ref() |
| .unwrap() |
| .lock() |
| .unwrap() |
| .advertiser |
| .set_periodic_advertising_data(s.adv_id(), bytes); |
| } |
| } |
| |
| fn set_periodic_advertising_enable( |
| &mut self, |
| advertiser_id: i32, |
| enable: bool, |
| include_adi: bool, |
| ) { |
| if self.advertisers.suspend_mode() != SuspendMode::Normal { |
| return; |
| } |
| if let Some(s) = self.advertisers.get_by_advertiser_id(advertiser_id) { |
| self.gatt.as_ref().unwrap().lock().unwrap().advertiser.set_periodic_advertising_enable( |
| s.adv_id(), |
| enable, |
| include_adi, |
| ); |
| } |
| } |
| |
| fn register_client( |
| &mut self, |
| app_uuid: String, |
| callback: Box<dyn IBluetoothGattCallback + Send>, |
| eatt_support: bool, |
| ) { |
| let uuid = match UuidHelper::parse_string(&app_uuid) { |
| Some(id) => id, |
| None => { |
| log::info!("Uuid is malformed: {}", app_uuid); |
| return; |
| } |
| }; |
| self.context_map.add(&uuid.uu, callback); |
| self.gatt |
| .as_ref() |
| .expect("GATT has not been initialized") |
| .lock() |
| .unwrap() |
| .client |
| .register_client(&uuid, eatt_support); |
| } |
| |
| fn unregister_client(&mut self, client_id: i32) { |
| self.context_map.remove(client_id); |
| self.gatt.as_ref().unwrap().lock().unwrap().client.unregister_client(client_id); |
| } |
| |
| fn client_connect( |
| &self, |
| client_id: i32, |
| addr: String, |
| is_direct: bool, |
| transport: BtTransport, |
| opportunistic: bool, |
| phy: LePhy, |
| ) { |
| let address = match RawAddress::from_string(addr.clone()) { |
| None => return, |
| Some(addr) => addr, |
| }; |
| |
| self.gatt.as_ref().unwrap().lock().unwrap().client.connect( |
| client_id, |
| &address, |
| is_direct, |
| transport.into(), |
| opportunistic, |
| phy.into(), |
| ); |
| } |
| |
| fn client_disconnect(&self, client_id: i32, address: String) { |
| let conn_id = self.context_map.get_conn_id_from_address(client_id, &address); |
| if conn_id.is_none() { |
| return; |
| } |
| |
| self.gatt.as_ref().unwrap().lock().unwrap().client.disconnect( |
| client_id, |
| &RawAddress::from_string(address).unwrap(), |
| conn_id.unwrap(), |
| ); |
| } |
| |
| fn refresh_device(&self, client_id: i32, addr: String) { |
| self.gatt |
| .as_ref() |
| .unwrap() |
| .lock() |
| .unwrap() |
| .client |
| .refresh(client_id, &RawAddress::from_string(addr).unwrap()); |
| } |
| |
| fn discover_services(&self, client_id: i32, addr: String) { |
| let conn_id = self.context_map.get_conn_id_from_address(client_id, &addr); |
| if conn_id.is_none() { |
| return; |
| } |
| |
| self.gatt.as_ref().unwrap().lock().unwrap().client.search_service(conn_id.unwrap(), None); |
| } |
| |
| fn discover_service_by_uuid(&self, client_id: i32, addr: String, uuid: String) { |
| let conn_id = self.context_map.get_conn_id_from_address(client_id, &addr); |
| if conn_id.is_none() { |
| return; |
| } |
| |
| let uuid = UuidHelper::parse_string(uuid); |
| if uuid.is_none() { |
| return; |
| } |
| |
| self.gatt.as_ref().unwrap().lock().unwrap().client.search_service(conn_id.unwrap(), uuid); |
| } |
| |
| fn read_characteristic(&self, client_id: i32, addr: String, handle: i32, auth_req: i32) { |
| let conn_id = self.context_map.get_conn_id_from_address(client_id, &addr); |
| if conn_id.is_none() { |
| return; |
| } |
| |
| // TODO(b/200065274): Perform check on restricted handles. |
| |
| self.gatt.as_ref().unwrap().lock().unwrap().client.read_characteristic( |
| conn_id.unwrap(), |
| handle as u16, |
| auth_req, |
| ); |
| } |
| |
| fn read_using_characteristic_uuid( |
| &self, |
| client_id: i32, |
| addr: String, |
| uuid: String, |
| start_handle: i32, |
| end_handle: i32, |
| auth_req: i32, |
| ) { |
| let conn_id = self.context_map.get_conn_id_from_address(client_id, &addr); |
| if conn_id.is_none() { |
| return; |
| } |
| |
| let uuid = UuidHelper::parse_string(uuid); |
| if uuid.is_none() { |
| return; |
| } |
| |
| // TODO(b/200065274): Perform check on restricted handles. |
| |
| self.gatt.as_ref().unwrap().lock().unwrap().client.read_using_characteristic_uuid( |
| conn_id.unwrap(), |
| &uuid.unwrap(), |
| start_handle as u16, |
| end_handle as u16, |
| auth_req, |
| ); |
| } |
| |
| fn write_characteristic( |
| &self, |
| client_id: i32, |
| addr: String, |
| handle: i32, |
| mut write_type: GattWriteType, |
| auth_req: i32, |
| value: Vec<u8>, |
| ) -> GattWriteRequestStatus { |
| let conn_id = self.context_map.get_conn_id_from_address(client_id, &addr); |
| if conn_id.is_none() { |
| return GattWriteRequestStatus::Fail; |
| } |
| |
| if self.reliable_queue.contains(&addr) { |
| write_type = GattWriteType::WritePrepare; |
| } |
| |
| // TODO(b/200065274): Perform check on restricted handles. |
| |
| // TODO(b/200070162): Handle concurrent write characteristic. |
| |
| self.gatt.as_ref().unwrap().lock().unwrap().client.write_characteristic( |
| conn_id.unwrap(), |
| handle as u16, |
| write_type.to_i32().unwrap(), |
| auth_req, |
| &value, |
| ); |
| |
| return GattWriteRequestStatus::Success; |
| } |
| |
| fn read_descriptor(&self, client_id: i32, addr: String, handle: i32, auth_req: i32) { |
| let conn_id = self.context_map.get_conn_id_from_address(client_id, &addr); |
| if conn_id.is_none() { |
| return; |
| } |
| |
| // TODO(b/200065274): Perform check on restricted handles. |
| |
| self.gatt.as_ref().unwrap().lock().unwrap().client.read_descriptor( |
| conn_id.unwrap(), |
| handle as u16, |
| auth_req, |
| ); |
| } |
| |
| fn write_descriptor( |
| &self, |
| client_id: i32, |
| addr: String, |
| handle: i32, |
| auth_req: i32, |
| value: Vec<u8>, |
| ) { |
| let conn_id = self.context_map.get_conn_id_from_address(client_id, &addr); |
| if conn_id.is_none() { |
| return; |
| } |
| |
| // TODO(b/200065274): Perform check on restricted handles. |
| |
| self.gatt.as_ref().unwrap().lock().unwrap().client.write_descriptor( |
| conn_id.unwrap(), |
| handle as u16, |
| auth_req, |
| &value, |
| ); |
| } |
| |
| fn register_for_notification(&self, client_id: i32, addr: String, handle: i32, enable: bool) { |
| let conn_id = self.context_map.get_conn_id_from_address(client_id, &addr); |
| if conn_id.is_none() { |
| return; |
| } |
| |
| // TODO(b/200065274): Perform check on restricted handles. |
| |
| if enable { |
| self.gatt.as_ref().unwrap().lock().unwrap().client.register_for_notification( |
| client_id, |
| &RawAddress::from_string(addr).unwrap(), |
| handle as u16, |
| ); |
| } else { |
| self.gatt.as_ref().unwrap().lock().unwrap().client.deregister_for_notification( |
| client_id, |
| &RawAddress::from_string(addr).unwrap(), |
| handle as u16, |
| ); |
| } |
| } |
| |
| fn begin_reliable_write(&mut self, _client_id: i32, addr: String) { |
| self.reliable_queue.insert(addr); |
| } |
| |
| fn end_reliable_write(&mut self, client_id: i32, addr: String, execute: bool) { |
| self.reliable_queue.remove(&addr); |
| |
| let conn_id = self.context_map.get_conn_id_from_address(client_id, &addr); |
| if conn_id.is_none() { |
| return; |
| } |
| |
| self.gatt |
| .as_ref() |
| .unwrap() |
| .lock() |
| .unwrap() |
| .client |
| .execute_write(conn_id.unwrap(), if execute { 1 } else { 0 }); |
| } |
| |
| fn read_remote_rssi(&self, client_id: i32, addr: String) { |
| self.gatt |
| .as_ref() |
| .unwrap() |
| .lock() |
| .unwrap() |
| .client |
| .read_remote_rssi(client_id, &RawAddress::from_string(addr).unwrap()); |
| } |
| |
| fn configure_mtu(&self, client_id: i32, addr: String, mtu: i32) { |
| let conn_id = self.context_map.get_conn_id_from_address(client_id, &addr); |
| if conn_id.is_none() { |
| return; |
| } |
| |
| self.gatt.as_ref().unwrap().lock().unwrap().client.configure_mtu(conn_id.unwrap(), mtu); |
| } |
| |
| fn connection_parameter_update( |
| &self, |
| _client_id: i32, |
| addr: String, |
| min_interval: i32, |
| max_interval: i32, |
| latency: i32, |
| timeout: i32, |
| min_ce_len: u16, |
| max_ce_len: u16, |
| ) { |
| self.gatt.as_ref().unwrap().lock().unwrap().client.conn_parameter_update( |
| &RawAddress::from_string(addr).unwrap(), |
| min_interval, |
| max_interval, |
| latency, |
| timeout, |
| min_ce_len, |
| max_ce_len, |
| ); |
| } |
| |
| fn client_set_preferred_phy( |
| &self, |
| client_id: i32, |
| address: String, |
| tx_phy: LePhy, |
| rx_phy: LePhy, |
| phy_options: i32, |
| ) { |
| let conn_id = self.context_map.get_conn_id_from_address(client_id, &address); |
| if conn_id.is_none() { |
| return; |
| } |
| |
| self.gatt.as_ref().unwrap().lock().unwrap().client.set_preferred_phy( |
| &RawAddress::from_string(address).unwrap(), |
| tx_phy.to_u8().unwrap(), |
| rx_phy.to_u8().unwrap(), |
| phy_options as u16, |
| ); |
| } |
| |
| fn client_read_phy(&mut self, client_id: i32, addr: String) { |
| let address = match RawAddress::from_string(addr.clone()) { |
| None => return, |
| Some(addr) => addr, |
| }; |
| |
| self.gatt.as_ref().unwrap().lock().unwrap().client.read_phy(client_id, &address); |
| } |
| } |
| |
| #[btif_callbacks_dispatcher(dispatch_gatt_client_callbacks, GattClientCallbacks)] |
| pub(crate) trait BtifGattClientCallbacks { |
| #[btif_callback(RegisterClient)] |
| fn register_client_cb(&mut self, status: GattStatus, client_id: i32, app_uuid: Uuid); |
| |
| #[btif_callback(Connect)] |
| fn connect_cb(&mut self, conn_id: i32, status: GattStatus, client_id: i32, addr: RawAddress); |
| |
| #[btif_callback(Disconnect)] |
| fn disconnect_cb(&mut self, conn_id: i32, status: GattStatus, client_id: i32, addr: RawAddress); |
| |
| #[btif_callback(SearchComplete)] |
| fn search_complete_cb(&mut self, conn_id: i32, status: GattStatus); |
| |
| #[btif_callback(RegisterForNotification)] |
| fn register_for_notification_cb( |
| &mut self, |
| conn_id: i32, |
| registered: i32, |
| status: GattStatus, |
| handle: u16, |
| ); |
| |
| #[btif_callback(Notify)] |
| fn notify_cb(&mut self, conn_id: i32, data: BtGattNotifyParams); |
| |
| #[btif_callback(ReadCharacteristic)] |
| fn read_characteristic_cb(&mut self, conn_id: i32, status: GattStatus, data: BtGattReadParams); |
| |
| #[btif_callback(WriteCharacteristic)] |
| fn write_characteristic_cb( |
| &mut self, |
| conn_id: i32, |
| status: GattStatus, |
| handle: u16, |
| len: u16, |
| value: *const u8, |
| ); |
| |
| #[btif_callback(ReadDescriptor)] |
| fn read_descriptor_cb(&mut self, conn_id: i32, status: GattStatus, data: BtGattReadParams); |
| |
| #[btif_callback(WriteDescriptor)] |
| fn write_descriptor_cb( |
| &mut self, |
| conn_id: i32, |
| status: GattStatus, |
| handle: u16, |
| len: u16, |
| value: *const u8, |
| ); |
| |
| #[btif_callback(ExecuteWrite)] |
| fn execute_write_cb(&mut self, conn_id: i32, status: GattStatus); |
| |
| #[btif_callback(ReadRemoteRssi)] |
| fn read_remote_rssi_cb( |
| &mut self, |
| client_id: i32, |
| addr: RawAddress, |
| rssi: i32, |
| status: GattStatus, |
| ); |
| |
| #[btif_callback(ConfigureMtu)] |
| fn configure_mtu_cb(&mut self, conn_id: i32, status: GattStatus, mtu: i32); |
| |
| #[btif_callback(Congestion)] |
| fn congestion_cb(&mut self, conn_id: i32, congested: bool); |
| |
| #[btif_callback(GetGattDb)] |
| fn get_gatt_db_cb(&mut self, conn_id: i32, elements: Vec<BtGattDbElement>, count: i32); |
| |
| #[btif_callback(PhyUpdated)] |
| fn phy_updated_cb(&mut self, conn_id: i32, tx_phy: u8, rx_phy: u8, status: GattStatus); |
| |
| #[btif_callback(ConnUpdated)] |
| fn conn_updated_cb( |
| &mut self, |
| conn_id: i32, |
| interval: u16, |
| latency: u16, |
| timeout: u16, |
| status: GattStatus, |
| ); |
| |
| #[btif_callback(ServiceChanged)] |
| fn service_changed_cb(&mut self, conn_id: i32); |
| |
| #[btif_callback(ReadPhy)] |
| fn read_phy_cb( |
| &mut self, |
| client_id: i32, |
| addr: RawAddress, |
| tx_phy: u8, |
| rx_phy: u8, |
| status: GattStatus, |
| ); |
| } |
| |
| impl BtifGattClientCallbacks for BluetoothGatt { |
| fn register_client_cb(&mut self, status: GattStatus, client_id: i32, app_uuid: Uuid) { |
| self.context_map.set_client_id(&app_uuid.uu, client_id); |
| |
| let client = self.context_map.get_by_uuid(&app_uuid.uu); |
| match client { |
| Some(c) => { |
| let cbid = c.cbid; |
| self.context_map.get_callback_from_callback_id(cbid).and_then( |
| |cb: &mut GattClientCallback| { |
| cb.on_client_registered(status, client_id); |
| Some(()) |
| }, |
| ); |
| } |
| None => { |
| warn!("Warning: Client not registered for UUID {}", app_uuid); |
| } |
| } |
| } |
| |
| fn connect_cb(&mut self, conn_id: i32, status: GattStatus, client_id: i32, addr: RawAddress) { |
| if status == GattStatus::Success { |
| self.context_map.add_connection(client_id, conn_id, &addr.to_string()); |
| } |
| |
| let client = self.context_map.get_by_client_id(client_id); |
| if let Some(c) = client { |
| let cbid = c.cbid; |
| self.context_map.get_callback_from_callback_id(cbid).and_then( |
| |cb: &mut GattClientCallback| { |
| cb.on_client_connection_state( |
| status, |
| client_id, |
| status == GattStatus::Success, |
| addr.to_string(), |
| ); |
| Some(()) |
| }, |
| ); |
| } |
| } |
| |
| fn disconnect_cb( |
| &mut self, |
| conn_id: i32, |
| status: GattStatus, |
| client_id: i32, |
| addr: RawAddress, |
| ) { |
| let client = self.context_map.get_by_client_id(client_id); |
| if let Some(c) = client { |
| let cbid = c.cbid; |
| self.context_map.get_callback_from_callback_id(cbid).and_then( |
| |cb: &mut GattClientCallback| { |
| cb.on_client_connection_state(status, client_id, false, addr.to_string()); |
| Some(()) |
| }, |
| ); |
| } |
| self.context_map.remove_connection(client_id, conn_id); |
| } |
| |
| fn search_complete_cb(&mut self, conn_id: i32, _status: GattStatus) { |
| // Gatt DB is ready! |
| self.gatt.as_ref().unwrap().lock().unwrap().client.get_gatt_db(conn_id); |
| } |
| |
| fn register_for_notification_cb( |
| &mut self, |
| _conn_id: i32, |
| _registered: i32, |
| _status: GattStatus, |
| _handle: u16, |
| ) { |
| // No-op. |
| } |
| |
| fn notify_cb(&mut self, conn_id: i32, data: BtGattNotifyParams) { |
| let client = self.context_map.get_client_by_conn_id(conn_id); |
| if let Some(c) = client { |
| let cbid = c.cbid; |
| self.context_map.get_callback_from_callback_id(cbid).and_then( |
| |cb: &mut GattClientCallback| { |
| cb.on_notify( |
| data.bda.to_string(), |
| data.handle as i32, |
| data.value[0..data.len as usize].to_vec(), |
| ); |
| Some(()) |
| }, |
| ); |
| } |
| } |
| |
| fn read_characteristic_cb(&mut self, conn_id: i32, status: GattStatus, data: BtGattReadParams) { |
| let address = self.context_map.get_address_by_conn_id(conn_id); |
| if address.is_none() { |
| return; |
| } |
| |
| let client = self.context_map.get_client_by_conn_id(conn_id); |
| if let Some(c) = client { |
| let cbid = c.cbid; |
| self.context_map.get_callback_from_callback_id(cbid).and_then( |
| |cb: &mut GattClientCallback| { |
| cb.on_characteristic_read( |
| address.unwrap().to_string(), |
| status, |
| data.handle as i32, |
| data.value.value[0..data.value.len as usize].to_vec(), |
| ); |
| Some(()) |
| }, |
| ); |
| } |
| } |
| |
| fn write_characteristic_cb( |
| &mut self, |
| conn_id: i32, |
| mut status: GattStatus, |
| handle: u16, |
| _len: u16, |
| _value: *const u8, |
| ) { |
| let address = self.context_map.get_address_by_conn_id(conn_id); |
| if address.is_none() { |
| return; |
| } |
| |
| // TODO(b/200070162): Design how to handle concurrent write characteristic to the same |
| // peer. |
| |
| let client = self.context_map.get_client_by_conn_id_mut(conn_id); |
| if client.is_none() { |
| return; |
| } |
| |
| match (client, address) { |
| (Some(c), Some(addr)) => { |
| if c.is_congested { |
| if status == GattStatus::Congested { |
| status = GattStatus::Success; |
| } |
| |
| c.congestion_queue.push((addr.to_string(), status, handle as i32)); |
| return; |
| } |
| |
| let cbid = c.cbid; |
| self.context_map.get_callback_from_callback_id(cbid).and_then( |
| |cb: &mut GattClientCallback| { |
| cb.on_characteristic_write(addr.to_string(), status, handle as i32); |
| Some(()) |
| }, |
| ); |
| } |
| _ => (), |
| }; |
| } |
| |
| fn read_descriptor_cb(&mut self, conn_id: i32, status: GattStatus, data: BtGattReadParams) { |
| let address = self.context_map.get_address_by_conn_id(conn_id); |
| if address.is_none() { |
| return; |
| } |
| |
| let client = self.context_map.get_client_by_conn_id(conn_id); |
| if let Some(c) = client { |
| let cbid = c.cbid; |
| self.context_map.get_callback_from_callback_id(cbid).and_then( |
| |cb: &mut GattClientCallback| { |
| cb.on_descriptor_read( |
| address.unwrap().to_string(), |
| status, |
| data.handle as i32, |
| data.value.value[0..data.value.len as usize].to_vec(), |
| ); |
| Some(()) |
| }, |
| ); |
| } |
| } |
| |
| fn write_descriptor_cb( |
| &mut self, |
| conn_id: i32, |
| status: GattStatus, |
| handle: u16, |
| _len: u16, |
| _value: *const u8, |
| ) { |
| let address = self.context_map.get_address_by_conn_id(conn_id); |
| if address.is_none() { |
| return; |
| } |
| |
| let client = self.context_map.get_client_by_conn_id(conn_id); |
| if let Some(c) = client { |
| let cbid = c.cbid; |
| self.context_map.get_callback_from_callback_id(cbid).and_then( |
| |cb: &mut GattClientCallback| { |
| cb.on_descriptor_write(address.unwrap().to_string(), status, handle as i32); |
| Some(()) |
| }, |
| ); |
| } |
| } |
| |
| fn execute_write_cb(&mut self, conn_id: i32, status: GattStatus) { |
| let address = self.context_map.get_address_by_conn_id(conn_id); |
| if address.is_none() { |
| return; |
| } |
| |
| let client = self.context_map.get_client_by_conn_id(conn_id); |
| if let Some(c) = client { |
| let cbid = c.cbid; |
| self.context_map.get_callback_from_callback_id(cbid).and_then( |
| |cb: &mut GattClientCallback| { |
| cb.on_execute_write(address.unwrap().to_string(), status); |
| Some(()) |
| }, |
| ); |
| } |
| } |
| |
| fn read_remote_rssi_cb( |
| &mut self, |
| client_id: i32, |
| addr: RawAddress, |
| rssi: i32, |
| status: GattStatus, |
| ) { |
| let client = self.context_map.get_by_client_id(client_id); |
| if let Some(c) = client { |
| let cbid = c.cbid; |
| self.context_map.get_callback_from_callback_id(cbid).and_then( |
| |cb: &mut GattClientCallback| { |
| cb.on_read_remote_rssi(addr.to_string(), rssi, status); |
| Some(()) |
| }, |
| ); |
| } |
| } |
| |
| fn configure_mtu_cb(&mut self, conn_id: i32, status: GattStatus, mtu: i32) { |
| let client = self.context_map.get_client_by_conn_id(conn_id); |
| let addr = self.context_map.get_address_by_conn_id(conn_id); |
| |
| match (client, addr) { |
| (Some(c), Some(addr)) => { |
| let cbid = c.cbid; |
| self.context_map.get_callback_from_callback_id(cbid).and_then( |
| |cb: &mut GattClientCallback| { |
| cb.on_configure_mtu(addr, mtu, status); |
| Some(()) |
| }, |
| ); |
| } |
| _ => (), |
| }; |
| } |
| |
| fn congestion_cb(&mut self, conn_id: i32, congested: bool) { |
| if let Some(mut client) = self.context_map.get_client_by_conn_id_mut(conn_id) { |
| client.is_congested = congested; |
| if !client.is_congested { |
| let cbid = client.cbid; |
| let mut congestion_queue: Vec<(String, GattStatus, i32)> = vec![]; |
| client.congestion_queue.retain(|v| { |
| congestion_queue.push(v.clone()); |
| false |
| }); |
| |
| self.context_map.get_callback_from_callback_id(cbid).and_then( |
| |cb: &mut GattClientCallback| { |
| for callback in congestion_queue.iter() { |
| cb.on_characteristic_write(callback.0.clone(), callback.1, callback.2); |
| } |
| Some(()) |
| }, |
| ); |
| } |
| } |
| } |
| |
| fn get_gatt_db_cb(&mut self, conn_id: i32, elements: Vec<BtGattDbElement>, _count: i32) { |
| let address = self.context_map.get_address_by_conn_id(conn_id); |
| if address.is_none() { |
| return; |
| } |
| |
| let client = self.context_map.get_client_by_conn_id(conn_id); |
| if client.is_none() { |
| return; |
| } |
| |
| let mut db_out: Vec<BluetoothGattService> = vec![]; |
| |
| for elem in elements { |
| match GattDbElementType::from_u32(elem.type_).unwrap() { |
| GattDbElementType::PrimaryService | GattDbElementType::SecondaryService => { |
| db_out.push(BluetoothGattService::new( |
| elem.uuid.uu, |
| elem.id as i32, |
| elem.type_ as i32, |
| )); |
| // TODO(b/200065274): Mark restricted services. |
| } |
| |
| GattDbElementType::Characteristic => { |
| match db_out.last_mut() { |
| Some(s) => s.characteristics.push(BluetoothGattCharacteristic::new( |
| elem.uuid.uu, |
| elem.id as i32, |
| elem.properties as i32, |
| 0, |
| )), |
| None => { |
| // TODO(b/193685325): Log error. |
| } |
| } |
| // TODO(b/200065274): Mark restricted characteristics. |
| } |
| |
| GattDbElementType::Descriptor => { |
| match db_out.last_mut() { |
| Some(s) => match s.characteristics.last_mut() { |
| Some(c) => c.descriptors.push(BluetoothGattDescriptor::new( |
| elem.uuid.uu, |
| elem.id as i32, |
| 0, |
| )), |
| None => { |
| // TODO(b/193685325): Log error. |
| } |
| }, |
| None => { |
| // TODO(b/193685325): Log error. |
| } |
| } |
| // TODO(b/200065274): Mark restricted descriptors. |
| } |
| |
| GattDbElementType::IncludedService => { |
| match db_out.last_mut() { |
| Some(s) => { |
| s.included_services.push(BluetoothGattService::new( |
| elem.uuid.uu, |
| elem.id as i32, |
| elem.type_ as i32, |
| )); |
| } |
| None => { |
| // TODO(b/193685325): Log error. |
| } |
| } |
| } |
| } |
| } |
| |
| match (client, address) { |
| (Some(c), Some(addr)) => { |
| let cbid = c.cbid; |
| self.context_map.get_callback_from_callback_id(cbid).and_then( |
| |cb: &mut GattClientCallback| { |
| cb.on_search_complete(addr.to_string(), db_out, GattStatus::Success); |
| Some(()) |
| }, |
| ); |
| } |
| _ => (), |
| }; |
| } |
| |
| fn phy_updated_cb(&mut self, conn_id: i32, tx_phy: u8, rx_phy: u8, status: GattStatus) { |
| let client = self.context_map.get_client_by_conn_id(conn_id); |
| if client.is_none() { |
| return; |
| } |
| |
| let address = self.context_map.get_address_by_conn_id(conn_id); |
| if address.is_none() { |
| return; |
| } |
| match (client, address) { |
| (Some(c), Some(addr)) => { |
| let cbid = c.cbid; |
| self.context_map.get_callback_from_callback_id(cbid).and_then( |
| |cb: &mut GattClientCallback| { |
| cb.on_phy_update( |
| addr, |
| LePhy::from_u8(tx_phy).unwrap(), |
| LePhy::from_u8(rx_phy).unwrap(), |
| status, |
| ); |
| Some(()) |
| }, |
| ); |
| } |
| _ => (), |
| }; |
| } |
| |
| fn read_phy_cb( |
| &mut self, |
| client_id: i32, |
| addr: RawAddress, |
| tx_phy: u8, |
| rx_phy: u8, |
| status: GattStatus, |
| ) { |
| let client = self.context_map.get_by_client_id(client_id); |
| if client.is_none() { |
| return; |
| } |
| |
| if let Some(c) = client { |
| let cbid = c.cbid; |
| self.context_map.get_callback_from_callback_id(cbid).and_then( |
| |cb: &mut GattClientCallback| { |
| cb.on_phy_read( |
| addr.to_string(), |
| LePhy::from_u8(tx_phy).unwrap(), |
| LePhy::from_u8(rx_phy).unwrap(), |
| status, |
| ); |
| Some(()) |
| }, |
| ); |
| } |
| } |
| |
| fn conn_updated_cb( |
| &mut self, |
| conn_id: i32, |
| interval: u16, |
| latency: u16, |
| timeout: u16, |
| status: GattStatus, |
| ) { |
| let client = self.context_map.get_client_by_conn_id(conn_id); |
| if client.is_none() { |
| return; |
| } |
| |
| let address = self.context_map.get_address_by_conn_id(conn_id); |
| if address.is_none() { |
| return; |
| } |
| |
| match (client, address) { |
| (Some(c), Some(addr)) => { |
| let cbid = c.cbid; |
| self.context_map.get_callback_from_callback_id(cbid).and_then( |
| |cb: &mut GattClientCallback| { |
| cb.on_connection_updated( |
| addr, |
| interval as i32, |
| latency as i32, |
| timeout as i32, |
| status, |
| ); |
| Some(()) |
| }, |
| ); |
| } |
| _ => (), |
| }; |
| } |
| |
| fn service_changed_cb(&mut self, conn_id: i32) { |
| let address = self.context_map.get_address_by_conn_id(conn_id); |
| if address.is_none() { |
| return; |
| } |
| |
| let client = self.context_map.get_client_by_conn_id(conn_id); |
| if client.is_none() { |
| return; |
| } |
| |
| match (client, address) { |
| (Some(c), Some(addr)) => { |
| let cbid = c.cbid; |
| self.context_map.get_callback_from_callback_id(cbid).and_then( |
| |cb: &mut GattClientCallback| { |
| cb.on_service_changed(addr); |
| Some(()) |
| }, |
| ); |
| } |
| _ => (), |
| }; |
| } |
| } |
| |
| #[btif_callbacks_dispatcher(dispatch_le_scanner_callbacks, GattScannerCallbacks)] |
| pub(crate) trait BtifGattScannerCallbacks { |
| #[btif_callback(OnScannerRegistered)] |
| fn on_scanner_registered(&mut self, uuid: Uuid, scanner_id: u8, status: GattStatus); |
| |
| #[btif_callback(OnScanResult)] |
| fn on_scan_result( |
| &mut self, |
| event_type: u16, |
| addr_type: u8, |
| bda: RawAddress, |
| primary_phy: u8, |
| secondary_phy: u8, |
| advertising_sid: u8, |
| tx_power: i8, |
| rssi: i8, |
| periodic_adv_int: u16, |
| adv_data: Vec<u8>, |
| ); |
| } |
| |
| #[btif_callbacks_dispatcher(dispatch_le_scanner_inband_callbacks, GattScannerInbandCallbacks)] |
| pub(crate) trait BtifGattScannerInbandCallbacks { |
| #[btif_callback(RegisterCallback)] |
| fn inband_register_callback(&mut self, app_uuid: Uuid, scanner_id: u8, btm_status: u8); |
| |
| #[btif_callback(StatusCallback)] |
| fn inband_status_callback(&mut self, scanner_id: u8, btm_status: u8); |
| |
| #[btif_callback(EnableCallback)] |
| fn inband_enable_callback(&mut self, action: u8, btm_status: u8); |
| |
| #[btif_callback(FilterParamSetupCallback)] |
| fn inband_filter_param_setup_callback( |
| &mut self, |
| scanner_id: u8, |
| available_space: u8, |
| action_type: u8, |
| btm_status: u8, |
| ); |
| |
| #[btif_callback(FilterConfigCallback)] |
| fn inband_filter_config_callback( |
| &mut self, |
| filter_index: u8, |
| filter_type: u8, |
| available_space: u8, |
| action: u8, |
| btm_status: u8, |
| ); |
| |
| #[btif_callback(MsftAdvMonitorAddCallback)] |
| fn inband_msft_adv_monitor_add_callback( |
| &mut self, |
| call_id: u32, |
| monitor_handle: u8, |
| status: u8, |
| ); |
| |
| #[btif_callback(MsftAdvMonitorRemoveCallback)] |
| fn inband_msft_adv_monitor_remove_callback(&mut self, call_id: u32, status: u8); |
| |
| #[btif_callback(MsftAdvMonitorEnableCallback)] |
| fn inband_msft_adv_monitor_enable_callback(&mut self, call_id: u32, status: u8); |
| |
| #[btif_callback(StartSyncCallback)] |
| fn inband_start_sync_callback( |
| &mut self, |
| status: u8, |
| sync_handle: u16, |
| advertising_sid: u8, |
| address_type: u8, |
| address: RawAddress, |
| phy: u8, |
| interval: u16, |
| ); |
| |
| #[btif_callback(SyncReportCallback)] |
| fn inband_sync_report_callback( |
| &mut self, |
| sync_handle: u16, |
| tx_power: i8, |
| rssi: i8, |
| status: u8, |
| data: Vec<u8>, |
| ); |
| |
| #[btif_callback(SyncLostCallback)] |
| fn inband_sync_lost_callback(&mut self, sync_handle: u16); |
| |
| #[btif_callback(SyncTransferCallback)] |
| fn inband_sync_transfer_callback(&mut self, status: u8, address: RawAddress); |
| } |
| |
| impl BtifGattScannerInbandCallbacks for BluetoothGatt { |
| fn inband_register_callback(&mut self, app_uuid: Uuid, scanner_id: u8, btm_status: u8) { |
| log::debug!( |
| "Callback received: {:#?}", |
| GattScannerInbandCallbacks::RegisterCallback(app_uuid, scanner_id, btm_status) |
| ); |
| } |
| |
| fn inband_status_callback(&mut self, scanner_id: u8, btm_status: u8) { |
| log::debug!( |
| "Callback received: {:#?}", |
| GattScannerInbandCallbacks::StatusCallback(scanner_id, btm_status) |
| ); |
| } |
| |
| fn inband_enable_callback(&mut self, action: u8, btm_status: u8) { |
| log::debug!( |
| "Callback received: {:#?}", |
| GattScannerInbandCallbacks::EnableCallback(action, btm_status) |
| ); |
| } |
| |
| fn inband_filter_param_setup_callback( |
| &mut self, |
| scanner_id: u8, |
| available_space: u8, |
| action_type: u8, |
| btm_status: u8, |
| ) { |
| log::debug!( |
| "Callback received: {:#?}", |
| GattScannerInbandCallbacks::FilterParamSetupCallback( |
| scanner_id, |
| available_space, |
| action_type, |
| btm_status |
| ) |
| ); |
| } |
| |
| fn inband_filter_config_callback( |
| &mut self, |
| filter_index: u8, |
| filter_type: u8, |
| available_space: u8, |
| action: u8, |
| btm_status: u8, |
| ) { |
| log::debug!( |
| "Callback received: {:#?}", |
| GattScannerInbandCallbacks::FilterConfigCallback( |
| filter_index, |
| filter_type, |
| available_space, |
| action, |
| btm_status, |
| ) |
| ); |
| } |
| |
| fn inband_msft_adv_monitor_add_callback( |
| &mut self, |
| call_id: u32, |
| monitor_handle: u8, |
| status: u8, |
| ) { |
| (self.adv_mon_add_cb_sender.lock().unwrap())(call_id, (monitor_handle, status)); |
| } |
| |
| fn inband_msft_adv_monitor_remove_callback(&mut self, call_id: u32, status: u8) { |
| (self.adv_mon_remove_cb_sender.lock().unwrap())(call_id, status); |
| } |
| |
| fn inband_msft_adv_monitor_enable_callback(&mut self, call_id: u32, status: u8) { |
| (self.adv_mon_enable_cb_sender.lock().unwrap())(call_id, status); |
| } |
| |
| fn inband_start_sync_callback( |
| &mut self, |
| status: u8, |
| sync_handle: u16, |
| advertising_sid: u8, |
| address_type: u8, |
| address: RawAddress, |
| phy: u8, |
| interval: u16, |
| ) { |
| log::debug!( |
| "Callback received: {:#?}", |
| GattScannerInbandCallbacks::StartSyncCallback( |
| status, |
| sync_handle, |
| advertising_sid, |
| address_type, |
| address, |
| phy, |
| interval, |
| ) |
| ); |
| } |
| |
| fn inband_sync_report_callback( |
| &mut self, |
| sync_handle: u16, |
| tx_power: i8, |
| rssi: i8, |
| status: u8, |
| data: Vec<u8>, |
| ) { |
| log::debug!( |
| "Callback received: {:#?}", |
| GattScannerInbandCallbacks::SyncReportCallback( |
| sync_handle, |
| tx_power, |
| rssi, |
| status, |
| data |
| ) |
| ); |
| } |
| |
| fn inband_sync_lost_callback(&mut self, sync_handle: u16) { |
| log::debug!( |
| "Callback received: {:#?}", |
| GattScannerInbandCallbacks::SyncLostCallback(sync_handle,) |
| ); |
| } |
| |
| fn inband_sync_transfer_callback(&mut self, status: u8, address: RawAddress) { |
| log::debug!( |
| "Callback received: {:#?}", |
| GattScannerInbandCallbacks::SyncTransferCallback(status, address) |
| ); |
| } |
| } |
| |
| impl BtifGattScannerCallbacks for BluetoothGatt { |
| fn on_scanner_registered(&mut self, uuid: Uuid, scanner_id: u8, status: GattStatus) { |
| log::debug!( |
| "on_scanner_registered UUID = {}, scanner_id = {}, status = {}", |
| uuid, |
| scanner_id, |
| status |
| ); |
| |
| if status != GattStatus::Success { |
| log::error!("Error registering scanner UUID {}", uuid); |
| self.scanners.lock().unwrap().remove(&uuid); |
| return; |
| } |
| |
| let mut scanners_lock = self.scanners.lock().unwrap(); |
| let scanner_info = scanners_lock.get_mut(&uuid); |
| |
| if let Some(info) = scanner_info { |
| info.scanner_id = Some(scanner_id); |
| let callback = self.scanner_callbacks.get_by_id(info.callback_id); |
| if let Some(cb) = callback { |
| cb.on_scanner_registered(uuid.uu, scanner_id, status); |
| } else { |
| log::warn!("There is no callback for scanner UUID {}", uuid); |
| } |
| } else { |
| log::warn!( |
| "Scanner registered callback for non-existent scanner info, UUID = {}", |
| uuid |
| ); |
| } |
| } |
| |
| fn on_scan_result( |
| &mut self, |
| event_type: u16, |
| addr_type: u8, |
| address: RawAddress, |
| primary_phy: u8, |
| secondary_phy: u8, |
| advertising_sid: u8, |
| tx_power: i8, |
| rssi: i8, |
| periodic_adv_int: u16, |
| adv_data: Vec<u8>, |
| ) { |
| self.scanner_callbacks.for_all_callbacks(|callback| { |
| callback.on_scan_result(ScanResult { |
| name: adv_parser::extract_name(adv_data.as_slice()), |
| address: address.to_string(), |
| addr_type, |
| event_type, |
| primary_phy, |
| secondary_phy, |
| advertising_sid, |
| tx_power, |
| rssi, |
| periodic_adv_int, |
| flags: adv_parser::extract_flags(adv_data.as_slice()), |
| service_uuids: adv_parser::extract_service_uuids(adv_data.as_slice()), |
| service_data: adv_parser::extract_service_data(adv_data.as_slice()), |
| manufacturer_data: adv_parser::extract_manufacturer_data(adv_data.as_slice()), |
| adv_data: adv_data.clone(), |
| }); |
| }); |
| } |
| } |
| |
| #[btif_callbacks_dispatcher(dispatch_le_adv_callbacks, GattAdvCallbacks)] |
| pub(crate) trait BtifGattAdvCallbacks { |
| #[btif_callback(OnAdvertisingSetStarted)] |
| fn on_advertising_set_started( |
| &mut self, |
| reg_id: i32, |
| advertiser_id: u8, |
| tx_power: i8, |
| status: AdvertisingStatus, |
| ); |
| |
| #[btif_callback(OnAdvertisingEnabled)] |
| fn on_advertising_enabled(&mut self, adv_id: u8, enabled: bool, status: AdvertisingStatus); |
| |
| #[btif_callback(OnAdvertisingDataSet)] |
| fn on_advertising_data_set(&mut self, adv_id: u8, status: AdvertisingStatus); |
| |
| #[btif_callback(OnScanResponseDataSet)] |
| fn on_scan_response_data_set(&mut self, adv_id: u8, status: AdvertisingStatus); |
| |
| #[btif_callback(OnAdvertisingParametersUpdated)] |
| fn on_advertising_parameters_updated( |
| &mut self, |
| adv_id: u8, |
| tx_power: i8, |
| status: AdvertisingStatus, |
| ); |
| |
| #[btif_callback(OnPeriodicAdvertisingParametersUpdated)] |
| fn on_periodic_advertising_parameters_updated(&mut self, adv_id: u8, status: AdvertisingStatus); |
| |
| #[btif_callback(OnPeriodicAdvertisingDataSet)] |
| fn on_periodic_advertising_data_set(&mut self, adv_id: u8, status: AdvertisingStatus); |
| |
| #[btif_callback(OnPeriodicAdvertisingEnabled)] |
| fn on_periodic_advertising_enabled( |
| &mut self, |
| adv_id: u8, |
| enabled: bool, |
| status: AdvertisingStatus, |
| ); |
| |
| #[btif_callback(OnOwnAddressRead)] |
| fn on_own_address_read(&mut self, adv_id: u8, addr_type: u8, address: RawAddress); |
| } |
| |
| impl BtifGattAdvCallbacks for BluetoothGatt { |
| fn on_advertising_set_started( |
| &mut self, |
| reg_id: i32, |
| advertiser_id: u8, |
| tx_power: i8, |
| status: AdvertisingStatus, |
| ) { |
| debug!( |
| "on_advertising_set_started(): reg_id = {}, advertiser_id = {}, tx_power = {}, status = {:?}", |
| reg_id, advertiser_id, tx_power, status |
| ); |
| |
| if let Some(s) = self.advertisers.get_mut_by_reg_id(reg_id) { |
| s.set_adv_id(Some(advertiser_id.into())); |
| s.set_enabled(status == AdvertisingStatus::Success); |
| } else { |
| return; |
| } |
| let s = self.advertisers.get_mut_by_reg_id(reg_id).unwrap().clone(); |
| |
| if let Some(cb) = self.advertisers.get_callback(&s) { |
| cb.on_advertising_set_started(reg_id, advertiser_id.into(), tx_power.into(), status); |
| } |
| |
| if status != AdvertisingStatus::Success { |
| warn!( |
| "on_advertising_set_started(): failed! reg_id = {}, status = {:?}", |
| reg_id, status |
| ); |
| self.advertisers.remove_by_reg_id(reg_id); |
| } |
| } |
| |
| fn on_advertising_enabled(&mut self, adv_id: u8, enabled: bool, status: AdvertisingStatus) { |
| debug!( |
| "on_advertising_enabled(): adv_id = {}, enabled = {}, status = {:?}", |
| adv_id, enabled, status |
| ); |
| |
| let advertiser_id: i32 = adv_id.into(); |
| |
| if let Some(s) = self.advertisers.get_mut_by_advertiser_id(advertiser_id) { |
| s.set_enabled(enabled); |
| } else { |
| return; |
| } |
| |
| let s = self.advertisers.get_by_advertiser_id(advertiser_id).unwrap().clone(); |
| if let Some(cb) = self.advertisers.get_callback(&s) { |
| cb.on_advertising_enabled(advertiser_id, enabled, status); |
| } |
| |
| if self.advertisers.suspend_mode() == SuspendMode::Suspending { |
| if self.advertisers.enabled_sets().count() == 0 { |
| self.advertisers.set_suspend_mode(SuspendMode::Suspended); |
| } |
| } |
| } |
| |
| fn on_advertising_data_set(&mut self, adv_id: u8, status: AdvertisingStatus) { |
| debug!("on_advertising_data_set(): adv_id = {}, status = {:?}", adv_id, status); |
| |
| let advertiser_id: i32 = adv_id.into(); |
| if None == self.advertisers.get_by_advertiser_id(advertiser_id) { |
| return; |
| } |
| let s = self.advertisers.get_by_advertiser_id(advertiser_id).unwrap().clone(); |
| |
| if let Some(cb) = self.advertisers.get_callback(&s) { |
| cb.on_advertising_data_set(advertiser_id, status); |
| } |
| } |
| |
| fn on_scan_response_data_set(&mut self, adv_id: u8, status: AdvertisingStatus) { |
| debug!("on_scan_response_data_set(): adv_id = {}, status = {:?}", adv_id, status); |
| |
| let advertiser_id: i32 = adv_id.into(); |
| if None == self.advertisers.get_by_advertiser_id(advertiser_id) { |
| return; |
| } |
| let s = self.advertisers.get_by_advertiser_id(advertiser_id).unwrap().clone(); |
| |
| if let Some(cb) = self.advertisers.get_callback(&s) { |
| cb.on_scan_response_data_set(advertiser_id, status); |
| } |
| } |
| |
| fn on_advertising_parameters_updated( |
| &mut self, |
| adv_id: u8, |
| tx_power: i8, |
| status: AdvertisingStatus, |
| ) { |
| debug!( |
| "on_advertising_parameters_updated(): adv_id = {}, tx_power = {}, status = {:?}", |
| adv_id, tx_power, status |
| ); |
| |
| let advertiser_id: i32 = adv_id.into(); |
| if None == self.advertisers.get_by_advertiser_id(advertiser_id) { |
| return; |
| } |
| let s = self.advertisers.get_by_advertiser_id(advertiser_id).unwrap().clone(); |
| |
| if let Some(cb) = self.advertisers.get_callback(&s) { |
| cb.on_advertising_parameters_updated(advertiser_id, tx_power.into(), status); |
| } |
| } |
| |
| fn on_periodic_advertising_parameters_updated( |
| &mut self, |
| adv_id: u8, |
| status: AdvertisingStatus, |
| ) { |
| debug!( |
| "on_periodic_advertising_parameters_updated(): adv_id = {}, status = {:?}", |
| adv_id, status |
| ); |
| |
| let advertiser_id: i32 = adv_id.into(); |
| if None == self.advertisers.get_by_advertiser_id(advertiser_id) { |
| return; |
| } |
| let s = self.advertisers.get_by_advertiser_id(advertiser_id).unwrap().clone(); |
| |
| if let Some(cb) = self.advertisers.get_callback(&s) { |
| cb.on_periodic_advertising_parameters_updated(advertiser_id, status); |
| } |
| } |
| |
| fn on_periodic_advertising_data_set(&mut self, adv_id: u8, status: AdvertisingStatus) { |
| debug!("on_periodic_advertising_data_set(): adv_id = {}, status = {:?}", adv_id, status); |
| |
| let advertiser_id: i32 = adv_id.into(); |
| if None == self.advertisers.get_by_advertiser_id(advertiser_id) { |
| return; |
| } |
| let s = self.advertisers.get_by_advertiser_id(advertiser_id).unwrap().clone(); |
| |
| if let Some(cb) = self.advertisers.get_callback(&s) { |
| cb.on_periodic_advertising_data_set(advertiser_id, status); |
| } |
| } |
| |
| fn on_periodic_advertising_enabled( |
| &mut self, |
| adv_id: u8, |
| enabled: bool, |
| status: AdvertisingStatus, |
| ) { |
| debug!( |
| "on_periodic_advertising_enabled(): adv_id = {}, enabled = {}, status = {:?}", |
| adv_id, enabled, status |
| ); |
| |
| let advertiser_id: i32 = adv_id.into(); |
| if None == self.advertisers.get_by_advertiser_id(advertiser_id) { |
| return; |
| } |
| let s = self.advertisers.get_by_advertiser_id(advertiser_id).unwrap().clone(); |
| |
| if let Some(cb) = self.advertisers.get_callback(&s) { |
| cb.on_periodic_advertising_enabled(advertiser_id, enabled, status); |
| } |
| } |
| |
| fn on_own_address_read(&mut self, adv_id: u8, addr_type: u8, address: RawAddress) { |
| debug!( |
| "on_own_address_read(): adv_id = {}, addr_type = {}, address = {:?}", |
| adv_id, addr_type, address |
| ); |
| |
| let advertiser_id: i32 = adv_id.into(); |
| if None == self.advertisers.get_by_advertiser_id(advertiser_id) { |
| return; |
| } |
| let s = self.advertisers.get_by_advertiser_id(advertiser_id).unwrap().clone(); |
| |
| if let Some(cb) = self.advertisers.get_callback(&s) { |
| cb.on_own_address_read(advertiser_id, addr_type.into(), address.to_string()); |
| } |
| } |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| struct TestBluetoothGattCallback { |
| id: String, |
| } |
| |
| impl TestBluetoothGattCallback { |
| fn new(id: String) -> TestBluetoothGattCallback { |
| TestBluetoothGattCallback { id } |
| } |
| } |
| |
| impl IBluetoothGattCallback for TestBluetoothGattCallback { |
| fn on_client_registered(&self, _status: GattStatus, _client_id: i32) {} |
| fn on_client_connection_state( |
| &self, |
| _status: GattStatus, |
| _client_id: i32, |
| _connected: bool, |
| _addr: String, |
| ) { |
| } |
| |
| fn on_phy_update( |
| &self, |
| _addr: String, |
| _tx_phy: LePhy, |
| _rx_phy: LePhy, |
| _status: GattStatus, |
| ) { |
| } |
| |
| fn on_phy_read(&self, _addr: String, _tx_phy: LePhy, _rx_phy: LePhy, _status: GattStatus) {} |
| |
| fn on_search_complete( |
| &self, |
| _addr: String, |
| _services: Vec<BluetoothGattService>, |
| _status: GattStatus, |
| ) { |
| } |
| |
| fn on_characteristic_read( |
| &self, |
| _addr: String, |
| _status: GattStatus, |
| _handle: i32, |
| _value: Vec<u8>, |
| ) { |
| } |
| |
| fn on_characteristic_write(&self, _addr: String, _status: GattStatus, _handle: i32) {} |
| |
| fn on_execute_write(&self, _addr: String, _status: GattStatus) {} |
| |
| fn on_descriptor_read( |
| &self, |
| _addr: String, |
| _status: GattStatus, |
| _handle: i32, |
| _value: Vec<u8>, |
| ) { |
| } |
| |
| fn on_descriptor_write(&self, _addr: String, _status: GattStatus, _handle: i32) {} |
| |
| fn on_notify(&self, _addr: String, _handle: i32, _value: Vec<u8>) {} |
| |
| fn on_read_remote_rssi(&self, _addr: String, _rssi: i32, _status: GattStatus) {} |
| |
| fn on_configure_mtu(&self, _addr: String, _mtu: i32, _status: GattStatus) {} |
| |
| fn on_connection_updated( |
| &self, |
| _addr: String, |
| _interval: i32, |
| _latency: i32, |
| _timeout: i32, |
| _status: GattStatus, |
| ) { |
| } |
| |
| fn on_service_changed(&self, _addr: String) {} |
| } |
| |
| impl RPCProxy for TestBluetoothGattCallback { |
| fn get_object_id(&self) -> String { |
| self.id.clone() |
| } |
| } |
| |
| use super::*; |
| |
| #[test] |
| fn test_uuid_from_string() { |
| let uuid = UuidHelper::parse_string("abcdef"); |
| assert!(uuid.is_none()); |
| |
| let uuid = UuidHelper::parse_string("0123456789abcdef0123456789abcdef"); |
| assert!(uuid.is_some()); |
| let expected: [u8; 16] = [ |
| 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, |
| 0xcd, 0xef, |
| ]; |
| assert_eq!(Uuid::from(expected), uuid.unwrap()); |
| } |
| |
| #[test] |
| fn test_context_map_clients() { |
| let (tx, _rx) = crate::Stack::create_channel(); |
| let mut map = ContextMap::new(tx.clone()); |
| |
| // Add client 1. |
| let callback1 = Box::new(TestBluetoothGattCallback::new(String::from("Callback 1"))); |
| let uuid1 = UuidHelper::parse_string("00000000000000000000000000000001").unwrap().uu; |
| map.add(&uuid1, callback1); |
| let found = map.get_by_uuid(&uuid1); |
| assert!(found.is_some()); |
| assert_eq!( |
| "Callback 1", |
| match found { |
| Some(c) => { |
| let cbid = c.cbid; |
| map.callbacks |
| .get_by_id(cbid) |
| .and_then(|cb| Some(cb.get_object_id())) |
| .unwrap_or(String::new()) |
| } |
| None => String::new(), |
| } |
| ); |
| |
| // Add client 2. |
| let callback2 = Box::new(TestBluetoothGattCallback::new(String::from("Callback 2"))); |
| let uuid2 = UuidHelper::parse_string("00000000000000000000000000000002").unwrap().uu; |
| map.add(&uuid2, callback2); |
| let found = map.get_by_uuid(&uuid2); |
| assert!(found.is_some()); |
| assert_eq!( |
| "Callback 2", |
| match found { |
| Some(c) => { |
| let cbid = c.cbid; |
| map.callbacks |
| .get_by_id(cbid) |
| .and_then(|cb| Some(cb.get_object_id())) |
| .unwrap_or(String::new()) |
| } |
| None => String::new(), |
| } |
| ); |
| |
| // Set client ID and get by client ID. |
| map.set_client_id(&uuid1, 3); |
| let found = map.get_by_client_id(3); |
| assert!(found.is_some()); |
| |
| // Remove client 1. |
| map.remove(3); |
| let found = map.get_by_uuid(&uuid1); |
| assert!(found.is_none()); |
| } |
| |
| #[test] |
| fn test_context_map_connections() { |
| let (tx, _rx) = crate::Stack::create_channel(); |
| let mut map = ContextMap::new(tx.clone()); |
| let client_id = 1; |
| |
| map.add_connection(client_id, 3, &String::from("aa:bb:cc:dd:ee:ff")); |
| map.add_connection(client_id, 4, &String::from("11:22:33:44:55:66")); |
| |
| let found = map.get_conn_id_from_address(client_id, &String::from("aa:bb:cc:dd:ee:ff")); |
| assert!(found.is_some()); |
| assert_eq!(3, found.unwrap()); |
| |
| let found = map.get_conn_id_from_address(client_id, &String::from("11:22:33:44:55:66")); |
| assert!(found.is_some()); |
| assert_eq!(4, found.unwrap()); |
| } |
| } |