blob: fb0f61cc90674c5a7c884b202b9f2e1ff1b9f2af [file] [log] [blame]
//! 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, RawAddress, Uuid128Bit};
use bt_topshim::profiles::gatt::{
BtGattDbElement, BtGattNotifyParams, BtGattReadParams, Gatt, GattClientCallbacks,
GattClientCallbacksDispatcher, GattServerCallbacksDispatcher, GattStatus,
};
use bt_topshim::topstack;
use log::{debug, warn};
use num_traits::cast::{FromPrimitive, ToPrimitive};
use std::collections::HashSet;
use std::sync::{Arc, Mutex};
use tokio::sync::mpsc::Sender;
use crate::{Message, RPCProxy};
struct Client {
id: Option<i32>,
uuid: Uuid128Bit,
callback: Box<dyn IBluetoothGattCallback + Send>,
is_congested: bool,
// Queued on_characteristic_write callback.
congestion_queue: Vec<(String, i32, 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.
clients: Vec<Client>,
connections: Vec<Connection>,
}
impl ContextMap {
fn new() -> ContextMap {
ContextMap { 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_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: Box<dyn IBluetoothGattCallback + Send>) {
if self.get_by_uuid(uuid).is_some() {
return;
}
self.clients.push(Client {
id: None,
uuid: uuid.clone(),
callback,
is_congested: false,
congestion_queue: vec![],
});
}
fn remove(&mut self, id: i32) {
self.clients.retain(|client| !(client.id.is_some() && client.id.unwrap() == 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),
}
}
}
/// Defines the GATT API.
pub trait IBluetoothGatt {
fn register_scanner(&self, callback: Box<dyn IScannerCallback + Send>);
fn unregister_scanner(&self, scanner_id: i32);
fn start_scan(&self, scanner_id: i32, settings: ScanSettings, filters: Vec<ScanFilter>);
fn stop_scan(&self, scanner_id: i32);
/// 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: i32,
opportunistic: bool,
phy: i32,
);
/// Disconnects a GATT connection.
fn client_disconnect(&self, client_id: i32, addr: String);
/// 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);
/// 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,
);
}
#[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: i32, client_id: i32);
/// When there is a change in the state of a GATT client connection.
fn on_client_connection_state(
&self,
status: i32,
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: i32);
/// The completion of IBluetoothGatt::read_characteristic.
fn on_characteristic_read(&self, addr: String, status: i32, handle: i32, value: Vec<u8>);
/// The completion of IBluetoothGatt::write_characteristic.
fn on_characteristic_write(&self, addr: String, status: i32, handle: i32);
/// When a reliable write is completed.
fn on_execute_write(&self, addr: String, status: i32);
/// The completion of IBluetoothGatt::read_descriptor.
fn on_descriptor_read(&self, addr: String, status: i32, handle: i32, value: Vec<u8>);
/// The completion of IBluetoothGatt::write_descriptor.
fn on_descriptor_write(&self, addr: String, status: i32, 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: i32);
/// The completion of IBluetoothGatt::configure_mtu.
fn on_configure_mtu(&self, addr: String, mtu: i32, status: i32);
/// When a connection parameter changes.
fn on_connection_updated(
&self,
addr: String,
interval: i32,
latency: i32,
timeout: i32,
status: i32,
);
/// 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`.
pub trait IScannerCallback {
/// When the `register_scanner` request is done.
fn on_scanner_registered(&self, status: i32, scanner_id: i32);
}
#[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)]
#[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(u8)]
/// Represents LE PHY.
pub enum LePhy {
Invalid = 0,
Phy1m = 1,
Phy2m = 2,
PhyCoded = 3,
}
#[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 RSSI configurations for hardware offloaded scanning.
// TODO(b/200066804): This is still a placeholder struct, not yet complete.
#[derive(Debug, Default)]
pub struct RSSISettings {
pub low_threshold: i32,
pub high_threshold: i32,
}
/// Represents scanning configurations to be passed to `IBluetoothGatt::start_scan`.
#[derive(Debug, Default)]
pub struct ScanSettings {
pub interval: i32,
pub window: i32,
pub scan_type: ScanType,
pub rssi_settings: RSSISettings,
}
/// Represents a scan filter to be passed to `IBluetoothGatt::start_scan`.
#[derive(Debug, Default)]
pub struct ScanFilter {}
/// Implementation of the GATT API (IBluetoothGatt).
pub struct BluetoothGatt {
intf: Arc<Mutex<BluetoothInterface>>,
gatt: Option<Gatt>,
context_map: ContextMap,
reliable_queue: HashSet<String>,
}
impl BluetoothGatt {
/// Constructs a new IBluetoothGatt implementation.
pub fn new(intf: Arc<Mutex<BluetoothInterface>>) -> BluetoothGatt {
BluetoothGatt {
intf: intf,
gatt: None,
context_map: ContextMap::new(),
reliable_queue: HashSet::new(),
}
}
pub fn init_profiles(&mut self, tx: Sender<Message>) {
self.gatt = Gatt::new(&self.intf.lock().unwrap());
self.gatt.as_mut().unwrap().initialize(
GattClientCallbacksDispatcher {
dispatch: Box::new(move |cb| {
let tx_clone = tx.clone();
topstack::get_runtime().spawn(async move {
let _ = tx_clone.send(Message::GattClient(cb)).await;
});
}),
},
GattServerCallbacksDispatcher {
dispatch: Box::new(move |cb| {
// TODO(b/193685149): Implement the callbacks
debug!("received Gatt server callback: {:?}", cb);
}),
},
);
}
}
// Temporary util that covers only basic string conversion.
// TODO(b/193685325): Implement more UUID utils by using Uuid from gd/hci/uuid.h with cxx.
fn parse_uuid_string<T: Into<String>>(uuid: T) -> Option<Uuid> {
let uuid = uuid.into();
if uuid.len() != 32 {
return None;
}
let mut raw = [0; 16];
for i in 0..16 {
let byte = u8::from_str_radix(&uuid[i * 2..i * 2 + 2], 16);
if byte.is_err() {
return None;
}
raw[i] = byte.unwrap();
}
Some(Uuid { uu: raw })
}
#[derive(Debug, FromPrimitive, ToPrimitive)]
#[repr(u8)]
/// Status of WriteCharacteristic methods.
pub enum GattWriteRequestStatus {
Success = 0,
Fail = 1,
Busy = 2,
}
impl IBluetoothGatt for BluetoothGatt {
fn register_scanner(&self, _callback: Box<dyn IScannerCallback + Send>) {
// TODO(b/200066804): implement
}
fn unregister_scanner(&self, _scanner_id: i32) {
// TODO(b/200066804): implement
}
fn start_scan(&self, _scanner_id: i32, _settings: ScanSettings, _filters: Vec<ScanFilter>) {
// TODO(b/200066804): implement
}
fn stop_scan(&self, _scanner_id: i32) {
// TODO(b/200066804): implement
}
fn register_client(
&mut self,
app_uuid: String,
callback: Box<dyn IBluetoothGattCallback + Send>,
eatt_support: bool,
) {
let uuid = parse_uuid_string(app_uuid).unwrap();
self.context_map.add(&uuid.uu, callback);
self.gatt.as_ref().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().client.unregister_client(client_id);
}
fn client_connect(
&self,
client_id: i32,
addr: String,
is_direct: bool,
transport: i32,
opportunistic: bool,
phy: i32,
) {
let address = match RawAddress::from_string(addr.clone()) {
None => return,
Some(addr) => addr,
};
self.gatt.as_ref().unwrap().client.connect(
client_id,
&address,
is_direct,
transport,
opportunistic,
phy,
);
}
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().client.disconnect(
client_id,
&RawAddress::from_string(address).unwrap(),
conn_id.unwrap(),
);
}
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().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_mut().unwrap().client.read_phy(client_id, &address);
}
fn refresh_device(&self, client_id: i32, addr: String) {
self.gatt
.as_ref()
.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().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 = parse_uuid_string(uuid);
if uuid.is_none() {
return;
}
self.gatt.as_ref().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().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 = parse_uuid_string(uuid);
if uuid.is_none() {
return;
}
// TODO(b/200065274): Perform check on restricted handles.
self.gatt.as_ref().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().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().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().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().client.register_for_notification(
client_id,
&RawAddress::from_string(addr).unwrap(),
handle as u16,
);
} else {
self.gatt.as_ref().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()
.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()
.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().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().client.conn_parameter_update(
&RawAddress::from_string(addr).unwrap(),
min_interval,
max_interval,
latency,
timeout,
min_ce_len,
max_ce_len,
);
}
}
#[btif_callbacks_dispatcher(BluetoothGatt, dispatch_gatt_client_callbacks, GattClientCallbacks)]
pub(crate) trait BtifGattClientCallbacks {
#[btif_callback(RegisterClient)]
fn register_client_cb(&mut self, status: i32, client_id: i32, app_uuid: Uuid);
#[btif_callback(Connect)]
fn connect_cb(&mut self, conn_id: i32, status: i32, client_id: i32, addr: RawAddress);
#[btif_callback(Disconnect)]
fn disconnect_cb(&mut self, conn_id: i32, status: i32, client_id: i32, addr: RawAddress);
#[btif_callback(SearchComplete)]
fn search_complete_cb(&mut self, conn_id: i32, status: i32);
#[btif_callback(RegisterForNotification)]
fn register_for_notification_cb(
&mut self,
conn_id: i32,
registered: i32,
status: i32,
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: i32, data: BtGattReadParams);
#[btif_callback(WriteCharacteristic)]
fn write_characteristic_cb(&mut self, conn_id: i32, status: i32, handle: u16);
#[btif_callback(ReadDescriptor)]
fn read_descriptor_cb(&mut self, conn_id: i32, status: i32, data: BtGattReadParams);
#[btif_callback(WriteDescriptor)]
fn write_descriptor_cb(&mut self, conn_id: i32, status: i32, handle: u16);
#[btif_callback(ExecuteWrite)]
fn execute_write_cb(&mut self, conn_id: i32, status: i32);
#[btif_callback(ReadRemoteRssi)]
fn read_remote_rssi_cb(&mut self, client_id: i32, addr: RawAddress, rssi: i32, status: i32);
#[btif_callback(ConfigureMtu)]
fn configure_mtu_cb(&mut self, conn_id: i32, status: i32, 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: u8);
#[btif_callback(ConnUpdated)]
fn conn_updated_cb(
&mut self,
conn_id: i32,
interval: u16,
latency: u16,
timeout: u16,
status: u8,
);
#[btif_callback(ServiceChanged)]
fn service_changed_cb(&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: u8);
}
impl BtifGattClientCallbacks for BluetoothGatt {
fn register_client_cb(&mut self, status: i32, 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);
if client.is_none() {
warn!("Warning: Client not registered for UUID {:?}", app_uuid.uu);
return;
}
let callback = &client.unwrap().callback;
callback.on_client_registered(status, client_id);
}
fn connect_cb(&mut self, conn_id: i32, status: i32, client_id: i32, addr: RawAddress) {
if status == 0 {
self.context_map.add_connection(client_id, conn_id, &addr.to_string());
}
let client = self.context_map.get_by_client_id(client_id);
if client.is_none() {
return;
}
client.unwrap().callback.on_client_connection_state(
status,
client_id,
match GattStatus::from_i32(status) {
None => false,
Some(gatt_status) => gatt_status == GattStatus::Success,
},
addr.to_string(),
);
}
fn disconnect_cb(&mut self, conn_id: i32, status: i32, client_id: i32, addr: RawAddress) {
self.context_map.remove_connection(client_id, conn_id);
let client = self.context_map.get_by_client_id(client_id);
if client.is_none() {
return;
}
client.unwrap().callback.on_client_connection_state(
status,
client_id,
match GattStatus::from_i32(status) {
None => false,
Some(gatt_status) => gatt_status == GattStatus::Success,
},
addr.to_string(),
);
}
fn search_complete_cb(&mut self, conn_id: i32, _status: i32) {
// Gatt DB is ready!
self.gatt.as_ref().unwrap().client.get_gatt_db(conn_id);
}
fn register_for_notification_cb(
&mut self,
_conn_id: i32,
_registered: i32,
_status: i32,
_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 client.is_none() {
return;
}
client.unwrap().callback.on_notify(
RawAddress { val: data.bda.address }.to_string(),
data.handle as i32,
data.value[0..data.len as usize].to_vec(),
);
}
fn read_characteristic_cb(&mut self, conn_id: i32, status: i32, 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 client.is_none() {
return;
}
client.unwrap().callback.on_characteristic_read(
address.unwrap().to_string(),
status,
data.handle as i32,
data.value.value[0..data.value.len as usize].to_vec(),
);
}
fn write_characteristic_cb(&mut self, conn_id: i32, mut status: i32, handle: u16) {
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;
}
let client = client.unwrap();
if client.is_congested {
if status == GattStatus::Congested.to_i32().unwrap() {
status = GattStatus::Success.to_i32().unwrap();
}
client.congestion_queue.push((address.unwrap().to_string(), status, handle as i32));
return;
}
client.callback.on_characteristic_write(
address.unwrap().to_string(),
status,
handle as i32,
);
}
fn read_descriptor_cb(&mut self, conn_id: i32, status: i32, 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 client.is_none() {
return;
}
client.unwrap().callback.on_descriptor_read(
address.unwrap().to_string(),
status,
data.handle as i32,
data.value.value[0..data.value.len as usize].to_vec(),
);
}
fn write_descriptor_cb(&mut self, conn_id: i32, status: i32, handle: u16) {
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;
}
client.unwrap().callback.on_descriptor_write(
address.unwrap().to_string(),
status,
handle as i32,
);
}
fn execute_write_cb(&mut self, conn_id: i32, status: 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;
}
client.unwrap().callback.on_execute_write(address.unwrap().to_string(), status);
}
fn read_remote_rssi_cb(&mut self, client_id: i32, addr: RawAddress, rssi: i32, status: i32) {
let client = self.context_map.get_by_client_id(client_id);
if client.is_none() {
return;
}
client.unwrap().callback.on_read_remote_rssi(addr.to_string(), rssi, status);
}
fn configure_mtu_cb(&mut self, conn_id: i32, status: i32, mtu: i32) {
let client = self.context_map.get_client_by_conn_id(conn_id);
if client.is_none() {
return;
}
let addr = self.context_map.get_address_by_conn_id(conn_id);
if addr.is_none() {
return;
}
client.unwrap().callback.on_configure_mtu(addr.unwrap(), mtu, status);
}
fn congestion_cb(&mut self, conn_id: i32, congested: bool) {
let client = self.context_map.get_client_by_conn_id_mut(conn_id);
if client.is_none() {
return;
}
let client = client.unwrap();
client.is_congested = congested;
if !client.is_congested {
for callback in client.congestion_queue.iter() {
client.callback.on_characteristic_write(callback.0.clone(), callback.1, callback.2);
}
client.congestion_queue.clear();
}
}
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.
}
}
}
}
}
client.unwrap().callback.on_search_complete(address.unwrap().to_string(), db_out, 0);
}
fn phy_updated_cb(&mut self, conn_id: i32, tx_phy: u8, rx_phy: u8, status: u8) {
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;
}
client.unwrap().callback.on_phy_update(
address.unwrap(),
LePhy::from_u8(tx_phy).unwrap(),
LePhy::from_u8(rx_phy).unwrap(),
GattStatus::from_u8(status).unwrap(),
);
}
fn read_phy_cb(
&mut self,
client_id: i32,
addr: RawAddress,
tx_phy: u8,
rx_phy: u8,
status: u8,
) {
let client = self.context_map.get_by_client_id(client_id);
if client.is_none() {
return;
}
client.unwrap().callback.on_phy_read(
addr.to_string(),
LePhy::from_u8(tx_phy).unwrap(),
LePhy::from_u8(rx_phy).unwrap(),
GattStatus::from_u8(status).unwrap(),
);
}
fn conn_updated_cb(
&mut self,
conn_id: i32,
interval: u16,
latency: u16,
timeout: u16,
status: u8,
) {
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;
}
client.unwrap().callback.on_connection_updated(
address.unwrap(),
interval as i32,
latency as i32,
timeout as i32,
status as i32,
);
}
fn service_changed_cb(&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;
}
client.unwrap().callback.on_service_changed(address.unwrap());
}
}
#[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: i32, _client_id: i32) {}
fn on_client_connection_state(
&self,
_status: i32,
_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: i32,
) {
}
fn on_characteristic_read(
&self,
_addr: String,
_status: i32,
_handle: i32,
_value: Vec<u8>,
) {
}
fn on_characteristic_write(&self, _addr: String, _status: i32, _handle: i32) {}
fn on_execute_write(&self, _addr: String, _status: i32) {}
fn on_descriptor_read(&self, _addr: String, _status: i32, _handle: i32, _value: Vec<u8>) {}
fn on_descriptor_write(&self, _addr: String, _status: i32, _handle: i32) {}
fn on_notify(&self, _addr: String, _handle: i32, _value: Vec<u8>) {}
fn on_read_remote_rssi(&self, _addr: String, _rssi: i32, _status: i32) {}
fn on_configure_mtu(&self, _addr: String, _mtu: i32, _status: i32) {}
fn on_connection_updated(
&self,
_addr: String,
_interval: i32,
_latency: i32,
_timeout: i32,
_status: i32,
) {
}
fn on_service_changed(&self, _addr: String) {}
}
impl RPCProxy for TestBluetoothGattCallback {
fn register_disconnect(&mut self, _f: Box<dyn Fn(u32) + Send>) -> u32 {
0
}
fn get_object_id(&self) -> String {
self.id.clone()
}
fn unregister(&mut self, _id: u32) -> bool {
false
}
}
use super::*;
#[test]
fn test_uuid_from_string() {
let uuid = parse_uuid_string("abcdef");
assert!(uuid.is_none());
let uuid = parse_uuid_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 { uu: expected }, uuid.unwrap());
}
#[test]
fn test_context_map_clients() {
let mut map = ContextMap::new();
// Add client 1.
let callback1 = Box::new(TestBluetoothGattCallback::new(String::from("Callback 1")));
let uuid1 = parse_uuid_string("00000000000000000000000000000001").unwrap().uu;
map.add(&uuid1, callback1);
let found = map.get_by_uuid(&uuid1);
assert!(found.is_some());
assert_eq!("Callback 1", found.unwrap().callback.get_object_id());
// Add client 2.
let callback2 = Box::new(TestBluetoothGattCallback::new(String::from("Callback 2")));
let uuid2 = parse_uuid_string("00000000000000000000000000000002").unwrap().uu;
map.add(&uuid2, callback2);
let found = map.get_by_uuid(&uuid2);
assert!(found.is_some());
assert_eq!("Callback 2", found.unwrap().callback.get_object_id());
// 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 mut map = ContextMap::new();
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());
}
}