update/
lib.rs

1// SPDX-FileCopyrightText: 2023 Foundation Devices, Inc. <hello@foundation.xyz>
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4pub mod error;
5pub mod messages;
6
7pub use error::{DownloadError, Error};
8pub use messages::UpdateStatus;
9
10/// Minimum battery percentage required for firmware updates.
11pub const MIN_UPDATE_BATTERY_PERCENT: u8 = 20;
12
13#[macro_export]
14macro_rules! use_api {
15    () => {
16        mod update_permissions {
17            use update::messages::*;
18            #[derive(Clone, Default, server::Permissions)]
19            #[server_name = "os/update"]
20            pub struct UpdatePermissions;
21        }
22        type UpdateApi = update::UpdateApi<update_permissions::UpdatePermissions>;
23    };
24}
25
26#[derive(Clone, Debug, Default)]
27pub struct UpdateApi<P: server::CheckedPermissions> {
28    conn: server::CheckedConn<P>,
29}
30
31impl<P: server::CheckedPermissions> UpdateApi<P> {
32    pub fn subscribe_update<S>(&self, context: &mut server::ServerContext<S>)
33    where
34        S: server::Server + server::ArchiveEventHandler<messages::ProgressUpdate>,
35        P: server::MessageAllowed<messages::SubscribeUpdateProgress>,
36    {
37        self.conn.subscribe_archive_infallible(messages::SubscribeUpdateProgress, context)
38    }
39
40    /// Apply a series of successive updates to the device, sending progress/error updates to the
41    /// caller.
42    ///
43    /// If any of the releases require a reboot, the update server with store the information about
44    /// the remaining releases to be applied and initiate a reboot.
45    ///
46    /// To check whether an update has been stopped midway by a reboot, check
47    /// [UpdateStatus::needs_continue]. To resume an update that was stopped midway by a reboot,
48    /// call [Self::continue_update].
49    ///
50    /// NOTE: Paths to the release files **must be absolute**.
51    pub fn start_update(&self, release_paths: Vec<String>)
52    where
53        P: server::MessageAllowed<messages::StartUpdate>,
54    {
55        self.conn.send_archive(messages::StartUpdate { release_paths })
56    }
57
58    /// Continue an update that was interrupted by a reboot. Make sure that this is called only if
59    /// [UpdateStatus::needs_continue] is true.
60    pub fn continue_update(&self)
61    where
62        P: server::MessageAllowed<messages::ContinueUpdate>,
63    {
64        self.conn.send_archive(messages::ContinueUpdate)
65    }
66
67    /// Get the current firmware version.
68    pub fn firmware_version(&self) -> Result<String, Error>
69    where
70        P: server::MessageAllowed<messages::FirmwareVersion>,
71    {
72        self.conn.send_blocking_archive(messages::FirmwareVersion)
73    }
74
75    /// Apply the previously downloaded firmware update. This should be called after receiving a
76    /// [`messages::ProgressUpdate::DownloadComplete`] event.
77    pub fn apply_downloaded_update(&self)
78    where
79        P: server::MessageAllowed<messages::ApplyDownloadedUpdate>,
80    {
81        self.conn.send_archive(messages::ApplyDownloadedUpdate)
82    }
83
84    /// Check whether an update was applied and is awaiting acknowledgment after reboot.
85    pub fn check_update_applied(&self) -> bool
86    where
87        P: server::MessageAllowed<messages::GetUpdateApplied>,
88    {
89        self.conn.send_blocking_scalar(messages::GetUpdateApplied)
90    }
91
92    /// Clear the update applied flag after acknowledging the update on reboot.
93    /// Note: The flag is automatically set to true by the update server when applying an update.
94    pub fn clear_update_applied(&self)
95    where
96        P: server::MessageAllowed<messages::ClearUpdateApplied>,
97    {
98        self.conn.send_scalar(messages::ClearUpdateApplied)
99    }
100
101    /// Get the current update status, including whether an update is available and if battery
102    /// is sufficient.
103    pub fn update_status(&self) -> messages::UpdateStatus
104    where
105        P: server::MessageAllowed<messages::GetUpdateStatus>,
106    {
107        self.conn.send_blocking_archive(messages::GetUpdateStatus)
108    }
109}