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

Commit bd7608d5 authored by Abhishek Pandit-Subedi's avatar Abhishek Pandit-Subedi
Browse files

floss: Add Device Information Service

Update sysprops to have device information and register DIS during
startup via floss. This will eventually move out of Floss and into
libbluetooth in the future.

Bug: 277818879
Tag: #floss
Test: Gatt connect two chromebooks and observer PNP_ID is valid
Change-Id: I5ac03258ad5ecb00ec5620fb6e6609be87ea97f2
parent 87141c9e
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ use btstack::{
    bluetooth_gatt::BluetoothGatt,
    bluetooth_logging::BluetoothLogging,
    bluetooth_media::BluetoothMedia,
    dis::DeviceInformation,
    socket_manager::BluetoothSocketManager,
    suspend::Suspend,
    Message, Stack,
@@ -177,6 +178,8 @@ fn main() -> Result<(), Box<dyn Error>> {
    ))));
    let bt_sock_mgr = Arc::new(Mutex::new(Box::new(BluetoothSocketManager::new(tx.clone()))));
    let qa = Arc::new(Mutex::new(Box::new(BluetoothQA::new(tx.clone()))));
    let dis =
        Arc::new(Mutex::new(Box::new(DeviceInformation::new(bluetooth_gatt.clone(), tx.clone()))));

    topstack::get_runtime().block_on(async {
        // Connect to D-Bus system bus.
@@ -219,6 +222,7 @@ fn main() -> Result<(), Box<dyn Error>> {
            suspend.clone(),
            bt_sock_mgr.clone(),
            bluetooth_admin.clone(),
            dis.clone(),
        ));

        // Set up the disconnect watcher to monitor client disconnects.
+6 −0
Original line number Diff line number Diff line
@@ -1135,6 +1135,12 @@ impl BtifBluetoothCallbacks for Bluetooth {
                    Err(err) => warn!("create_pid_file() error: {}", err),
                    _ => (),
                }

                // Inform the rest of the stack we're ready.
                let txl = self.tx.clone();
                tokio::spawn(async move {
                    let _ = txl.send(Message::AdapterReady).await;
                });
            }
        }
    }
+40 −15
Original line number Diff line number Diff line
@@ -687,7 +687,11 @@ pub struct BluetoothGattDescriptor {
}

impl BluetoothGattDescriptor {
    fn new(uuid: Uuid128Bit, instance_id: i32, permissions: i32) -> BluetoothGattDescriptor {
    pub(crate) fn new(
        uuid: Uuid128Bit,
        instance_id: i32,
        permissions: i32,
    ) -> BluetoothGattDescriptor {
        BluetoothGattDescriptor { uuid, instance_id, permissions }
    }
}
@@ -705,16 +709,27 @@ pub struct BluetoothGattCharacteristic {
}

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(
    // Properties are u8 but i32 in these apis.
    pub const PROPERTY_BROADCAST: i32 = 1 << 0;
    pub const PROPERTY_READ: i32 = 1 << 1;
    pub const PROPERTY_WRITE_NO_RESPONSE: i32 = 1 << 2;
    pub const PROPERTY_WRITE: i32 = 1 << 3;
    pub const PROPERTY_NOTIFY: i32 = 1 << 4;
    pub const PROPERTY_INDICATE: i32 = 1 << 5;
    pub const PROPERTY_SIGNED_WRITE: i32 = 1 << 6;
    pub const PROPERTY_EXTENDED_PROPS: i32 = 1 << 7;

    // Permissions are u16 but i32 in these apis.
    pub const PERMISSION_READ: i32 = 1 << 0;
    pub const PERMISSION_READ_ENCRYPTED: i32 = 1 << 1;
    pub const PERMISSION_READ_ENCRYPED_MITM: i32 = 1 << 2;
    pub const PERMISSION_WRITE: i32 = 1 << 4;
    pub const PERMISSION_WRITE_ENCRYPTED: i32 = 1 << 5;
    pub const PERMISSION_WRITE_ENCRYPTED_MITM: i32 = 1 << 6;
    pub const PERMISSION_WRITE_SIGNED: i32 = 1 << 7;
    pub const PERMISSION_WRITE_SIGNED_MITM: i32 = 1 << 8;

    pub(crate) fn new(
        uuid: Uuid128Bit,
        instance_id: i32,
        properties: i32,
@@ -748,7 +763,11 @@ pub struct BluetoothGattService {
}

impl BluetoothGattService {
    fn new(uuid: Uuid128Bit, instance_id: i32, service_type: i32) -> BluetoothGattService {
    pub(crate) fn new(
        uuid: Uuid128Bit,
        instance_id: i32,
        service_type: i32,
    ) -> BluetoothGattService {
        BluetoothGattService {
            uuid,
            instance_id,
@@ -1095,7 +1114,7 @@ pub trait IScannerCallback: RPCProxy {
#[derive(Debug, FromPrimitive, ToPrimitive)]
#[repr(u8)]
/// GATT write type.
enum GattDbElementType {
pub(crate) enum GattDbElementType {
    PrimaryService = 0,
    SecondaryService = 1,
    IncludedService = 2,
@@ -1103,6 +1122,12 @@ enum GattDbElementType {
    Descriptor = 4,
}

impl Into<i32> for GattDbElementType {
    fn into(self) -> i32 {
        self.to_u8().unwrap_or(0).into()
    }
}

#[derive(Debug, FromPrimitive, ToPrimitive, Copy, Clone)]
#[repr(u8)]
/// GATT write type.
@@ -1728,10 +1753,10 @@ impl BluetoothGatt {
    /// Start an active scan on given scanner id. This will look up and assign
    /// the correct ScanSettings for it as well.
    pub(crate) fn start_active_scan(&mut self, scanner_id: u8) -> BtStatus {
        let interval: u16 = sysprop::get_i32(sysprop::Property::LeInquiryScanInterval)
        let interval: u16 = sysprop::get_i32(sysprop::PropertyI32::LeInquiryScanInterval)
            .try_into()
            .expect("Bad value configured for LeInquiryScanInterval");
        let window: u16 = sysprop::get_i32(sysprop::Property::LeInquiryScanWindow)
        let window: u16 = sysprop::get_i32(sysprop::PropertyI32::LeInquiryScanWindow)
            .try_into()
            .expect("Bad value configured for LeInquiryScanWindow");

+268 −0
Original line number Diff line number Diff line
//! TODO(b/277818879) - Temporary DIS implementation

use log;
use std::sync::{Arc, Mutex};
use tokio::sync::mpsc::Sender;

use crate::bluetooth_gatt::{
    BluetoothGatt, BluetoothGattCharacteristic, BluetoothGattService, GattDbElementType,
    IBluetoothGatt, IBluetoothGattServerCallback,
};
use crate::uuid::{Profile, UuidHelper};
use crate::{Message, RPCProxy};
use bt_topshim::profiles::gatt::{GattStatus, LePhy};
use bt_topshim::sysprop;

/// Random uuid generated for registering against gatt server.
const DIS_APP_RANDOM_UUID: &str = "1b518948-fd77-4459-906f-4923104bb639";

/// UUID for PNP ID characteristic.
const PNP_ID_CHAR_UUID: &str = "00002A50-0000-1000-8000-00805F9B34FB";

/// Handles exporting the Device Information Service (DIS).
pub struct DeviceInformation {
    /// Reference to Gatt server implementation to export service.
    bluetooth_gatt: Arc<Mutex<Box<BluetoothGatt>>>,

    /// Server id (available once we are registered).
    gatt_server_id: Option<i32>,

    /// Handle for the PNP ID characteristic.
    pnp_id_handle: Option<i32>,

    /// Sender for stack mainloop.
    tx: Sender<Message>,
}

impl DeviceInformation {
    pub fn new(bluetooth_gatt: Arc<Mutex<Box<BluetoothGatt>>>, tx: Sender<Message>) -> Self {
        Self { bluetooth_gatt, gatt_server_id: None, pnp_id_handle: None, tx }
    }

    pub(crate) fn initialize(&mut self) {
        let callback = Box::new(DeviceInformationServerCallbacks::new(self.tx.clone()));

        // First register for callbacks with the server.
        self.bluetooth_gatt.lock().unwrap().register_server(
            DIS_APP_RANDOM_UUID.to_string(),
            callback,
            /*eatt_support=*/ true,
        );
    }

    pub(crate) fn handle_callbacks(&mut self, callback: &ServiceCallbacks) {
        match callback {
            ServiceCallbacks::Registered(status, server_id) => {
                if status != &GattStatus::Success {
                    log::error!("DIS failed to register callbacks. Status={:?}", status);
                    return;
                }

                self.gatt_server_id = Some(*server_id);

                // Construct and add Device Information service.
                let mut service = BluetoothGattService::new(
                    UuidHelper::get_profile_uuid(&Profile::Dis)
                        .expect("DIS uuid mapping missing")
                        .clone(),
                    /*instance_id=*/ 0,
                    GattDbElementType::PrimaryService.into(),
                );

                service.characteristics.push(BluetoothGattCharacteristic::new(
                    UuidHelper::from_string(PNP_ID_CHAR_UUID).expect("PNP ID uuid is malformed"),
                    /*instance_id=*/ 0,
                    BluetoothGattCharacteristic::PROPERTY_READ,
                    BluetoothGattCharacteristic::PERMISSION_READ,
                ));

                self.bluetooth_gatt.lock().unwrap().add_service(*server_id, service);
            }

            ServiceCallbacks::ServiceAdded(status, service) => {
                if status != &GattStatus::Success {
                    return;
                }

                let pnp_uuid =
                    UuidHelper::from_string(PNP_ID_CHAR_UUID).expect("PNP ID uuid is malformed");

                // Find the PNP ID characteristic we inserted before and store
                // the handle for it.
                for characteristic in &service.characteristics {
                    if characteristic.uuid == pnp_uuid {
                        self.pnp_id_handle = Some(characteristic.instance_id);
                    }
                }
            }
            ServiceCallbacks::OnCharacteristicReadRequest(
                addr,
                trans_id,
                offset,
                _is_long,
                handle,
            ) => match (self.gatt_server_id, self.pnp_id_handle) {
                (Some(server_id), Some(pnp_handle)) => {
                    if &pnp_handle == handle {
                        let vendor_id = sysprop::get_i32(sysprop::PropertyI32::VendorId);
                        let vendor_id_source =
                            sysprop::get_i32(sysprop::PropertyI32::VendorIdSource);
                        let product_id = sysprop::get_i32(sysprop::PropertyI32::ProductId);
                        let product_version =
                            sysprop::get_i32(sysprop::PropertyI32::ProductVersion);

                        // PNP ID ordering (all values are in little endian):
                        // - Vendor ID source (1 octet)
                        // - Vendor ID (2 octet)
                        // - Product ID (2 octet)
                        // - Product Version (2 octet)
                        let mut value: Vec<u8> = Vec::new();
                        value.push(vendor_id_source.to_le_bytes()[0]);
                        value.extend_from_slice(&vendor_id.to_le_bytes()[0..2]);
                        value.extend_from_slice(&product_id.to_le_bytes()[0..2]);
                        value.extend_from_slice(&product_version.to_le_bytes()[0..2]);

                        self.bluetooth_gatt.lock().unwrap().send_response(
                            server_id,
                            addr.clone(),
                            *trans_id,
                            GattStatus::Success,
                            *offset,
                            value,
                        );
                    }
                }

                (_, _) => (),
            },
        }
    }
}

// Callbacks we need to handle for DIS.
pub enum ServiceCallbacks {
    Registered(GattStatus, i32),
    ServiceAdded(GattStatus, BluetoothGattService),
    OnCharacteristicReadRequest(String, i32, i32, bool, i32),
}

// Handle callbacks for DIS to register
struct DeviceInformationServerCallbacks {
    // Sender to the main loop
    tx: Sender<Message>,
}

impl DeviceInformationServerCallbacks {
    fn new(tx: Sender<Message>) -> Self {
        Self { tx }
    }
}

impl IBluetoothGattServerCallback for DeviceInformationServerCallbacks {
    fn on_server_registered(&mut self, status: GattStatus, server_id: i32) {
        let txl = self.tx.clone();
        tokio::spawn(async move {
            let _ = txl.send(Message::Dis(ServiceCallbacks::Registered(status, server_id))).await;
        });
    }

    fn on_service_added(&mut self, status: GattStatus, service: BluetoothGattService) {
        let txl = self.tx.clone();
        tokio::spawn(async move {
            let _ = txl.send(Message::Dis(ServiceCallbacks::ServiceAdded(status, service))).await;
        });
    }

    fn on_characteristic_read_request(
        &mut self,
        addr: String,
        trans_id: i32,
        offset: i32,
        is_long: bool,
        handle: i32,
    ) {
        let txl = self.tx.clone();
        tokio::spawn(async move {
            let _ = txl
                .send(Message::Dis(ServiceCallbacks::OnCharacteristicReadRequest(
                    addr, trans_id, offset, is_long, handle,
                )))
                .await;
        });
    }

    // Remaining callbacks are unhandled

    fn on_service_removed(&mut self, _status: GattStatus, _handle: i32) {}
    fn on_server_connection_state(&mut self, _server_id: i32, _connected: bool, _addr: String) {}
    fn on_descriptor_read_request(
        &mut self,
        _addr: String,
        _trans_id: i32,
        _offset: i32,
        _is_long: bool,
        _handle: i32,
    ) {
    }
    fn on_characteristic_write_request(
        &mut self,
        _addr: String,
        _trans_id: i32,
        _offset: i32,
        _len: i32,
        _is_prep: bool,
        _need_rsp: bool,
        _handle: i32,
        _value: Vec<u8>,
    ) {
    }
    fn on_descriptor_write_request(
        &mut self,
        _addr: String,
        _trans_id: i32,
        _offset: i32,
        _len: i32,
        _is_prep: bool,
        _need_rsp: bool,
        _handle: i32,
        _value: Vec<u8>,
    ) {
    }
    fn on_execute_write(&mut self, _addr: String, _trans_id: i32, _exec_write: bool) {}
    fn on_notification_sent(&mut self, _addr: String, _status: GattStatus) {}
    fn on_mtu_changed(&mut self, _addr: String, _mtu: i32) {}
    fn on_phy_update(
        &mut self,
        _addr: String,
        _tx_phy: LePhy,
        _rx_phy: LePhy,
        _status: GattStatus,
    ) {
    }
    fn on_phy_read(&mut self, _addr: String, _tx_phy: LePhy, _rx_phy: LePhy, _status: GattStatus) {}
    fn on_connection_updated(
        &mut self,
        _addr: String,
        _interval: i32,
        _latency: i32,
        _timeout: i32,
        _status: GattStatus,
    ) {
    }
    fn on_subrate_change(
        &mut self,
        _addr: String,
        _subrate_factor: i32,
        _latency: i32,
        _cont_num: i32,
        _timeout: i32,
        _status: GattStatus,
    ) {
    }
}

impl RPCProxy for DeviceInformationServerCallbacks {
    fn get_object_id(&self) -> String {
        "DIS Gatt Server Callback".to_string()
    }
}
+20 −0
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@ pub mod bluetooth_logging;
pub mod bluetooth_media;
pub mod bluetooth_qa;
pub mod callbacks;
pub mod dis;
pub mod socket_manager;
pub mod suspend;
pub mod uuid;
@@ -38,6 +39,7 @@ use crate::bluetooth_gatt::{
    dispatch_le_scanner_callbacks, dispatch_le_scanner_inband_callbacks, BluetoothGatt,
};
use crate::bluetooth_media::{BluetoothMedia, MediaActions};
use crate::dis::{DeviceInformation, ServiceCallbacks};
use crate::socket_manager::{BluetoothSocketManager, SocketActions};
use crate::suspend::Suspend;
use bt_topshim::{
@@ -55,6 +57,9 @@ pub enum Message {
    // Shuts down the stack.
    Shutdown,

    // Adapter is enabled and ready.
    AdapterReady,

    // Callbacks from libbluetooth
    A2dp(A2dpCallbacks),
    Avrcp(AvrcpCallbacks),
@@ -115,6 +120,9 @@ pub enum Message {
    // Admin policy related
    AdminCallbackDisconnected(u32),
    HidHostEnable,

    // Dis callbacks
    Dis(ServiceCallbacks),
}

/// Represents suspend mode of a module.
@@ -150,6 +158,7 @@ impl Stack {
        suspend: Arc<Mutex<Box<Suspend>>>,
        bluetooth_socketmgr: Arc<Mutex<Box<BluetoothSocketManager>>>,
        bluetooth_admin: Arc<Mutex<Box<BluetoothAdmin>>>,
        bluetooth_dis: Arc<Mutex<Box<DeviceInformation>>>,
    ) {
        loop {
            let m = rx.recv().await;
@@ -164,6 +173,14 @@ impl Stack {
                    bluetooth.lock().unwrap().disable();
                }

                Message::AdapterReady => {
                    // Initialize objects that need the adapter to be fully
                    // enabled before running.

                    // Register device information service.
                    bluetooth_dis.lock().unwrap().initialize();
                }

                Message::A2dp(a) => {
                    bluetooth_media.lock().unwrap().dispatch_a2dp_callbacks(a);
                }
@@ -322,6 +339,9 @@ impl Stack {
                Message::HidHostEnable => {
                    bluetooth.lock().unwrap().enable_hidhost();
                }
                Message::Dis(callback) => {
                    bluetooth_dis.lock().unwrap().handle_callbacks(&callback);
                }
            }
        }
    }
Loading