Loading system/rust/src/gatt/callbacks.rs +13 −8 Original line number Diff line number Diff line Loading @@ -11,6 +11,7 @@ use async_trait::async_trait; use crate::packets::{AttAttributeDataChild, AttAttributeDataView, AttErrorCode}; use super::{ ffi::AttributeBackingType, ids::{AttHandle, ConnectionId, TransactionId}, server::IndicationError, }; Loading @@ -18,25 +19,27 @@ use super::{ /// These callbacks are expected to be made available to the GattModule from /// JNI. pub trait GattCallbacks { /// Invoked when a client tries to read a characteristic. Expects a response /// using bluetooth::gatt::send_response(); fn on_server_read_characteristic( /// Invoked when a client tries to read a characteristic/descriptor. Expects /// a response using bluetooth::gatt::send_response(); fn on_server_read( &self, conn_id: ConnectionId, trans_id: TransactionId, handle: AttHandle, attr_type: AttributeBackingType, offset: u32, is_long: bool, ); /// Invoked when a client tries to write a characteristic. Expects a /// response using bluetooth::gatt::send_response(); /// Invoked when a client tries to write a characteristic/descriptor. /// Expects a response using bluetooth::gatt::send_response(); #[allow(clippy::too_many_arguments)] // needed to match the C++ interface fn on_server_write_characteristic( fn on_server_write( &self, conn_id: ConnectionId, trans_id: TransactionId, handle: AttHandle, attr_type: AttributeBackingType, offset: u32, need_response: bool, is_prepare: bool, Loading Loading @@ -64,17 +67,19 @@ pub trait GattDatastore { fn remove_connection(&self, conn_id: ConnectionId); /// Read a characteristic from the specified connection at the given handle. async fn read_characteristic( async fn read( &self, conn_id: ConnectionId, handle: AttHandle, attr_type: AttributeBackingType, ) -> Result<AttAttributeDataChild, AttErrorCode>; /// Write data to a given characteristic on the specified connection. async fn write_characteristic( async fn write( &self, conn_id: ConnectionId, handle: AttHandle, attr_type: AttributeBackingType, data: AttAttributeDataView<'_>, ) -> Result<(), AttErrorCode>; } system/rust/src/gatt/callbacks/callback_transaction_manager.rs +7 −6 Original line number Diff line number Diff line Loading @@ -12,7 +12,7 @@ use crate::{ packets::{AttAttributeDataChild, AttAttributeDataView, AttErrorCode}, }; use super::GattDatastore; use super::{AttributeBackingType, GattDatastore}; struct PendingTransaction { response: oneshot::Sender<Result<AttAttributeDataChild, AttErrorCode>>, Loading Loading @@ -121,15 +121,16 @@ impl GattDatastore for CallbackTransactionManager { assert!(old_conn.is_some(), "Received unexpected connection ID, something has gone wrong") } async fn read_characteristic( async fn read( &self, conn_id: ConnectionId, handle: AttHandle, attr_type: AttributeBackingType, ) -> Result<AttAttributeDataChild, AttErrorCode> { let (trans_id, rx) = self.pending_transactions.borrow_mut().start_new_transaction(conn_id)?; self.callbacks.on_server_read_characteristic(conn_id, trans_id, handle, 0, false); self.callbacks.on_server_read(conn_id, trans_id, handle, attr_type, 0, false); if let Ok(value) = rx.await { value Loading @@ -139,17 +140,17 @@ impl GattDatastore for CallbackTransactionManager { } } async fn write_characteristic( async fn write( &self, conn_id: ConnectionId, handle: AttHandle, attr_type: AttributeBackingType, data: AttAttributeDataView<'_>, ) -> Result<(), AttErrorCode> { let (trans_id, rx) = self.pending_transactions.borrow_mut().start_new_transaction(conn_id)?; self.callbacks .on_server_write_characteristic(conn_id, trans_id, handle, 0, true, false, data); self.callbacks.on_server_write(conn_id, trans_id, handle, attr_type, 0, true, false, data); if let Ok(value) = rx.await { value.map(|_| ()) // the data passed back is irrelevant for write Loading system/rust/src/gatt/ffi.rs +148 −20 Original line number Diff line number Diff line //! FFI interfaces for the GATT module. Some structs are exported so that //! core::init can instantiate and pass them into the main loop. use std::iter::Peekable; use anyhow::{bail, Result}; use bt_common::init_flags::{ always_use_private_gatt_for_debugging_is_enabled, rust_event_loop_is_enabled, Loading @@ -23,8 +25,10 @@ use super::{ channel::AttTransport, ids::{AdvertiserId, AttHandle, ConnectionId, ServerId, TransactionId, TransportIndex}, server::{ att_server_bearer::AttServerBearer, gatt_database::{AttPermissions, GattCharacteristicWithHandle, GattServiceWithHandle}, gatt_database::{ AttPermissions, GattCharacteristicWithHandle, GattDescriptorWithHandle, GattServiceWithHandle, }, IndicationError, }, GattCallbacks, Loading @@ -44,33 +48,49 @@ mod inner { type Uuid = crate::core::uuid::Uuid; } /// The GATT entity backing the value of a user-controlled /// attribute #[derive(Debug)] #[namespace = "bluetooth::gatt"] enum AttributeBackingType { /// A GATT characteristic #[cxx_name = "CHARACTERISTIC"] Characteristic = 0u32, /// A GATT descriptor #[cxx_name = "DESCRIPTOR"] Descriptor = 1u32, } #[namespace = "bluetooth::gatt"] unsafe extern "C++" { include!("src/gatt/ffi/gatt_shim.h"); type AttributeBackingType; /// This contains the callbacks from Rust into C++ JNI needed for GATT type GattServerCallbacks; /// This callback is invoked when reading a characteristic - the client /// This callback is invoked when reading - the client /// must reply using SendResponse #[cxx_name = "OnServerReadCharacteristic"] fn on_server_read_characteristic( #[cxx_name = "OnServerRead"] fn on_server_read( self: &GattServerCallbacks, conn_id: u16, trans_id: u32, attr_handle: u16, attr_type: AttributeBackingType, offset: u32, is_long: bool, ); /// This callback is invoked when writing a characteristic - the client /// This callback is invoked when writing - the client /// must reply using SendResponse #[cxx_name = "OnServerWriteCharacteristic"] fn on_server_write_characteristic( #[cxx_name = "OnServerWrite"] fn on_server_write( self: &GattServerCallbacks, conn_id: u16, trans_id: u32, attr_handle: u16, attr_type: AttributeBackingType, offset: u32, need_response: bool, is_prepare: bool, Loading @@ -80,7 +100,7 @@ mod inner { /// This callback is invoked when an indication has been sent and the /// peer device has confirmed it, or if some error occurred. #[cxx_name = "OnIndicationSentConfirmation"] fn on_indication_sent_confirmation(&self, conn_id: u16, status: i32); fn on_indication_sent_confirmation(self: &GattServerCallbacks, conn_id: u16, status: i32); } /// What action the arbiter should take in response to an incoming packet Loading Loading @@ -160,34 +180,37 @@ mod inner { pub struct GattCallbacksImpl(pub UniquePtr<GattServerCallbacks>); impl GattCallbacks for GattCallbacksImpl { fn on_server_read_characteristic( fn on_server_read( &self, conn_id: ConnectionId, trans_id: TransactionId, handle: AttHandle, attr_type: AttributeBackingType, offset: u32, is_long: bool, ) { self.0 .as_ref() .unwrap() .on_server_read_characteristic(conn_id.0, trans_id.0, handle.0, offset, is_long); .on_server_read(conn_id.0, trans_id.0, handle.0, attr_type, offset, is_long); } fn on_server_write_characteristic( fn on_server_write( &self, conn_id: ConnectionId, trans_id: TransactionId, handle: AttHandle, attr_type: AttributeBackingType, offset: u32, need_response: bool, is_prepare: bool, value: AttAttributeDataView, ) { self.0.as_ref().unwrap().on_server_write_characteristic( self.0.as_ref().unwrap().on_server_write( conn_id.0, trans_id.0, handle.0, attr_type, offset, need_response, is_prepare, Loading Loading @@ -262,11 +285,33 @@ fn close_server(server_id: u8) { }) } fn consume_descriptors<'a>( records: &mut Peekable<impl Iterator<Item = &'a GattRecord>>, ) -> Vec<GattDescriptorWithHandle> { let mut out = vec![]; while let Some(GattRecord { uuid, attribute_handle, permissions, .. }) = records.next_if(|record| record.record_type == GattRecordType::Descriptor) { let mut att_permissions = AttPermissions::empty(); att_permissions.set(AttPermissions::READABLE, permissions & 0x01 != 0); att_permissions.set(AttPermissions::WRITABLE, permissions & 0x10 != 0); out.push(GattDescriptorWithHandle { handle: AttHandle(*attribute_handle), type_: *uuid, permissions: att_permissions, }) } out } fn records_to_service(service_records: &[GattRecord]) -> Result<GattServiceWithHandle> { let mut characteristics = vec![]; let mut service_handle_uuid = None; for record in service_records { let mut service_records = service_records.iter().peekable(); while let Some(record) = service_records.next() { match record.record_type { GattRecordType::PrimaryService => { if service_handle_uuid.is_some() { Loading @@ -274,11 +319,17 @@ fn records_to_service(service_records: &[GattRecord]) -> Result<GattServiceWithH } service_handle_uuid = Some((record.attribute_handle, record.uuid)); } GattRecordType::Characteristic => characteristics.push(GattCharacteristicWithHandle { GattRecordType::Characteristic => { characteristics.push(GattCharacteristicWithHandle { handle: AttHandle(record.attribute_handle), type_: record.uuid, permissions: AttPermissions::from_bits_truncate(record.properties), }), descriptors: consume_descriptors(&mut service_records), }); } GattRecordType::Descriptor => { bail!("Got unexpected descriptor outside of characteristic declaration") } _ => { warn!("ignoring unsupported database entry of type {:?}", record.record_type) } Loading Loading @@ -424,7 +475,10 @@ mod test { const CHARACTERISTIC_HANDLE: AttHandle = AttHandle(2); const CHARACTERISTIC_UUID: Uuid = Uuid::new(0x5678); const ANOTHER_CHARACTERISTIC_HANDLE: AttHandle = AttHandle(3); const DESCRIPTOR_UUID: Uuid = Uuid::new(0x4321); const ANOTHER_DESCRIPTOR_UUID: Uuid = Uuid::new(0x5432); const ANOTHER_CHARACTERISTIC_HANDLE: AttHandle = AttHandle(10); const ANOTHER_CHARACTERISTIC_UUID: Uuid = Uuid::new(0x9ABC); fn make_service_record(uuid: Uuid, handle: AttHandle) -> GattRecord { Loading @@ -449,6 +503,17 @@ mod test { } } fn make_descriptor_record(uuid: Uuid, handle: AttHandle, permissions: u16) -> GattRecord { GattRecord { uuid, record_type: GattRecordType::Descriptor, attribute_handle: handle.0, properties: 0, extended_properties: 0, permissions, } } #[test] fn test_empty_records() { let res = records_to_service(&[]); Loading Loading @@ -546,4 +611,67 @@ mod test { AttPermissions::READABLE | AttPermissions::WRITABLE ); } #[test] fn test_multiple_descriptors() { let service = records_to_service(&[ make_service_record(SERVICE_UUID, AttHandle(1)), make_characteristic_record(CHARACTERISTIC_UUID, AttHandle(2), 0), make_descriptor_record(DESCRIPTOR_UUID, AttHandle(3), 0), make_descriptor_record(ANOTHER_DESCRIPTOR_UUID, AttHandle(4), 0), ]) .unwrap(); assert_eq!(service.characteristics[0].descriptors.len(), 2); assert_eq!(service.characteristics[0].descriptors[0].handle, AttHandle(3)); assert_eq!(service.characteristics[0].descriptors[0].type_, DESCRIPTOR_UUID); assert_eq!(service.characteristics[0].descriptors[1].handle, AttHandle(4)); assert_eq!(service.characteristics[0].descriptors[1].type_, ANOTHER_DESCRIPTOR_UUID); } #[test] fn test_descriptor_permissions() { let service = records_to_service(&[ make_service_record(SERVICE_UUID, AttHandle(1)), make_characteristic_record(CHARACTERISTIC_UUID, AttHandle(2), 0), make_descriptor_record(DESCRIPTOR_UUID, AttHandle(3), 0x01), make_descriptor_record(DESCRIPTOR_UUID, AttHandle(4), 0x10), make_descriptor_record(DESCRIPTOR_UUID, AttHandle(5), 0x11), ]) .unwrap(); assert_eq!(service.characteristics[0].descriptors[0].permissions, AttPermissions::READABLE); assert_eq!(service.characteristics[0].descriptors[1].permissions, AttPermissions::WRITABLE); assert_eq!( service.characteristics[0].descriptors[2].permissions, AttPermissions::READABLE | AttPermissions::WRITABLE ); } #[test] fn test_descriptors_multiple_characteristics() { let service = records_to_service(&[ make_service_record(SERVICE_UUID, AttHandle(1)), make_characteristic_record(CHARACTERISTIC_UUID, AttHandle(2), 0), make_descriptor_record(DESCRIPTOR_UUID, AttHandle(3), 0), make_characteristic_record(CHARACTERISTIC_UUID, AttHandle(4), 0), make_descriptor_record(DESCRIPTOR_UUID, AttHandle(5), 0), ]) .unwrap(); assert_eq!(service.characteristics[0].descriptors.len(), 1); assert_eq!(service.characteristics[0].descriptors[0].handle, AttHandle(3)); assert_eq!(service.characteristics[1].descriptors.len(), 1); assert_eq!(service.characteristics[1].descriptors[0].handle, AttHandle(5)); } #[test] fn test_unexpected_descriptor() { let res = records_to_service(&[ make_service_record(SERVICE_UUID, AttHandle(1)), make_descriptor_record(DESCRIPTOR_UUID, AttHandle(3), 0), ]); assert!(res.is_err()); } } system/rust/src/gatt/ffi/gatt_shim.cc +42 −18 Original line number Diff line number Diff line Loading @@ -50,11 +50,10 @@ std::optional<RawAddress> AddressOfConnection(uint16_t conn_id) { namespace bluetooth { namespace gatt { void GattServerCallbacks::OnServerReadCharacteristic(uint16_t conn_id, uint32_t trans_id, void GattServerCallbacks::OnServerRead(uint16_t conn_id, uint32_t trans_id, uint16_t attr_handle, uint32_t offset, bool is_long) const { AttributeBackingType attr_type, uint32_t offset, bool is_long) const { auto addr = AddressOfConnection(conn_id); if (!addr.has_value()) { LOG_WARN( Loading @@ -63,16 +62,28 @@ void GattServerCallbacks::OnServerReadCharacteristic(uint16_t conn_id, return; } switch (attr_type) { case AttributeBackingType::CHARACTERISTIC: do_in_jni_thread( FROM_HERE, base::Bind(callbacks.request_read_characteristic_cb, conn_id, trans_id, base::Bind(callbacks.request_read_characteristic_cb, conn_id, trans_id, addr.value(), attr_handle, offset, is_long)); break; case AttributeBackingType::DESCRIPTOR: do_in_jni_thread( FROM_HERE, base::Bind(callbacks.request_read_descriptor_cb, conn_id, trans_id, addr.value(), attr_handle, offset, is_long)); break; default: LOG_ALWAYS_FATAL("Unexpected backing type %d", attr_type); } } void GattServerCallbacks::OnServerWriteCharacteristic( uint16_t conn_id, uint32_t trans_id, uint16_t attr_handle, uint32_t offset, bool need_response, bool is_prepare, ::rust::Slice<const uint8_t> value) const { void GattServerCallbacks::OnServerWrite( uint16_t conn_id, uint32_t trans_id, uint16_t attr_handle, AttributeBackingType attr_type, uint32_t offset, bool need_response, bool is_prepare, ::rust::Slice<const uint8_t> value) const { auto addr = AddressOfConnection(conn_id); if (!addr.has_value()) { LOG_WARN( Loading @@ -84,11 +95,24 @@ void GattServerCallbacks::OnServerWriteCharacteristic( auto buf = new uint8_t[value.size()]; std::copy(value.begin(), value.end(), buf); switch (attr_type) { case AttributeBackingType::CHARACTERISTIC: do_in_jni_thread( FROM_HERE, base::Bind(callbacks.request_write_characteristic_cb, conn_id, trans_id, addr.value(), attr_handle, offset, need_response, is_prepare, base::Owned(buf), value.size())); base::Bind(callbacks.request_write_characteristic_cb, conn_id, trans_id, addr.value(), attr_handle, offset, need_response, is_prepare, base::Owned(buf), value.size())); break; case AttributeBackingType::DESCRIPTOR: do_in_jni_thread( FROM_HERE, base::Bind(callbacks.request_write_descriptor_cb, conn_id, trans_id, addr.value(), attr_handle, offset, need_response, is_prepare, base::Owned(buf), value.size())); break; default: LOG_ALWAYS_FATAL("Unexpected backing type %hhu", attr_type); } } void GattServerCallbacks::OnIndicationSentConfirmation(uint16_t conn_id, Loading system/rust/src/gatt/ffi/gatt_shim.h +16 −7 Original line number Diff line number Diff line Loading @@ -25,17 +25,26 @@ namespace bluetooth { namespace gatt { /// The GATT entity backing the value of a user-controlled /// attribute enum class AttributeBackingType { /// A GATT characteristic CHARACTERISTIC, /// A GATT descriptor DESCRIPTOR, }; class GattServerCallbacks { public: GattServerCallbacks(const btgatt_server_callbacks_t& callbacks) : callbacks(callbacks){}; void OnServerReadCharacteristic(uint16_t conn_id, uint32_t trans_id, uint16_t attr_handle, uint32_t offset, void OnServerRead(uint16_t conn_id, uint32_t trans_id, uint16_t attr_handle, AttributeBackingType attr_type, uint32_t offset, bool is_long) const; void OnServerWriteCharacteristic(uint16_t conn_id, uint32_t trans_id, uint16_t attr_handle, uint32_t offset, void OnServerWrite(uint16_t conn_id, uint32_t trans_id, uint16_t attr_handle, AttributeBackingType attr_type, uint32_t offset, bool need_response, bool is_prepare, ::rust::Slice<const uint8_t> value) const; Loading Loading
system/rust/src/gatt/callbacks.rs +13 −8 Original line number Diff line number Diff line Loading @@ -11,6 +11,7 @@ use async_trait::async_trait; use crate::packets::{AttAttributeDataChild, AttAttributeDataView, AttErrorCode}; use super::{ ffi::AttributeBackingType, ids::{AttHandle, ConnectionId, TransactionId}, server::IndicationError, }; Loading @@ -18,25 +19,27 @@ use super::{ /// These callbacks are expected to be made available to the GattModule from /// JNI. pub trait GattCallbacks { /// Invoked when a client tries to read a characteristic. Expects a response /// using bluetooth::gatt::send_response(); fn on_server_read_characteristic( /// Invoked when a client tries to read a characteristic/descriptor. Expects /// a response using bluetooth::gatt::send_response(); fn on_server_read( &self, conn_id: ConnectionId, trans_id: TransactionId, handle: AttHandle, attr_type: AttributeBackingType, offset: u32, is_long: bool, ); /// Invoked when a client tries to write a characteristic. Expects a /// response using bluetooth::gatt::send_response(); /// Invoked when a client tries to write a characteristic/descriptor. /// Expects a response using bluetooth::gatt::send_response(); #[allow(clippy::too_many_arguments)] // needed to match the C++ interface fn on_server_write_characteristic( fn on_server_write( &self, conn_id: ConnectionId, trans_id: TransactionId, handle: AttHandle, attr_type: AttributeBackingType, offset: u32, need_response: bool, is_prepare: bool, Loading Loading @@ -64,17 +67,19 @@ pub trait GattDatastore { fn remove_connection(&self, conn_id: ConnectionId); /// Read a characteristic from the specified connection at the given handle. async fn read_characteristic( async fn read( &self, conn_id: ConnectionId, handle: AttHandle, attr_type: AttributeBackingType, ) -> Result<AttAttributeDataChild, AttErrorCode>; /// Write data to a given characteristic on the specified connection. async fn write_characteristic( async fn write( &self, conn_id: ConnectionId, handle: AttHandle, attr_type: AttributeBackingType, data: AttAttributeDataView<'_>, ) -> Result<(), AttErrorCode>; }
system/rust/src/gatt/callbacks/callback_transaction_manager.rs +7 −6 Original line number Diff line number Diff line Loading @@ -12,7 +12,7 @@ use crate::{ packets::{AttAttributeDataChild, AttAttributeDataView, AttErrorCode}, }; use super::GattDatastore; use super::{AttributeBackingType, GattDatastore}; struct PendingTransaction { response: oneshot::Sender<Result<AttAttributeDataChild, AttErrorCode>>, Loading Loading @@ -121,15 +121,16 @@ impl GattDatastore for CallbackTransactionManager { assert!(old_conn.is_some(), "Received unexpected connection ID, something has gone wrong") } async fn read_characteristic( async fn read( &self, conn_id: ConnectionId, handle: AttHandle, attr_type: AttributeBackingType, ) -> Result<AttAttributeDataChild, AttErrorCode> { let (trans_id, rx) = self.pending_transactions.borrow_mut().start_new_transaction(conn_id)?; self.callbacks.on_server_read_characteristic(conn_id, trans_id, handle, 0, false); self.callbacks.on_server_read(conn_id, trans_id, handle, attr_type, 0, false); if let Ok(value) = rx.await { value Loading @@ -139,17 +140,17 @@ impl GattDatastore for CallbackTransactionManager { } } async fn write_characteristic( async fn write( &self, conn_id: ConnectionId, handle: AttHandle, attr_type: AttributeBackingType, data: AttAttributeDataView<'_>, ) -> Result<(), AttErrorCode> { let (trans_id, rx) = self.pending_transactions.borrow_mut().start_new_transaction(conn_id)?; self.callbacks .on_server_write_characteristic(conn_id, trans_id, handle, 0, true, false, data); self.callbacks.on_server_write(conn_id, trans_id, handle, attr_type, 0, true, false, data); if let Ok(value) = rx.await { value.map(|_| ()) // the data passed back is irrelevant for write Loading
system/rust/src/gatt/ffi.rs +148 −20 Original line number Diff line number Diff line //! FFI interfaces for the GATT module. Some structs are exported so that //! core::init can instantiate and pass them into the main loop. use std::iter::Peekable; use anyhow::{bail, Result}; use bt_common::init_flags::{ always_use_private_gatt_for_debugging_is_enabled, rust_event_loop_is_enabled, Loading @@ -23,8 +25,10 @@ use super::{ channel::AttTransport, ids::{AdvertiserId, AttHandle, ConnectionId, ServerId, TransactionId, TransportIndex}, server::{ att_server_bearer::AttServerBearer, gatt_database::{AttPermissions, GattCharacteristicWithHandle, GattServiceWithHandle}, gatt_database::{ AttPermissions, GattCharacteristicWithHandle, GattDescriptorWithHandle, GattServiceWithHandle, }, IndicationError, }, GattCallbacks, Loading @@ -44,33 +48,49 @@ mod inner { type Uuid = crate::core::uuid::Uuid; } /// The GATT entity backing the value of a user-controlled /// attribute #[derive(Debug)] #[namespace = "bluetooth::gatt"] enum AttributeBackingType { /// A GATT characteristic #[cxx_name = "CHARACTERISTIC"] Characteristic = 0u32, /// A GATT descriptor #[cxx_name = "DESCRIPTOR"] Descriptor = 1u32, } #[namespace = "bluetooth::gatt"] unsafe extern "C++" { include!("src/gatt/ffi/gatt_shim.h"); type AttributeBackingType; /// This contains the callbacks from Rust into C++ JNI needed for GATT type GattServerCallbacks; /// This callback is invoked when reading a characteristic - the client /// This callback is invoked when reading - the client /// must reply using SendResponse #[cxx_name = "OnServerReadCharacteristic"] fn on_server_read_characteristic( #[cxx_name = "OnServerRead"] fn on_server_read( self: &GattServerCallbacks, conn_id: u16, trans_id: u32, attr_handle: u16, attr_type: AttributeBackingType, offset: u32, is_long: bool, ); /// This callback is invoked when writing a characteristic - the client /// This callback is invoked when writing - the client /// must reply using SendResponse #[cxx_name = "OnServerWriteCharacteristic"] fn on_server_write_characteristic( #[cxx_name = "OnServerWrite"] fn on_server_write( self: &GattServerCallbacks, conn_id: u16, trans_id: u32, attr_handle: u16, attr_type: AttributeBackingType, offset: u32, need_response: bool, is_prepare: bool, Loading @@ -80,7 +100,7 @@ mod inner { /// This callback is invoked when an indication has been sent and the /// peer device has confirmed it, or if some error occurred. #[cxx_name = "OnIndicationSentConfirmation"] fn on_indication_sent_confirmation(&self, conn_id: u16, status: i32); fn on_indication_sent_confirmation(self: &GattServerCallbacks, conn_id: u16, status: i32); } /// What action the arbiter should take in response to an incoming packet Loading Loading @@ -160,34 +180,37 @@ mod inner { pub struct GattCallbacksImpl(pub UniquePtr<GattServerCallbacks>); impl GattCallbacks for GattCallbacksImpl { fn on_server_read_characteristic( fn on_server_read( &self, conn_id: ConnectionId, trans_id: TransactionId, handle: AttHandle, attr_type: AttributeBackingType, offset: u32, is_long: bool, ) { self.0 .as_ref() .unwrap() .on_server_read_characteristic(conn_id.0, trans_id.0, handle.0, offset, is_long); .on_server_read(conn_id.0, trans_id.0, handle.0, attr_type, offset, is_long); } fn on_server_write_characteristic( fn on_server_write( &self, conn_id: ConnectionId, trans_id: TransactionId, handle: AttHandle, attr_type: AttributeBackingType, offset: u32, need_response: bool, is_prepare: bool, value: AttAttributeDataView, ) { self.0.as_ref().unwrap().on_server_write_characteristic( self.0.as_ref().unwrap().on_server_write( conn_id.0, trans_id.0, handle.0, attr_type, offset, need_response, is_prepare, Loading Loading @@ -262,11 +285,33 @@ fn close_server(server_id: u8) { }) } fn consume_descriptors<'a>( records: &mut Peekable<impl Iterator<Item = &'a GattRecord>>, ) -> Vec<GattDescriptorWithHandle> { let mut out = vec![]; while let Some(GattRecord { uuid, attribute_handle, permissions, .. }) = records.next_if(|record| record.record_type == GattRecordType::Descriptor) { let mut att_permissions = AttPermissions::empty(); att_permissions.set(AttPermissions::READABLE, permissions & 0x01 != 0); att_permissions.set(AttPermissions::WRITABLE, permissions & 0x10 != 0); out.push(GattDescriptorWithHandle { handle: AttHandle(*attribute_handle), type_: *uuid, permissions: att_permissions, }) } out } fn records_to_service(service_records: &[GattRecord]) -> Result<GattServiceWithHandle> { let mut characteristics = vec![]; let mut service_handle_uuid = None; for record in service_records { let mut service_records = service_records.iter().peekable(); while let Some(record) = service_records.next() { match record.record_type { GattRecordType::PrimaryService => { if service_handle_uuid.is_some() { Loading @@ -274,11 +319,17 @@ fn records_to_service(service_records: &[GattRecord]) -> Result<GattServiceWithH } service_handle_uuid = Some((record.attribute_handle, record.uuid)); } GattRecordType::Characteristic => characteristics.push(GattCharacteristicWithHandle { GattRecordType::Characteristic => { characteristics.push(GattCharacteristicWithHandle { handle: AttHandle(record.attribute_handle), type_: record.uuid, permissions: AttPermissions::from_bits_truncate(record.properties), }), descriptors: consume_descriptors(&mut service_records), }); } GattRecordType::Descriptor => { bail!("Got unexpected descriptor outside of characteristic declaration") } _ => { warn!("ignoring unsupported database entry of type {:?}", record.record_type) } Loading Loading @@ -424,7 +475,10 @@ mod test { const CHARACTERISTIC_HANDLE: AttHandle = AttHandle(2); const CHARACTERISTIC_UUID: Uuid = Uuid::new(0x5678); const ANOTHER_CHARACTERISTIC_HANDLE: AttHandle = AttHandle(3); const DESCRIPTOR_UUID: Uuid = Uuid::new(0x4321); const ANOTHER_DESCRIPTOR_UUID: Uuid = Uuid::new(0x5432); const ANOTHER_CHARACTERISTIC_HANDLE: AttHandle = AttHandle(10); const ANOTHER_CHARACTERISTIC_UUID: Uuid = Uuid::new(0x9ABC); fn make_service_record(uuid: Uuid, handle: AttHandle) -> GattRecord { Loading @@ -449,6 +503,17 @@ mod test { } } fn make_descriptor_record(uuid: Uuid, handle: AttHandle, permissions: u16) -> GattRecord { GattRecord { uuid, record_type: GattRecordType::Descriptor, attribute_handle: handle.0, properties: 0, extended_properties: 0, permissions, } } #[test] fn test_empty_records() { let res = records_to_service(&[]); Loading Loading @@ -546,4 +611,67 @@ mod test { AttPermissions::READABLE | AttPermissions::WRITABLE ); } #[test] fn test_multiple_descriptors() { let service = records_to_service(&[ make_service_record(SERVICE_UUID, AttHandle(1)), make_characteristic_record(CHARACTERISTIC_UUID, AttHandle(2), 0), make_descriptor_record(DESCRIPTOR_UUID, AttHandle(3), 0), make_descriptor_record(ANOTHER_DESCRIPTOR_UUID, AttHandle(4), 0), ]) .unwrap(); assert_eq!(service.characteristics[0].descriptors.len(), 2); assert_eq!(service.characteristics[0].descriptors[0].handle, AttHandle(3)); assert_eq!(service.characteristics[0].descriptors[0].type_, DESCRIPTOR_UUID); assert_eq!(service.characteristics[0].descriptors[1].handle, AttHandle(4)); assert_eq!(service.characteristics[0].descriptors[1].type_, ANOTHER_DESCRIPTOR_UUID); } #[test] fn test_descriptor_permissions() { let service = records_to_service(&[ make_service_record(SERVICE_UUID, AttHandle(1)), make_characteristic_record(CHARACTERISTIC_UUID, AttHandle(2), 0), make_descriptor_record(DESCRIPTOR_UUID, AttHandle(3), 0x01), make_descriptor_record(DESCRIPTOR_UUID, AttHandle(4), 0x10), make_descriptor_record(DESCRIPTOR_UUID, AttHandle(5), 0x11), ]) .unwrap(); assert_eq!(service.characteristics[0].descriptors[0].permissions, AttPermissions::READABLE); assert_eq!(service.characteristics[0].descriptors[1].permissions, AttPermissions::WRITABLE); assert_eq!( service.characteristics[0].descriptors[2].permissions, AttPermissions::READABLE | AttPermissions::WRITABLE ); } #[test] fn test_descriptors_multiple_characteristics() { let service = records_to_service(&[ make_service_record(SERVICE_UUID, AttHandle(1)), make_characteristic_record(CHARACTERISTIC_UUID, AttHandle(2), 0), make_descriptor_record(DESCRIPTOR_UUID, AttHandle(3), 0), make_characteristic_record(CHARACTERISTIC_UUID, AttHandle(4), 0), make_descriptor_record(DESCRIPTOR_UUID, AttHandle(5), 0), ]) .unwrap(); assert_eq!(service.characteristics[0].descriptors.len(), 1); assert_eq!(service.characteristics[0].descriptors[0].handle, AttHandle(3)); assert_eq!(service.characteristics[1].descriptors.len(), 1); assert_eq!(service.characteristics[1].descriptors[0].handle, AttHandle(5)); } #[test] fn test_unexpected_descriptor() { let res = records_to_service(&[ make_service_record(SERVICE_UUID, AttHandle(1)), make_descriptor_record(DESCRIPTOR_UUID, AttHandle(3), 0), ]); assert!(res.is_err()); } }
system/rust/src/gatt/ffi/gatt_shim.cc +42 −18 Original line number Diff line number Diff line Loading @@ -50,11 +50,10 @@ std::optional<RawAddress> AddressOfConnection(uint16_t conn_id) { namespace bluetooth { namespace gatt { void GattServerCallbacks::OnServerReadCharacteristic(uint16_t conn_id, uint32_t trans_id, void GattServerCallbacks::OnServerRead(uint16_t conn_id, uint32_t trans_id, uint16_t attr_handle, uint32_t offset, bool is_long) const { AttributeBackingType attr_type, uint32_t offset, bool is_long) const { auto addr = AddressOfConnection(conn_id); if (!addr.has_value()) { LOG_WARN( Loading @@ -63,16 +62,28 @@ void GattServerCallbacks::OnServerReadCharacteristic(uint16_t conn_id, return; } switch (attr_type) { case AttributeBackingType::CHARACTERISTIC: do_in_jni_thread( FROM_HERE, base::Bind(callbacks.request_read_characteristic_cb, conn_id, trans_id, base::Bind(callbacks.request_read_characteristic_cb, conn_id, trans_id, addr.value(), attr_handle, offset, is_long)); break; case AttributeBackingType::DESCRIPTOR: do_in_jni_thread( FROM_HERE, base::Bind(callbacks.request_read_descriptor_cb, conn_id, trans_id, addr.value(), attr_handle, offset, is_long)); break; default: LOG_ALWAYS_FATAL("Unexpected backing type %d", attr_type); } } void GattServerCallbacks::OnServerWriteCharacteristic( uint16_t conn_id, uint32_t trans_id, uint16_t attr_handle, uint32_t offset, bool need_response, bool is_prepare, ::rust::Slice<const uint8_t> value) const { void GattServerCallbacks::OnServerWrite( uint16_t conn_id, uint32_t trans_id, uint16_t attr_handle, AttributeBackingType attr_type, uint32_t offset, bool need_response, bool is_prepare, ::rust::Slice<const uint8_t> value) const { auto addr = AddressOfConnection(conn_id); if (!addr.has_value()) { LOG_WARN( Loading @@ -84,11 +95,24 @@ void GattServerCallbacks::OnServerWriteCharacteristic( auto buf = new uint8_t[value.size()]; std::copy(value.begin(), value.end(), buf); switch (attr_type) { case AttributeBackingType::CHARACTERISTIC: do_in_jni_thread( FROM_HERE, base::Bind(callbacks.request_write_characteristic_cb, conn_id, trans_id, addr.value(), attr_handle, offset, need_response, is_prepare, base::Owned(buf), value.size())); base::Bind(callbacks.request_write_characteristic_cb, conn_id, trans_id, addr.value(), attr_handle, offset, need_response, is_prepare, base::Owned(buf), value.size())); break; case AttributeBackingType::DESCRIPTOR: do_in_jni_thread( FROM_HERE, base::Bind(callbacks.request_write_descriptor_cb, conn_id, trans_id, addr.value(), attr_handle, offset, need_response, is_prepare, base::Owned(buf), value.size())); break; default: LOG_ALWAYS_FATAL("Unexpected backing type %hhu", attr_type); } } void GattServerCallbacks::OnIndicationSentConfirmation(uint16_t conn_id, Loading
system/rust/src/gatt/ffi/gatt_shim.h +16 −7 Original line number Diff line number Diff line Loading @@ -25,17 +25,26 @@ namespace bluetooth { namespace gatt { /// The GATT entity backing the value of a user-controlled /// attribute enum class AttributeBackingType { /// A GATT characteristic CHARACTERISTIC, /// A GATT descriptor DESCRIPTOR, }; class GattServerCallbacks { public: GattServerCallbacks(const btgatt_server_callbacks_t& callbacks) : callbacks(callbacks){}; void OnServerReadCharacteristic(uint16_t conn_id, uint32_t trans_id, uint16_t attr_handle, uint32_t offset, void OnServerRead(uint16_t conn_id, uint32_t trans_id, uint16_t attr_handle, AttributeBackingType attr_type, uint32_t offset, bool is_long) const; void OnServerWriteCharacteristic(uint16_t conn_id, uint32_t trans_id, uint16_t attr_handle, uint32_t offset, void OnServerWrite(uint16_t conn_id, uint32_t trans_id, uint16_t attr_handle, AttributeBackingType attr_type, uint32_t offset, bool need_response, bool is_prepare, ::rust::Slice<const uint8_t> value) const; Loading