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

Commit f6752409 authored by Sonny Sasaka's avatar Sonny Sasaka
Browse files

floss: Implement IBluetoothGatt ClientDisconnect

Bug: 193685325
Tag: #floss
Test: manual - Build floss on Linux and use WIP btclient

Change-Id: I623e129af3939c540fe101d4cc0b067bec0d94a8
parent e9fa0024
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -27,6 +27,16 @@ struct BluetoothGattCallbackDBus {}
impl IBluetoothGattCallback for BluetoothGattCallbackDBus {
    #[dbus_method("OnClientRegistered")]
    fn on_client_registered(&self, _status: i32, _scanner_id: i32) {}

    #[dbus_method("OnClientConnectionState")]
    fn on_client_connection_state(
        &self,
        status: i32,
        client_id: i32,
        connected: bool,
        addr: String,
    ) {
    }
}

#[allow(dead_code)]
@@ -97,4 +107,7 @@ impl IBluetoothGatt for IBluetoothGattDBus {
        phy: i32,
    ) {
    }

    #[dbus_method("ClientDisconnect")]
    fn client_disconnect(&self, client_id: i32, addr: String) {}
}
+109 −5
Original line number Diff line number Diff line
@@ -6,9 +6,12 @@ use bt_topshim::bindings::root::bluetooth::Uuid;
use bt_topshim::btif::{BluetoothInterface, RawAddress};
use bt_topshim::profiles::gatt::{
    Gatt, GattClientCallbacks, GattClientCallbacksDispatcher, GattServerCallbacksDispatcher,
    GattStatus,
};
use bt_topshim::topstack;

use num_traits::cast::FromPrimitive;

use std::sync::{Arc, Mutex};

use tokio::sync::mpsc::Sender;
@@ -21,21 +24,32 @@ struct Client {
    callback: Box<dyn IBluetoothGattCallback + Send>,
}

struct Connection {
    conn_id: i32,
    address: String,
    client_id: i32,
}

struct ContextMap {
    // TODO(b/196635530): Consider using `multimap` for a more efficient implementation of get by
    // multiple keys.
    clients: Vec<Client>,
    connections: Vec<Connection>,
}

impl ContextMap {
    fn new() -> ContextMap {
        ContextMap { clients: vec![] }
        ContextMap { clients: vec![], connections: vec![] }
    }

    fn get_by_uuid(&self, uuid: &Uuid128Bit) -> Option<&Client> {
        self.clients.iter().find(|client| client.uuid == *uuid)
    }

    fn get_by_client_id(&self, client_id: i32) -> Option<&Client> {
        self.clients.iter().find(|client| client.id.is_some() && client.id.unwrap() == client_id)
    }

    fn add(&mut self, uuid: &Uuid128Bit, callback: Box<dyn IBluetoothGattCallback + Send>) {
        if self.get_by_uuid(uuid).is_some() {
            return;
@@ -56,6 +70,25 @@ impl ContextMap {

        client.unwrap().id = Some(id);
    }

    fn add_connection(&mut self, client_id: i32, conn_id: i32, address: &String) {
        if self.get_conn_id_from_address(client_id, address).is_some() {
            return;
        }

        self.connections.push(Connection { conn_id, address: address.clone(), client_id });
    }

    fn get_conn_id_from_address(&self, client_id: i32, address: &String) -> Option<i32> {
        match self
            .connections
            .iter()
            .find(|conn| conn.client_id == client_id && conn.address == *address)
        {
            None => None,
            Some(conn) => Some(conn.conn_id),
        }
    }
}

/// Defines the GATT API.
@@ -88,12 +121,24 @@ pub trait IBluetoothGatt {
        opportunistic: bool,
        phy: i32,
    );

    /// Disconnects a GATT connection.
    fn client_disconnect(&self, client_id: i32, addr: String);
}

/// Callback for GATT Client API.
pub trait IBluetoothGattCallback: RPCProxy {
    /// When the `register_client` request is done.
    fn on_client_registered(&self, status: i32, client_id: i32);

    /// When there is a change in the state of a GATT client connection.
    fn on_client_connection_state(
        &self,
        status: i32,
        client_id: i32,
        connected: bool,
        addr: String,
    );
}

/// Interface for scanner callbacks to clients, passed to `IBluetoothGatt::register_scanner`.
@@ -252,6 +297,19 @@ impl IBluetoothGatt for BluetoothGatt {
            phy,
        );
    }

    fn client_disconnect(&self, client_id: i32, address: String) {
        let conn_id = self.context_map.get_conn_id_from_address(client_id, &address);
        if conn_id.is_none() {
            return;
        }

        self.gatt.as_ref().unwrap().client.disconnect(
            client_id,
            &RawAddress::from_string(address).unwrap(),
            conn_id.unwrap(),
        );
    }
}

#[btif_callbacks_dispatcher(BluetoothGatt, dispatch_gatt_client_callbacks, GattClientCallbacks)]
@@ -279,8 +337,25 @@ impl BtifGattClientCallbacks for BluetoothGatt {
        callback.on_client_registered(status, client_id);
    }

    fn connect_cb(&mut self, _conn_id: i32, _status: i32, _client_id: i32, _addr: RawAddress) {
        // TODO(b/193685325): handle;
    fn connect_cb(&mut self, conn_id: i32, status: i32, client_id: i32, addr: RawAddress) {
        if status == 0 {
            self.context_map.add_connection(client_id, conn_id, &addr.to_string());
        }

        let client = self.context_map.get_by_client_id(client_id);
        if client.is_none() {
            return;
        }

        client.unwrap().callback.on_client_connection_state(
            status,
            client_id,
            match GattStatus::from_i32(status) {
                None => false,
                Some(gatt_status) => gatt_status == GattStatus::Success,
            },
            addr.to_string(),
        );
    }
}

@@ -298,6 +373,14 @@ mod tests {

    impl IBluetoothGattCallback for TestBluetoothGattCallback {
        fn on_client_registered(&self, _status: i32, _client_id: i32) {}
        fn on_client_connection_state(
            &self,
            _status: i32,
            _client_id: i32,
            _connected: bool,
            _addr: String,
        ) {
        }
    }

    impl RPCProxy for TestBluetoothGattCallback {
@@ -325,7 +408,7 @@ mod tests {
    }

    #[test]
    fn test_context_map() {
    fn test_context_map_clients() {
        let mut map = ContextMap::new();

        // Add client 1.
@@ -344,10 +427,31 @@ mod tests {
        assert!(found.is_some());
        assert_eq!("Callback 2", found.unwrap().callback.get_object_id());

        // Remove client 1.
        // Set client ID and get by client ID.
        map.set_client_id(&uuid1, 3);
        let found = map.get_by_client_id(3);
        assert!(found.is_some());

        // Remove client 1.
        map.remove(3);
        let found = map.get_by_uuid(&uuid1);
        assert!(found.is_none());
    }

    #[test]
    fn test_context_map_connections() {
        let mut map = ContextMap::new();
        let client_id = 1;

        map.add_connection(client_id, 3, &String::from("aa:bb:cc:dd:ee:ff"));
        map.add_connection(client_id, 4, &String::from("11:22:33:44:55:66"));

        let found = map.get_conn_id_from_address(client_id, &String::from("aa:bb:cc:dd:ee:ff"));
        assert!(found.is_some());
        assert_eq!(3, found.unwrap());

        let found = map.get_conn_id_from_address(client_id, &String::from("11:22:33:44:55:66"));
        assert!(found.is_some());
        assert_eq!(4, found.unwrap());
    }
}
+53 −0
Original line number Diff line number Diff line
@@ -34,6 +34,59 @@ impl std::fmt::Debug for BtGattNotifyParams {
    }
}

#[derive(Debug, FromPrimitive, PartialEq, PartialOrd)]
#[repr(u32)]
pub enum GattStatus {
    Success = 0x00,
    InvalidHandle = 0x01,
    ReadNotPermit = 0x02,
    WriteNotPermit = 0x03,
    InvalidPdu = 0x04,
    InsufAuthentication = 0x05,
    ReqNotSupported = 0x06,
    InvalidOffset = 0x07,
    InsufAuthorization = 0x08,
    PrepareQFull = 0x09,
    NotFound = 0x0a,
    NotLong = 0x0b,
    InsufKeySize = 0x0c,
    InvalidAttrLen = 0x0d,
    ErrUnlikely = 0x0e,
    InsufEncryption = 0x0f,
    UnsupportGrpType = 0x10,
    InsufResource = 0x11,
    DatabaseOutOfSync = 0x12,
    ValueNotAllowed = 0x13,
    IllegalParameter = 0x87,
    TooShort = 0x7f,
    NoResources = 0x80,
    InternalError = 0x81,
    WrongState = 0x82,
    DbFull = 0x83,
    Busy = 0x84,
    Error = 0x85,
    CmdStarted = 0x86,
    Pending = 0x88,
    AuthFail = 0x89,
    More = 0x8a,
    InvalidCfg = 0x8b,
    ServiceStarted = 0x8c,
    EncryptedNoMitm = 0x8d,
    NotEncrypted = 0x8e,
    Congested = 0x8f,
    DupReg = 0x90,      /* 0x90 */
    AlreadyOpen = 0x91, /* 0x91 */
    Cancel = 0x92,      /* 0x92 */
    /* = 0xE0 ~ 0xFC reserved for future use */

    /* Client Characteristic Configuration Descriptor Improperly Configured */
    CccCfgErr = 0xFD,
    /* Procedure Already in progress */
    PrcInProgress = 0xFE,
    /* Attribute value out of range */
    OutOfRange = 0xFF,
}

#[derive(Debug)]
pub enum GattClientCallbacks {
    RegisterClient(i32, i32, Uuid),