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

Commit ce7fd35b authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "floss: Add Device Information Service"

parents 6a8804d2 bd7608d5
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