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}