summaryrefslogtreecommitdiff
path: root/system/rust/src/gatt/server.rs
blob: b7769f19dc57fc627a605d52b89f6c4d2f83a55d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
//! This module is a simple GATT server that shares the ATT channel with the
//! existing C++ GATT client.

mod att_database;
pub mod att_server_bearer;
pub mod gatt_database;
mod indication_handler;
mod request_handler;
pub mod services;
mod transactions;

mod command_handler;
pub mod isolation_manager;
#[cfg(test)]
mod test;

use std::collections::HashMap;
use std::rc::Rc;
use std::sync::{Arc, Mutex, MutexGuard};

use crate::core::shared_box::{SharedBox, WeakBox, WeakBoxRef};
use crate::gatt::server::gatt_database::GattDatabase;

use self::super::ids::ServerId;
use self::att_server_bearer::AttServerBearer;
use self::gatt_database::{AttDatabaseImpl, GattServiceWithHandle};
use self::isolation_manager::IsolationManager;
use self::services::register_builtin_services;

use super::callbacks::RawGattDatastore;
use super::channel::AttTransport;
use super::ids::{AdvertiserId, AttHandle, TransportIndex};
use anyhow::{anyhow, bail, Result};
use log::info;

pub use indication_handler::IndicationError;

#[allow(missing_docs)]
pub struct GattModule {
    connections: HashMap<TransportIndex, GattConnection>,
    databases: HashMap<ServerId, SharedBox<GattDatabase>>,
    transport: Rc<dyn AttTransport>,
    // NOTE: this is logically owned by the GattModule. We share it behind a Mutex just so we
    // can use it as part of the Arbiter. Once the Arbiter is removed, this should be owned
    // fully by the GattModule.
    isolation_manager: Arc<Mutex<IsolationManager>>,
}

struct GattConnection {
    bearer: SharedBox<AttServerBearer<AttDatabaseImpl>>,
    database: WeakBox<GattDatabase>,
}

impl GattModule {
    /// Constructor.
    pub fn new(
        transport: Rc<dyn AttTransport>,
        isolation_manager: Arc<Mutex<IsolationManager>>,
    ) -> Self {
        Self {
            connections: HashMap::new(),
            databases: HashMap::new(),
            transport,
            isolation_manager,
        }
    }

    /// Handle LE link connect
    pub fn on_le_connect(
        &mut self,
        tcb_idx: TransportIndex,
        advertiser_id: Option<AdvertiserId>,
    ) -> Result<()> {
        info!("connected on tcb_idx {tcb_idx:?}");
        self.isolation_manager.lock().unwrap().on_le_connect(tcb_idx, advertiser_id);

        let Some(server_id) = self.isolation_manager.lock().unwrap().get_server_id(tcb_idx) else {
            bail!("non-isolated servers are not yet supported (b/274945531)")
        };
        let database = self.databases.get(&server_id);
        let Some(database) = database else {
            bail!("got connection to {server_id:?} but this server does not exist!");
        };

        let transport = self.transport.clone();
        let bearer = SharedBox::new(AttServerBearer::new(
            database.get_att_database(tcb_idx),
            move |packet| transport.send_packet(tcb_idx, packet),
        ));
        database.on_bearer_ready(tcb_idx, bearer.as_ref());
        self.connections.insert(tcb_idx, GattConnection { bearer, database: database.downgrade() });
        Ok(())
    }

    /// Handle an LE link disconnect
    pub fn on_le_disconnect(&mut self, tcb_idx: TransportIndex) -> Result<()> {
        info!("disconnected conn_id {tcb_idx:?}");
        self.isolation_manager.lock().unwrap().on_le_disconnect(tcb_idx);
        let connection = self.connections.remove(&tcb_idx);
        let Some(connection) = connection else {
            bail!("got disconnection from {tcb_idx:?} but bearer does not exist");
        };
        drop(connection.bearer);
        connection.database.with(|db| db.map(|db| db.on_bearer_dropped(tcb_idx)));
        Ok(())
    }

    /// Register a new GATT service on a given server
    pub fn register_gatt_service(
        &mut self,
        server_id: ServerId,
        service: GattServiceWithHandle,
        datastore: impl RawGattDatastore + 'static,
    ) -> Result<()> {
        self.databases
            .get(&server_id)
            .ok_or_else(|| anyhow!("server {server_id:?} not opened"))?
            .add_service_with_handles(service, Rc::new(datastore))
    }

    /// Unregister an existing GATT service on a given server
    pub fn unregister_gatt_service(
        &mut self,
        server_id: ServerId,
        service_handle: AttHandle,
    ) -> Result<()> {
        self.databases
            .get(&server_id)
            .ok_or_else(|| anyhow!("server {server_id:?} not opened"))?
            .remove_service_at_handle(service_handle)
    }

    /// Open a GATT server
    pub fn open_gatt_server(&mut self, server_id: ServerId) -> Result<()> {
        let mut db = GattDatabase::new();
        register_builtin_services(&mut db)?;
        let old = self.databases.insert(server_id, db.into());
        if old.is_some() {
            bail!("GATT server {server_id:?} already exists but was re-opened, clobbering old value...")
        }
        Ok(())
    }

    /// Close a GATT server
    pub fn close_gatt_server(&mut self, server_id: ServerId) -> Result<()> {
        let old = self.databases.remove(&server_id);
        if old.is_none() {
            bail!("GATT server {server_id:?} did not exist")
        };

        if true {
            // Disable to always use private gatt for debugging
            self.isolation_manager.lock().unwrap().clear_server(server_id);
        }

        Ok(())
    }

    /// Get an ATT bearer for a particular connection
    pub fn get_bearer(
        &self,
        tcb_idx: TransportIndex,
    ) -> Option<WeakBoxRef<AttServerBearer<AttDatabaseImpl>>> {
        self.connections.get(&tcb_idx).map(|x| x.bearer.as_ref())
    }

    /// Get the IsolationManager to manage associations between servers + advertisers
    pub fn get_isolation_manager(&mut self) -> MutexGuard<'_, IsolationManager> {
        self.isolation_manager.lock().unwrap()
    }
}