app_manager/
lib.rs

1// SPDX-FileCopyrightText: 2025 Foundation Devices, Inc. <hello@foundation.xyz>
2// SPDX-License-Identifier: GPL-3.0-or-later
3pub use error::*;
4pub use messages::*;
5
6pub mod error;
7pub mod messages;
8
9use server::{CheckedConn, CheckedPermissions, MessageAllowed};
10use xous::{AppId, PID};
11
12#[macro_export]
13macro_rules! use_api {
14    () => {
15        mod app_manager_permissions {
16            use app_manager::messages::*;
17            #[derive(Clone, Default, server::Permissions)]
18            #[server_name = "os/app-manager"]
19            pub struct AppManagerPermissions;
20        }
21        type AppManagerApi = app_manager::AppManagerApi<app_manager_permissions::AppManagerPermissions>;
22    };
23}
24
25#[derive(Default)]
26pub struct AppManagerApi<P: CheckedPermissions>(pub(crate) CheckedConn<P>);
27
28impl<P: CheckedPermissions> AppManagerApi<P> {
29    pub fn launch_app_blocking(&self, app_id: &AppId) -> Result<PID, AppManagerError>
30    where
31        P: MessageAllowed<LaunchAppBlocking>,
32    {
33        self.0
34            .try_send_blocking_scalar(LaunchAppBlocking(*app_id))
35            .map_err(|_| AppManagerError::InternalError)?
36    }
37
38    pub fn launch_app(&self, app_id: &AppId) -> Result<(), xous::Error>
39    where
40        P: MessageAllowed<LaunchApp>,
41    {
42        self.0.try_send_scalar(LaunchApp(*app_id))?;
43        Ok(())
44    }
45
46    pub fn refresh_installed_apps(&self) -> Result<(), AppManagerError>
47    where
48        P: MessageAllowed<RefreshInstalledApps>,
49    {
50        self.0.try_send_blocking_scalar(RefreshInstalledApps).map_err(|_| AppManagerError::InternalError)?
51    }
52
53    pub fn app_name_by_app_id(&self, id: &AppId, locale: &str) -> Option<String>
54    where
55        P: MessageAllowed<GetAppName>,
56    {
57        self.0.send_blocking_archive(GetAppName::new_by_app_id(id, locale))
58    }
59
60    pub fn app_name_by_pid(&self, pid: PID, locale: &str) -> Option<String>
61    where
62        P: MessageAllowed<GetAppName>,
63    {
64        self.0.send_blocking_archive(GetAppName::new_by_pid(pid, locale))
65    }
66
67    pub fn get_qr_match_rules(&self) -> Vec<AppQrMatchRules>
68    where
69        P: MessageAllowed<GetQrMatchRules>,
70    {
71        self.0.send_blocking_archive(GetQrMatchRules)
72    }
73
74    /// List installed apps, optionally filtered. Pass `AppFilter::default()`
75    /// (or `AppFilter { is_flux: None }`) for "everything", `AppFilter::flux_only()`
76    /// for Flux child apps, etc.
77    pub fn list_apps(&self, locale: &str, filter: AppFilter) -> Vec<AppEntry>
78    where
79        P: MessageAllowed<ListApps>,
80    {
81        self.0.send_blocking_archive(ListApps { locale: locale.to_string(), filter })
82    }
83
84    /// Convenience wrapper around [`Self::list_apps`] for callers that only
85    /// want Flux-child apps (the original `list_flux_apps` use case).
86    pub fn list_flux_apps(&self, locale: &str) -> Vec<AppEntry>
87    where
88        P: MessageAllowed<ListApps>,
89    {
90        self.list_apps(locale, AppFilter::flux_only())
91    }
92
93    pub fn get_installed_apps(&self, locale: &str) -> Vec<InstalledAppInfo>
94    where
95        P: MessageAllowed<GetInstalledApps>,
96    {
97        self.0.send_blocking_archive(GetInstalledApps { locale: locale.to_string() })
98    }
99
100    pub fn get_app_icon(&self, app_id: &str) -> Option<Vec<u8>>
101    where
102        P: MessageAllowed<GetAppIcon>,
103    {
104        self.0.send_blocking_archive(GetAppIcon { app_id: app_id.to_string() })
105    }
106
107    pub fn get_third_party_certificates(&self) -> Vec<ThirdPartyCertificateInfo>
108    where
109        P: MessageAllowed<GetThirdPartyCertificates>,
110    {
111        self.0.send_blocking_archive(GetThirdPartyCertificates)
112    }
113
114    pub fn import_third_party_certificate(
115        &self,
116        certificate_pem: Vec<u8>,
117    ) -> Result<ImportThirdPartyCertificateResult, xous::Error>
118    where
119        P: MessageAllowed<ImportThirdPartyCertificate>,
120    {
121        self.0.try_send_blocking_archive(ImportThirdPartyCertificate { certificate_pem })
122    }
123
124    pub fn remove_third_party_certificate(
125        &self,
126        public_key: impl Into<String>,
127        locale: &str,
128    ) -> Result<RemoveThirdPartyCertificateResult, xous::Error>
129    where
130        P: MessageAllowed<RemoveThirdPartyCertificate>,
131    {
132        self.0.try_send_blocking_archive(RemoveThirdPartyCertificate {
133            public_key: public_key.into(),
134            locale: locale.to_string(),
135        })
136    }
137
138    pub fn remove_installed_app(&self, app_id: &AppId) -> Result<RemoveInstalledAppResult, xous::Error>
139    where
140        P: MessageAllowed<RemoveInstalledApp>,
141    {
142        self.0.try_send_blocking_archive(RemoveInstalledApp { app_id: *app_id })
143    }
144
145    /// Subscribe the calling server to app lifecycle events (launch/crash).
146    ///
147    /// The subscriber must implement `server::ArchiveEventHandler<AppEvent>`.
148    pub fn server_subscribe_app_events<S>(&self, context: &mut server::ServerContext<S>)
149    where
150        P: 'static,
151        P: MessageAllowed<SubscribeAppEvents>,
152        S: server::Server + server::ArchiveEventHandler<AppEvent>,
153    {
154        self.0.subscribe_archive_infallible(SubscribeAppEvents, context)
155    }
156}
157
158pub fn decode_app_id_str(id: &str) -> anyhow::Result<AppId> {
159    Ok(AppId(app_manifest::parse_app_id_bytes(id)?))
160}