[sl4f] GattServerFacade: Account for long writes
Continue to allow long writes as long as the maximum
attribute length isn't reached.
BUG: CONN-72
Test: PTS Test GATT/SR/GAW/BI-09,BV-05..10,BI-32,BI-33
Change-Id: Iac3c3c89467fb4f5298fa5e4176eeb042ca3d219
diff --git a/garnet/bin/sl4f/src/bluetooth/constants.rs b/garnet/bin/sl4f/src/bluetooth/constants.rs
index 644abe6..eb04194 100644
--- a/garnet/bin/sl4f/src/bluetooth/constants.rs
+++ b/garnet/bin/sl4f/src/bluetooth/constants.rs
@@ -16,3 +16,6 @@
/// GATT Attribute Property Values
pub const PROPERTY_NOTIFY: u32 = 0x10;
pub const PROPERTY_INDICATE: u32 = 0x20;
+
+/// GATT Max Attribute Length (Vol 3, Part F, 3.2.9)
+pub const GATT_MAX_ATTRIBUTE_VALUE_LENGTH: usize = 512;
diff --git a/garnet/bin/sl4f/src/bluetooth/gatt_server_facade.rs b/garnet/bin/sl4f/src/bluetooth/gatt_server_facade.rs
index a621218..eca0456 100644
--- a/garnet/bin/sl4f/src/bluetooth/gatt_server_facade.rs
+++ b/garnet/bin/sl4f/src/bluetooth/gatt_server_facade.rs
@@ -21,9 +21,9 @@
use std::collections::HashMap;
use crate::bluetooth::constants::{
- PERMISSION_READ_ENCRYPTED, PERMISSION_READ_ENCRYPTED_MITM, PERMISSION_WRITE_ENCRYPTED,
- PERMISSION_WRITE_ENCRYPTED_MITM, PERMISSION_WRITE_SIGNED, PERMISSION_WRITE_SIGNED_MITM,
- PROPERTY_INDICATE, PROPERTY_NOTIFY,
+ GATT_MAX_ATTRIBUTE_VALUE_LENGTH, PERMISSION_READ_ENCRYPTED, PERMISSION_READ_ENCRYPTED_MITM,
+ PERMISSION_WRITE_ENCRYPTED, PERMISSION_WRITE_ENCRYPTED_MITM, PERMISSION_WRITE_SIGNED,
+ PERMISSION_WRITE_SIGNED_MITM, PROPERTY_INDICATE, PROPERTY_NOTIFY,
};
#[derive(Debug)]
@@ -47,7 +47,10 @@
struct InnerGattServerFacade {
/// attribute_value_mapping: A Hashmap that will be used for capturing
/// and updating Characteristic and Descriptor values for each service.
- attribute_value_mapping: HashMap<u64, Vec<u8>>,
+ /// The bool value represents whether value size of the initial Characteristic
+ /// Descriptor value should be enforced or not for prepared writes. True
+ /// for enforce, false to allow the size to grow to max values.
+ attribute_value_mapping: HashMap<u64, (Vec<u8>, bool)>,
/// A generic counter GATT server attributes
generic_id_counter: Counter,
@@ -152,7 +155,7 @@
id: u64,
offset: i32,
responder: LocalServiceDelegateOnReadValueResponder,
- value_in_mapping: Option<&Vec<u8>>,
+ value_in_mapping: Option<&(Vec<u8>, bool)>,
) {
let tag = "GattServerFacade::on_read_value:";
fx_log_info!(
@@ -163,10 +166,11 @@
);
match value_in_mapping {
Some(v) => {
- if v.len() < offset as usize {
+ let (value, _enforce_initial_attribute_length) = v;
+ if value.len() < offset as usize {
let _result = responder.send(None, gatt::ErrorCode::InvalidOffset);
} else {
- let value_to_write = v.clone().split_off(offset as usize);
+ let value_to_write = value.clone().split_off(offset as usize);
let _result = responder.send(
Some(&mut value_to_write.to_vec().into_iter()),
gatt::ErrorCode::NoError,
@@ -180,12 +184,20 @@
};
}
+ pub fn write_and_extend(value: &mut Vec<u8>, value_to_write: Vec<u8>, offset: usize) {
+ let split_idx = (value.len() - offset).min(value_to_write.len());
+ let (overlapping, extending) = value_to_write.split_at(split_idx);
+ let end_of_overlap = offset + overlapping.len();
+ value.splice(offset..end_of_overlap, overlapping.iter().cloned());
+ value.extend_from_slice(extending);
+ }
+
pub fn on_write_value(
id: u64,
offset: u16,
- value: Vec<u8>,
+ value_to_write: Vec<u8>,
responder: LocalServiceDelegateOnWriteValueResponder,
- value_in_mapping: Option<&mut Vec<u8>>,
+ value_in_mapping: Option<&mut (Vec<u8>, bool)>,
) {
let tag = "GattServerFacade::on_write_value:";
fx_log_info!(
@@ -193,17 +205,22 @@
"OnWriteValue request at id: {:?}, with offset: {:?}, with value: {:?}",
id,
offset,
- value
+ value_to_write
);
match value_in_mapping {
Some(v) => {
- if v.len() < (value.len() + offset as usize) {
+ let (value, enforce_initial_attribute_length) = v;
+ let max_attribute_size: usize = match enforce_initial_attribute_length {
+ true => value.len(),
+ false => GATT_MAX_ATTRIBUTE_VALUE_LENGTH,
+ };
+ if max_attribute_size < (value_to_write.len() + offset as usize) {
let _result = responder.send(gatt::ErrorCode::InvalidValueLength);
- } else if v.len() < offset as usize {
+ } else if value.len() < offset as usize {
let _result = responder.send(gatt::ErrorCode::InvalidOffset);
} else {
- v.splice(offset as usize.., value.into_iter());
+ &mut GattServerFacade::write_and_extend(value, value_to_write, offset as usize);
let _result = responder.send(gatt::ErrorCode::NoError);
}
}
@@ -217,8 +234,8 @@
pub fn on_write_without_response(
id: u64,
offset: u16,
- value: Vec<u8>,
- value_in_mapping: Option<&mut Vec<u8>>,
+ value_to_write: Vec<u8>,
+ value_in_mapping: Option<&mut (Vec<u8>, bool)>,
) {
let tag = "GattServerFacade::on_write_without_response:";
fx_log_info!(
@@ -226,18 +243,17 @@
"OnWriteWithoutResponse request at id: {:?}, with offset: {:?}, with value: {:?}",
id,
offset,
- value
+ value_to_write
);
if let Some(v) = value_in_mapping {
- if v.len() >= (value.len() + offset as usize) {
- v.splice(offset as usize.., value.into_iter());
- }
+ let (value, _enforce_initial_attribute_length) = v;
+ &mut GattServerFacade::write_and_extend(value, value_to_write, offset as usize);
}
}
pub async fn monitor_delegate_request_stream(
stream: LocalServiceDelegateRequestStream,
- mut attribute_value_mapping: HashMap<u64, Vec<u8>>,
+ mut attribute_value_mapping: HashMap<u64, (Vec<u8>, bool)>,
service_proxy: LocalServiceProxy,
) -> Result<(), Error> {
use fidl_fuchsia_bluetooth_gatt::LocalServiceDelegateRequest::*;
@@ -424,6 +440,9 @@
};
let descriptor_value = self.parse_attribute_value_to_byte_array(&descriptor["value"]);
+ let raw_enforce_enforce_initial_attribute_length =
+ descriptor["enforce_initial_attribute_length"].as_bool().unwrap_or(false);
+
// No properties for descriptors.
let properties = 0u32;
@@ -440,7 +459,10 @@
.permissions_and_properties_from_raw_num(raw_descriptor_permissions, properties);
let descriptor_id = self.inner.write().generic_id_counter.next();
- self.inner.write().attribute_value_mapping.insert(descriptor_id, descriptor_value);
+ self.inner.write().attribute_value_mapping.insert(
+ descriptor_id,
+ (descriptor_value, raw_enforce_enforce_initial_attribute_length),
+ );
let descriptor_obj = Descriptor {
id: descriptor_id,
type_: descriptor_uuid.clone(),
@@ -484,6 +506,10 @@
let characteristic_value =
self.parse_attribute_value_to_byte_array(&characteristic["value"]);
+
+ let raw_enforce_enforce_initial_attribute_length =
+ characteristic["enforce_initial_attribute_length"].as_bool().unwrap_or(false);
+
let descriptor_list = &characteristic["descriptors"];
let descriptors = self.generate_descriptors(descriptor_list)?;
@@ -491,11 +517,12 @@
raw_characteristic_permissions,
characteristic_properties,
);
+
let characteristic_id = self.inner.write().generic_id_counter.next();
- self.inner
- .write()
- .attribute_value_mapping
- .insert(characteristic_id, characteristic_value);
+ self.inner.write().attribute_value_mapping.insert(
+ characteristic_id,
+ (characteristic_value, raw_enforce_enforce_initial_attribute_length),
+ );
let characteristic_obj = Characteristic {
id: characteristic_id,
type_: characteristic_uuid,