1use anyhow::{Context, Result};
8use fidl_fuchsia_time_external as ffte;
9use fuchsia_runtime as fxr;
10use fuchsia_trace as trace;
11use futures::channel::mpsc;
12use futures::{SinkExt, StreamExt};
13use log::{debug, error};
14use scopeguard::defer;
15use std::cell::RefCell;
16
17#[derive(Debug)]
18pub enum Command {
19 PowerManagement {
21 suspended: bool,
24 done: mpsc::Sender<()>,
26 },
27 Reference {
30 boot_reference: zx::BootInstant,
32 utc_reference: fxr::UtcInstant,
34 responder: mpsc::Sender<Result<()>>,
37 },
38 Connectivity {
40 http_available: bool,
42 },
43 UpdateRtc,
45}
46
47pub struct Server {
49 adjust_sender: RefCell<mpsc::Sender<Command>>,
51}
52
53impl Server {
54 pub fn new(adjust_sender: mpsc::Sender<Command>) -> Self {
59 Self { adjust_sender: RefCell::new(adjust_sender) }
61 }
62
63 pub async fn serve(&self, mut stream: ffte::AdjustRequestStream) -> Result<()> {
65 debug!("time_adjust::serve: entering serving loop");
66 defer! {
67 debug!("time_adjust::serve: exited serving loop");
68 };
69 while let Some(request) = stream.next().await {
70 trace::duration!("timekeeper", "adjust:request");
71 debug!("time_adjust::Server::serve: request: {:?}", request);
72 match request {
73 Ok(ffte::AdjustRequest::ReportBootToUtcMapping {
74 boot_reference,
75 utc_reference,
76 responder,
77 }) => {
78 trace::instant!("alarms", "adjust:request:params", trace::Scope::Process,
79 "boot_reference" => boot_reference.into_nanos(), "utc_reference" => utc_reference);
80 let utc_reference = fxr::UtcInstant::from_nanos(utc_reference);
81 let (tx, mut rx) = mpsc::channel(1);
82 let command =
83 Command::Reference { boot_reference, utc_reference, responder: tx };
84 self.adjust_sender
85 .borrow_mut()
86 .send(command)
87 .await
88 .context("while trying to send to adjust_sender")?;
89 let result = rx.next().await.context("could not get a response")?;
90 responder.send(
91 result
92 .context("while sending response to Adjust")
93 .map_err(|e| {
94 error!("could not send response: {:?}", e);
95 e
96 })
97 .map_err(|_| ffte::Error::Internal),
98 )?;
99 }
100 Err(e) => {
101 error!("FIDL error: {:?}", e);
102 }
103 };
104 }
105 Ok(())
106 }
107}
108
109#[cfg(test)]
110mod tests {
111 use super::*;
112 use fuchsia_async as fasync;
113
114 #[fuchsia::test]
115 async fn basic_test() -> Result<()> {
116 let (tx, mut rx) = mpsc::channel(1);
117 let (proxy, stream) = fidl::endpoints::create_proxy_and_stream::<ffte::AdjustMarker>();
118 let server = Server::new(tx);
119 let _task = fasync::Task::local(async move { server.serve(stream).await });
120
121 let _success = fasync::Task::local(async move {
122 proxy
125 .report_boot_to_utc_mapping(zx::BootInstant::from_nanos(42), 4200i64)
126 .await
127 .expect("infallible")
128 });
129 let recv = rx.next().await.expect("infallible");
130 match recv {
131 Command::Reference { boot_reference, utc_reference, mut responder } => {
132 responder.send(Ok(())).await.unwrap();
133 assert_eq!(boot_reference, zx::BootInstant::from_nanos(42));
134 assert_eq!(utc_reference, fxr::UtcInstant::from_nanos(4200));
135 }
136 e => {
137 error!("Unexpected response: {:?}", e)
138 }
139 }
140
141 Ok(())
142 }
143}