1use anyhow::Result;
9use fuchsia_runtime::UtcTimeline;
10use log::{debug, error};
11use serde::{Deserialize, Serialize};
12use std::fs;
13use std::path::Path;
14
15const PERSISTENT_STATE_PATH: &'static str = "/data/persistent_state.json";
17
18const REBOOT_COUNT: u8 = 1;
20
21#[derive(Serialize, Deserialize, Debug, Default, Clone)]
26pub struct State {
27 num_reboots_until_rtc_write_allowed: u8,
30
31 #[serde(default)]
34 reference_boot_instant_ns: i64,
35
36 #[serde(default)]
39 reference_utc_instant_ns: i64,
40}
41
42impl State {
43 pub fn new(update_rtc: bool) -> Self {
44 let mut value = Self { ..Default::default() };
45 value.set_may_update_rtc(update_rtc);
46 value
47 }
48
49 pub fn may_update_rtc(&self) -> bool {
51 self.num_reboots_until_rtc_write_allowed == 0
52 }
53
54 pub fn set_may_update_rtc(&mut self, update_rtc: bool) {
55 self.num_reboots_until_rtc_write_allowed = match update_rtc {
56 true => 0,
57 false => REBOOT_COUNT,
60 };
61 }
62
63 pub fn get_rtc_reference(&self) -> (zx::BootInstant, zx::Instant<UtcTimeline>) {
65 let reference_boot = zx::BootInstant::from_nanos(self.reference_boot_instant_ns);
66 let reference_utc = zx::Instant::from_nanos(self.reference_utc_instant_ns);
67 (reference_boot, reference_utc)
68 }
69
70 pub fn set_rtc_reference(
80 &mut self,
81 boot_reference: zx::BootInstant,
82 utc_reference: zx::Instant<UtcTimeline>,
83 ) {
84 self.reference_boot_instant_ns = boot_reference.into_nanos();
85 self.reference_utc_instant_ns = utc_reference.into_nanos();
86 }
87
88 fn spend_one_reboot_count(&mut self) {
90 self.num_reboots_until_rtc_write_allowed =
91 self.num_reboots_until_rtc_write_allowed.saturating_sub(1);
92 }
93
94 pub fn read_and_update() -> Result<Self> {
96 Self::read_and_update_internal(PERSISTENT_STATE_PATH)
97 }
98
99 pub fn read_and_update_internal<P: AsRef<Path>>(path: P) -> Result<Self> {
100 let path = path.as_ref();
101 let maybe_state_str: String = fs::read_to_string(path).map_err(|e| {
102 debug!("while reading: {:?}", e);
103 e
104 })?;
105 let state: State = serde_json::from_str(&maybe_state_str).map_err(|e| {
106 debug!("while deserializing: {:?}", e);
107 e
108 })?;
109 debug!("read persistent state: {:?}", &state);
110 let mut next = state.clone();
111 next.spend_one_reboot_count();
112 Self::write_internal(path, &next)?;
113 Ok(state)
114 }
115
116 pub fn write(state: &Self) -> Result<()> {
118 Self::write_internal(PERSISTENT_STATE_PATH, state)
119 }
120
121 pub fn write_internal<P: AsRef<Path>>(path: P, state: &Self) -> Result<()> {
122 let path = path.as_ref();
123 debug!("writing persistent state: {:?} to {:?}", state, path);
124 let state_str = serde_json::to_string(state).map_err(|e| {
125 error!("while serializing state: {:?}", e);
126 e
127 })?;
128 fs::write(path, state_str).map_err(|e| {
129 error!("while writing persistent state: {:?}: {:?}", path, e);
130 e.into()
131 })
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138
139 #[test]
140 fn test_roundtrip() {
141 let d = tempfile::TempDir::new().expect("tempdir created");
142 let p = d.path().join("file.json");
143 let s = State::new(false);
144 State::write_internal(&p, &s).unwrap();
145
146 assert!(!State::read_and_update_internal(&p).unwrap().may_update_rtc());
148 assert!(State::read_and_update_internal(&p).unwrap().may_update_rtc());
150 }
151}