server/
checked_conn.rs

1// SPDX-FileCopyrightText: 2023 Foundation Devices, Inc. <hello@foundation.xyz>
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4use std::sync::Arc;
5
6use crate::{
7    archive_event_handler, lend_mut, scalar_async_response_handler, scalar_event_handler, send_archive,
8    send_archive_nowait, send_blocking_archive, send_blocking_scalar, send_move, send_move_nowait,
9    send_scalar, send_scalar_async, send_scalar_nowait, subscribe_archive, subscribe_scalar,
10    try_send_blocking_archive, try_send_blocking_scalar, try_send_move, try_send_scalar,
11    try_send_scalar_async, Archive, ArchiveEventHandler, ArchiveSubscription, BlockingArchive,
12    BlockingScalar, BlockingScalarResponseHandler, LendMut, Move, Scalar, ScalarEventHandler,
13    ScalarSubscription, Server, ServerContext,
14};
15
16/// A typed connection to a running KeyOS server.
17///
18/// `CheckedConn<P>` is the lower-level message sender used by the public API
19/// crates. Most application code should call a crate-specific wrapper such as
20/// `BluetoothApi::enable_ble` or `SettingsApi::get_locale` instead of sending
21/// raw message values directly. When adding a wrapper method, choose the send
22/// method that matches the message trait implemented by `#[derive(Message)]`:
23///
24/// | Message trait | Send method | Use when |
25/// | --- | --- | --- |
26/// | [`BlockingScalar`] | [`send_blocking_scalar`](Self::send_blocking_scalar) or [`try_send_blocking_scalar`](Self::try_send_blocking_scalar) | The message fits in scalar registers and returns a response. |
27/// | [`Scalar`] | [`send_scalar`](Self::send_scalar), [`try_send_scalar`](Self::try_send_scalar), or [`send_scalar_nowait`](Self::send_scalar_nowait) | The message fits in scalar registers and has no response. |
28/// | [`BlockingArchive`] | [`send_blocking_archive`](Self::send_blocking_archive) or [`try_send_blocking_archive`](Self::try_send_blocking_archive) | The message is serialized with `rkyv` and returns a response. |
29/// | [`Archive`] | [`send_archive`](Self::send_archive), [`try_send_archive`](Self::try_send_archive), or [`send_archive_nowait`](Self::send_archive_nowait) | The message is serialized with `rkyv` and has no response. |
30/// | [`LendMut`] | [`lend_mut`](Self::lend_mut) | The caller lends a mutable memory range to the server and waits for the server to finish with it. |
31/// | [`Move`] | [`send_move`](Self::send_move), [`try_send_move`](Self::try_send_move), or [`send_move_nowait`](Self::send_move_nowait) | The caller transfers ownership of a memory range to the server. |
32/// | [`ScalarSubscription`] / [`ArchiveSubscription`] | [`subscribe_scalar`](Self::subscribe_scalar) or [`subscribe_archive`](Self::subscribe_archive) | The caller registers its [`ServerContext`] to receive future events. |
33///
34/// For normal system API wrappers, prefer the infallible methods when the target
35/// service is mandatory: delivery failure means the system service has crashed or
36/// disconnected, and the device should reboot rather than route unreachable
37/// error handling through every caller. Use `try_*` methods when the API
38/// intentionally exposes optional service availability, caller-recoverable
39/// transport failure, or explicit queue-full handling.
40#[derive(Clone)]
41pub struct CheckedConn<T: CheckedPermissions> {
42    cid: Arc<DisconnectOnDrop>,
43    _phantom: core::marker::PhantomData<fn() -> T>,
44}
45
46/// Marker trait for the server name and compile-time permissions attached to a
47/// connection.
48///
49/// Client crates normally get an implementation from `#[derive(Permissions)]`
50/// via the API crate's `use_api!` macro. The derived implementation also emits
51/// [`MessageAllowed<M>`] implementations for every message granted to the
52/// caller by the API manifest.
53pub trait CheckedPermissions: Clone + Default + 'static {
54    const NAME: &str;
55}
56
57/// Compile-time proof that permissions type `P` may send message `M`.
58///
59/// API wrapper methods express their permission needs with bounds like
60/// `P: MessageAllowed<GetStatus>`. If a call fails to compile because this
61/// bound is not satisfied, grant that message in the app's manifest. Custom
62/// permission types only satisfy compile-time bounds; `xous-names` still
63/// enforces the manifest at runtime. Hand-written permissions are only valid for
64/// infrastructure paths that connect to their own server.
65pub trait MessageAllowed<M> {}
66
67impl<P: CheckedPermissions> std::fmt::Debug for CheckedConn<P> {
68    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
69        f.debug_struct("CheckedConn")
70            .field("M", &core::any::type_name::<P>())
71            .field("CID", &self.cid.0)
72            .finish()
73    }
74}
75
76impl<P: CheckedPermissions> Default for CheckedConn<P> {
77    fn default() -> Self {
78        let names = xous_names::XousNames::new().unwrap();
79        names.request_connection_blocking(P::NAME).unwrap().into()
80    }
81}
82
83#[derive(Default, Clone)]
84pub struct WithAllPermissions<P: CheckedPermissions> {
85    _phantom: core::marker::PhantomData<fn() -> P>,
86}
87
88impl<P: CheckedPermissions> CheckedPermissions for WithAllPermissions<P> {
89    const NAME: &str = P::NAME;
90}
91
92impl<P: CheckedPermissions, M> MessageAllowed<M> for WithAllPermissions<P> {}
93
94#[derive(Debug, Default, Clone)]
95pub struct AllPermissions;
96
97impl CheckedPermissions for AllPermissions {
98    const NAME: &str = "";
99}
100
101impl<T> MessageAllowed<T> for AllPermissions {}
102
103impl<P: CheckedPermissions> CheckedConn<P> {
104    // ==================== Utility Methods ====================
105
106    /// Open a connection to the server based on the server name.
107    pub fn try_connect() -> Option<Self> {
108        let names = xous_names::XousNames::new().unwrap();
109        names.request_connection(P::NAME).map(Into::into).ok()
110    }
111
112    pub fn try_connect_with_timeout(timeout: std::time::Duration) -> Option<Self> {
113        let started = std::time::Instant::now();
114        loop {
115            if let Some(conn) = Self::try_connect() {
116                return Some(conn);
117            }
118
119            if started.elapsed() >= timeout {
120                return None;
121            }
122
123            std::thread::sleep(std::time::Duration::from_millis(100));
124        }
125    }
126
127    /// Get the remote process ID.
128    pub fn get_remote_pid(&self) -> xous::PID { xous::get_remote_pid(self.cid.0).unwrap() }
129
130    /// Get a version of this connection that does not do compile-time
131    /// permission checking.
132    ///
133    /// Use this only for infrastructure code that has already enforced
134    /// permissions another way. Normal API wrappers should keep their
135    /// `P: MessageAllowed<M>` bounds so missing permissions fail at compile
136    /// time.
137    pub fn unchecked(&self) -> CheckedConn<WithAllPermissions<P>> {
138        CheckedConn { cid: self.cid.clone(), _phantom: Default::default() }
139    }
140
141    // ==================== BlockingScalar Messages ====================
142
143    /// Send a [`BlockingScalar`] message and wait for its response.
144    ///
145    /// Panics if the message cannot be delivered.
146    ///
147    /// Warning: Cannot be used in an IRQ handler context.
148    pub fn send_blocking_scalar<M>(&self, msg: M) -> M::Response
149    where
150        M: BlockingScalar,
151        P: MessageAllowed<M>,
152    {
153        send_blocking_scalar(self.cid.0, msg)
154    }
155
156    /// Send a [`BlockingScalar`] message and wait for its response.
157    ///
158    /// Returns the underlying transport error if the message cannot be
159    /// delivered.
160    ///
161    /// Warning: Cannot be used in an IRQ handler context.
162    pub fn try_send_blocking_scalar<M>(&self, msg: M) -> Result<M::Response, xous::Error>
163    where
164        M: BlockingScalar,
165        P: MessageAllowed<M>,
166    {
167        try_send_blocking_scalar(self.cid.0, msg).map_err(|e| e.into_inner())
168    }
169
170    /// Send a [`BlockingScalar`] message and handle its response later on the
171    /// supplied [`ServerContext`].
172    pub fn send_scalar_async<M, SR>(&self, msg: M, context: &mut ServerContext<SR>)
173    where
174        M: BlockingScalar,
175        P: MessageAllowed<M>,
176        SR: BlockingScalarResponseHandler<M::Response>,
177    {
178        let msg_id = send_scalar_async(self.cid.0, msg, context.sid);
179        context.handlers.push((msg_id, scalar_async_response_handler::<M, SR>));
180    }
181
182    /// Send a [`BlockingScalar`] message asynchronously, returning the transport
183    /// error if the message cannot be queued.
184    pub fn try_send_scalar_async<M, SR>(
185        &self,
186        msg: M,
187        context: &mut ServerContext<SR>,
188    ) -> Result<(), xous::Error>
189    where
190        M: BlockingScalar,
191        P: MessageAllowed<M>,
192        SR: BlockingScalarResponseHandler<M::Response>,
193    {
194        let msg_id =
195            try_send_scalar_async(self.cid.0, msg, context.sid).map_err(|e| e.into_inner().into_xous())?;
196        context.handlers.push((msg_id, scalar_async_response_handler::<M, SR>));
197        Ok(())
198    }
199
200    // ==================== Scalar Messages (fire-and-forget) ====================
201    //
202
203    /// Send a fire-and-forget [`Scalar`] message.
204    ///
205    /// Blocks if the message queue is full and panics if the message cannot be
206    /// delivered.
207    ///
208    /// Warning: Cannot be used in an IRQ handler context.
209    pub fn send_scalar<M>(&self, msg: M)
210    where
211        M: Scalar,
212        P: MessageAllowed<M>,
213    {
214        send_scalar(self.cid.0, msg)
215    }
216
217    /// Send a fire-and-forget [`Scalar`] message.
218    ///
219    /// Blocks if the message queue is full and returns the delivery error if
220    /// the message cannot be delivered.
221    ///
222    /// Warning: Cannot be used in an IRQ handler context.
223    pub fn try_send_scalar<M>(&self, msg: M) -> Result<(), xous::Error>
224    where
225        M: Scalar,
226        P: MessageAllowed<M>,
227    {
228        try_send_scalar(self.cid.0, msg).map_err(|e| e.into_inner())
229    }
230
231    /// Send a fire-and-forget [`Scalar`] message without waiting for queue
232    /// space.
233    ///
234    /// Returns an error if the queue is full or delivery otherwise fails.
235    /// Can be used in an IRQ handler context.
236    pub fn send_scalar_nowait<M>(&self, msg: M) -> Result<(), xous::Error>
237    where
238        M: Scalar,
239        P: MessageAllowed<M>,
240    {
241        send_scalar_nowait(self.cid.0, msg).map_err(|e| e.into_inner())
242    }
243
244    // ==================== Archive Messages ====================
245
246    /// Send a [`BlockingArchive`] message and wait for its response.
247    ///
248    /// Panics if the message cannot be delivered.
249    pub fn send_blocking_archive<M>(&self, msg: M) -> M::Response
250    where
251        M: BlockingArchive,
252        P: MessageAllowed<M>,
253    {
254        send_blocking_archive(self.cid.0, msg)
255    }
256
257    /// Send a [`BlockingArchive`] message and wait for its response.
258    ///
259    /// Returns the underlying transport error if the message cannot be
260    /// delivered or decoded.
261    pub fn try_send_blocking_archive<M>(&self, msg: M) -> Result<M::Response, xous::Error>
262    where
263        M: BlockingArchive,
264        P: MessageAllowed<M>,
265    {
266        try_send_blocking_archive(self.cid.0, msg).map_err(|e| e.into_inner().into_xous())
267    }
268
269    // ==================== Archive Messages (fire-and-forget) ====================
270
271    /// Send a fire-and-forget [`Archive`] message.
272    ///
273    /// Blocks if the message queue is full and returns the delivery error if
274    /// the message cannot be delivered.
275    ///
276    /// Warning: Cannot be used in an IRQ handler context.
277    pub fn try_send_archive<M>(&self, msg: M) -> Result<(), xous::Error>
278    where
279        M: Archive,
280        P: MessageAllowed<M>,
281    {
282        send_archive(self.cid.0, msg)
283    }
284
285    /// Send a fire-and-forget [`Archive`] message.
286    ///
287    /// Blocks if the message queue is full and panics if the message cannot be
288    /// delivered.
289    ///
290    /// Warning: Cannot be used in an IRQ handler context.
291    #[track_caller]
292    pub fn send_archive<M>(&self, msg: M)
293    where
294        M: Archive,
295        P: MessageAllowed<M>,
296    {
297        send_archive(self.cid.0, msg).unwrap()
298    }
299
300    /// Send a fire-and-forget [`Archive`] message without waiting for queue
301    /// space.
302    ///
303    /// Returns an error if the queue is full or delivery otherwise fails.
304    /// Can be used in an IRQ handler context.
305    pub fn send_archive_nowait<M>(&self, msg: M) -> Result<(), xous::Error>
306    where
307        M: Archive,
308        P: MessageAllowed<M>,
309    {
310        send_archive_nowait(self.cid.0, msg)
311    }
312
313    // ==================== LendMut Messages ====================
314
315    /// Send a [`LendMut`] message and wait for the server to finish with the
316    /// lent memory.
317    ///
318    /// Use this for APIs that pass a [`xous::MemoryRange`] to a server for
319    /// in-place reads or writes.
320    pub fn lend_mut<M>(&self, msg: M) -> M::Response
321    where
322        M: LendMut,
323        P: MessageAllowed<M>,
324    {
325        lend_mut(self.cid.0, msg)
326    }
327
328    // ==================== Move Messages ====================
329
330    /// Send a [`Move`] message, transferring ownership of its memory range.
331    ///
332    /// Blocks if the message queue is full and panics if the message cannot be
333    /// delivered.
334    ///
335    /// Warning: Cannot be used in an IRQ handler context.
336    pub fn send_move<M>(&self, msg: M)
337    where
338        M: Move,
339        P: MessageAllowed<M>,
340    {
341        send_move(self.cid.0, msg)
342    }
343
344    /// Send a [`Move`] message, transferring ownership of its memory range.
345    ///
346    /// Blocks if the message queue is full and returns the delivery error if
347    /// the message cannot be delivered.
348    ///
349    /// Warning: Cannot be used in an IRQ handler context.
350    pub fn try_send_move<M>(&self, msg: M) -> Result<(), xous::Error>
351    where
352        M: Move,
353        P: MessageAllowed<M>,
354    {
355        try_send_move(self.cid.0, msg).map_err(|e| e.into_inner())
356    }
357
358    /// Send a [`Move`] message without waiting for queue space.
359    ///
360    /// Returns an error if the queue is full or delivery otherwise fails.
361    /// Can be used in an IRQ handler context.
362    pub fn send_move_nowait<M>(&self, msg: M) -> Result<(), xous::Error>
363    where
364        M: Move,
365        P: MessageAllowed<M>,
366    {
367        send_move_nowait(self.cid.0, msg).map_err(|e| e.into_inner())
368    }
369
370    // ==================== Subscriptions ====================
371
372    /// Subscribe to archive events.
373    pub fn subscribe_archive<M, SR>(&self, msg: M, context: &mut ServerContext<SR>) -> Result<(), M::Error>
374    where
375        M: ArchiveSubscription + 'static,
376        P: MessageAllowed<M>,
377        SR: ArchiveEventHandler<M::Event>,
378    {
379        match subscribe_archive::<M>(self.cid.0, msg, context.sid) {
380            Ok((msg_id, cancel_msg_id)) => {
381                context.handlers.push((msg_id, archive_event_handler::<M::Event, SR>));
382                context.handlers.push((cancel_msg_id, cancellation_handler::<SR>));
383                Ok(())
384            }
385            Err(e) => Err(e),
386        }
387    }
388
389    /// Subscribe to archive events (infallible version).
390    pub fn subscribe_archive_infallible<M, SR>(&self, msg: M, context: &mut ServerContext<SR>)
391    where
392        M: ArchiveSubscription<Error = crate::Infallible> + 'static,
393        P: MessageAllowed<M>,
394        SR: ArchiveEventHandler<M::Event>,
395    {
396        self.subscribe_archive::<M, SR>(msg, context).unwrap()
397    }
398
399    /// Subscribe to scalar events.
400    pub fn subscribe_scalar<M, SR>(&self, msg: M, context: &mut ServerContext<SR>) -> Result<(), M::Error>
401    where
402        M: ScalarSubscription + 'static,
403        P: MessageAllowed<M>,
404        SR: ScalarEventHandler<M::Event>,
405    {
406        match subscribe_scalar::<M>(self.cid.0, msg, context.sid) {
407            Ok((msg_id, cancel_msg_id)) => {
408                context.handlers.push((msg_id, scalar_event_handler::<M::Event, SR>));
409                context.handlers.push((cancel_msg_id, cancellation_handler::<SR>));
410                Ok(())
411            }
412            Err(e) => Err(e),
413        }
414    }
415
416    /// Subscribe to scalar events (infallible version).
417    pub fn subscribe_scalar_infallible<M, SR>(&self, msg: M, context: &mut ServerContext<SR>)
418    where
419        M: ScalarSubscription<Error = crate::Infallible> + 'static,
420        P: MessageAllowed<M>,
421        SR: ScalarEventHandler<M::Event>,
422    {
423        self.subscribe_scalar::<M, SR>(msg, context).unwrap()
424    }
425}
426
427impl<P: CheckedPermissions> From<xous::CID> for CheckedConn<P> {
428    fn from(cid: xous::CID) -> Self {
429        Self { cid: Arc::new(DisconnectOnDrop(cid)), _phantom: Default::default() }
430    }
431}
432
433fn cancellation_handler<SR: Server>(
434    _handler: &mut SR,
435    raw: xous::MessageEnvelope,
436    context: &mut ServerContext<SR>,
437) {
438    if let Ok((msg_id, cancel_msg_id)) = crate::event::extract_cancellation_message(&raw.body) {
439        context.handlers.retain(|(id, _)| *id != msg_id && *id != cancel_msg_id);
440    }
441}
442
443struct DisconnectOnDrop(xous::CID);
444
445impl Drop for DisconnectOnDrop {
446    fn drop(&mut self) {
447        if let Err(e) = xous::disconnect(self.0) {
448            log::error!("Disconnect failed: {e:?}");
449        }
450    }
451}