Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 09d55be6 authored by Sarvesh Kalwit's avatar Sarvesh Kalwit
Browse files

floss: Allow sending a GATT server request response with btclient

Allow btclient users to manually send a response to GATT server
attribute read and write requests.

Bug: 328813054
Test: m -j && manually with btclient:
Add and register services. Connect with NRF Connect.
Send read/write requests with NRF Connect and send a response after each
request.
Observe that requests are processed.
Flag: Exempt, Floss-only change

Change-Id: I0840403d91b88dde2a723a02ce12de8a7a2fe5a1
parent fb721fae
Loading
Loading
Loading
Loading
+50 −5
Original line number Diff line number Diff line
@@ -8,8 +8,8 @@ use crate::dbus_iface::{
    export_qa_callback_dbus_intf, export_scanner_callback_dbus_intf,
    export_socket_callback_dbus_intf, export_suspend_callback_dbus_intf,
};
use crate::ClientContext;
use crate::{console_red, console_yellow, print_error, print_info};
use crate::{ClientContext, GattRequest};
use bt_topshim::btif::{BtBondState, BtPropertyType, BtSspVariant, BtStatus, Uuid128Bit};
use bt_topshim::profiles::gatt::{AdvertisingStatus, GattStatus, LePhy};
use bt_topshim::profiles::hfp::HfpCodecId;
@@ -841,7 +841,7 @@ impl RPCProxy for BtGattCallback {

pub(crate) struct BtGattServerCallback {
    objpath: String,
    _context: Arc<Mutex<ClientContext>>,
    context: Arc<Mutex<ClientContext>>,

    dbus_connection: Arc<SyncConnection>,
    dbus_crossroads: Arc<Mutex<Crossroads>>,
@@ -850,11 +850,11 @@ pub(crate) struct BtGattServerCallback {
impl BtGattServerCallback {
    pub(crate) fn new(
        objpath: String,
        _context: Arc<Mutex<ClientContext>>,
        context: Arc<Mutex<ClientContext>>,
        dbus_connection: Arc<SyncConnection>,
        dbus_crossroads: Arc<Mutex<Crossroads>>,
    ) -> Self {
        Self { objpath, _context, dbus_connection, dbus_crossroads }
        Self { objpath, context, dbus_connection, dbus_crossroads }
    }
}

@@ -890,12 +890,21 @@ impl IBluetoothGattServerCallback for BtGattServerCallback {
    ) {
        print_info!(
            "GATT characteristic read request for addr = {}, trans_id = {}, offset = {}, is_long = {}, handle = {}",
            addr,
            addr.clone(),
            trans_id,
            offset,
            is_long,
            handle
        );

        if self.context.lock().unwrap().pending_gatt_request.is_some() {
            print_info!(
                "This request will be dropped because the previous one has not been responded to"
            );
            return;
        }
        self.context.lock().unwrap().pending_gatt_request =
            Some(GattRequest { address: addr, id: trans_id, offset: offset, value: vec![] });
    }

    fn on_descriptor_read_request(
@@ -914,6 +923,15 @@ impl IBluetoothGattServerCallback for BtGattServerCallback {
            is_long,
            handle
        );

        if self.context.lock().unwrap().pending_gatt_request.is_some() {
            print_info!(
                "This request will be dropped because the previous one has not been responded to"
            );
            return;
        }
        self.context.lock().unwrap().pending_gatt_request =
            Some(GattRequest { address: addr, id: trans_id, offset: offset, value: vec![] });
    }

    fn on_characteristic_write_request(
@@ -939,6 +957,15 @@ impl IBluetoothGattServerCallback for BtGattServerCallback {
            handle,
            value
        );

        if self.context.lock().unwrap().pending_gatt_request.is_some() {
            print_info!(
                "This request will be dropped because the previous one has not been responded to"
            );
            return;
        }
        self.context.lock().unwrap().pending_gatt_request =
            Some(GattRequest { address: addr, id: trans_id, offset: offset, value: value });
    }

    fn on_descriptor_write_request(
@@ -964,6 +991,15 @@ impl IBluetoothGattServerCallback for BtGattServerCallback {
            handle,
            value
        );

        if self.context.lock().unwrap().pending_gatt_request.is_some() {
            print_info!(
                "This request will be dropped because the previous one has not been responded to"
            );
            return;
        }
        self.context.lock().unwrap().pending_gatt_request =
            Some(GattRequest { address: addr, id: trans_id, offset: offset, value: value });
    }

    fn on_execute_write(&mut self, addr: String, trans_id: i32, exec_write: bool) {
@@ -973,6 +1009,15 @@ impl IBluetoothGattServerCallback for BtGattServerCallback {
            trans_id,
            exec_write
        );

        if self.context.lock().unwrap().pending_gatt_request.is_some() {
            print_info!(
                "This request will be dropped because the previous one has not been responded to"
            );
            return;
        }
        self.context.lock().unwrap().pending_gatt_request =
            Some(GattRequest { address: addr, id: trans_id, offset: 0, value: vec![] });
    }

    fn on_notification_sent(&mut self, addr: String, status: GattStatus) {
+37 −10
Original line number Diff line number Diff line
@@ -10,9 +10,10 @@ use crate::callbacks::{BtGattCallback, BtGattServerCallback};
use crate::ClientContext;
use crate::{console_red, console_yellow, print_error, print_info};
use bt_topshim::btif::{BtConnectionState, BtDiscMode, BtStatus, BtTransport, Uuid, INVALID_RSSI};
use bt_topshim::profiles::gatt::{GattStatus, LePhy};
use bt_topshim::profiles::hid_host::BthhReportType;
use bt_topshim::profiles::sdp::{BtSdpMpsRecord, BtSdpRecord};
use bt_topshim::profiles::{gatt::LePhy, ProfileConnectionState};
use bt_topshim::profiles::ProfileConnectionState;
use btstack::battery_manager::IBatteryManager;
use btstack::bluetooth::{BluetoothDevice, IBluetooth};
use btstack::bluetooth_gatt::{
@@ -38,11 +39,6 @@ const GENERIC_UUID: &str = "00000000-0000-1000-8000-00805F9B34FB";
const CCC_DESCRIPTOR_UUID: &str = "00002902-0000-1000-8000-00805F9B34FB";
const BATTERY_SERVICE_UUID: &str = "0000180F-0000-1000-8000-00805F9B34FB";

const PROPERTY_WRITE: i32 = 1 << 3;
const PROPERTY_NOTIFY: i32 = 1 << 4;
const PERMISSION_READ: i32 = 1 << 0;
const PERMISSION_WRITE: i32 = 1 << 1;

enum CommandError {
    // Command not handled due to invalid arguments.
    InvalidArgs,
@@ -242,6 +238,7 @@ fn build_commands() -> HashMap<String, CommandOption> {
                String::from("gatt server-add-service <server_id> <incl_service_instance_id>"),
                String::from("gatt server-remove-service <server_id> <service_handle>"),
                String::from("gatt server-clear-all-services <server_id>"),
                String::from("gatt server-send-response <server_id> <success|fail>"),
                String::from("gatt server-set-direct-connect <true|false>"),
                String::from("gatt server-set-connect-transport <Bredr|LE|Auto>"),
            ],
@@ -1432,18 +1429,23 @@ impl CommandHandler {
                let mut characteristic = BluetoothGattCharacteristic::new(
                    characteristic_uuid.into(),
                    0,
                    PROPERTY_WRITE + PROPERTY_NOTIFY,
                    PERMISSION_READ + PERMISSION_WRITE,
                    BluetoothGattCharacteristic::PROPERTY_READ
                        | BluetoothGattCharacteristic::PROPERTY_WRITE
                        | BluetoothGattCharacteristic::PROPERTY_NOTIFY,
                    BluetoothGattCharacteristic::PERMISSION_READ
                        | BluetoothGattCharacteristic::PERMISSION_WRITE,
                );
                let descriptor = BluetoothGattDescriptor::new(
                    descriptor_uuid.into(),
                    0,
                    PERMISSION_READ + PERMISSION_WRITE,
                    BluetoothGattCharacteristic::PERMISSION_READ
                        | BluetoothGattCharacteristic::PERMISSION_WRITE,
                );
                let ccc_descriptor = BluetoothGattDescriptor::new(
                    ccc_descriptor_uuid.into(),
                    0,
                    PERMISSION_READ + PERMISSION_WRITE,
                    BluetoothGattCharacteristic::PERMISSION_READ
                        | BluetoothGattCharacteristic::PERMISSION_WRITE,
                );

                service.included_services.push(included_service);
@@ -1473,6 +1475,31 @@ impl CommandHandler {
                    .or(Err("Failed to parse server_id"))?;
                self.lock_context().gatt_dbus.as_mut().unwrap().clear_services(server_id);
            }
            "server-send-response" => {
                let server_id = String::from(get_arg(args, 1)?)
                    .parse::<i32>()
                    .or(Err("Failed to parse server_id"))?;
                let status = match String::from(get_arg(args, 2)?).as_str() {
                    "success" => GattStatus::Success,
                    "fail" => GattStatus::Error,
                    _ => return Err("{} is not one of the following: `success`, `fail`".into()),
                };

                let request = match self.lock_context().pending_gatt_request.clone() {
                    None => return Err("No pending request to send response to".into()),
                    Some(r) => r,
                };
                self.lock_context().gatt_dbus.as_mut().unwrap().send_response(
                    server_id,
                    request.address.clone(),
                    request.id,
                    status,
                    request.offset,
                    request.value.clone(),
                );

                self.lock_context().pending_gatt_request = None;
            }
            "server-set-direct-connect" => {
                let is_direct = String::from(get_arg(args, 1)?)
                    .parse::<bool>()
+12 −0
Original line number Diff line number Diff line
@@ -39,6 +39,14 @@ mod dbus_arg;
mod dbus_iface;
mod editor;

#[derive(Clone)]
pub(crate) struct GattRequest {
    address: String,
    id: i32,
    offset: i32,
    value: Vec<u8>,
}

/// Context structure for the client. Used to keep track details about the active adapter and its
/// state.
pub(crate) struct ClientContext {
@@ -155,6 +163,9 @@ pub(crate) struct ClientContext {

    /// A set of addresses whose battery changes are being tracked.
    pub(crate) battery_address_filter: HashSet<String>,

    /// A request from a GATT client that is still being processed.
    pending_gatt_request: Option<GattRequest>,
}

impl ClientContext {
@@ -207,6 +218,7 @@ impl ClientContext {
            mps_sdp_handle: None,
            client_commands_with_callbacks,
            battery_address_filter: HashSet::new(),
            pending_gatt_request: None,
        }
    }