1use anyhow::Error;
6use async_trait::async_trait;
7use fidl_fuchsia_hardware_power_statecontrol as powercontrol;
8use fuchsia_async as fasync;
9use fuchsia_component::client::connect_to_protocol;
10#[cfg(test)]
11use mockall::automock;
12use zx::{MonotonicDuration, Status as zx_status};
13
14#[cfg_attr(test, automock)]
15#[async_trait(?Send)]
16pub trait RebootHandler {
17 async fn reboot(&self, delay_seconds: Option<u64>) -> Result<(), Error>;
20}
21
22#[derive(Default)]
23pub struct RebootImpl;
24
25impl RebootImpl {
26 async fn request_reboot_with_proxy(
27 &self,
28 delay_seconds: Option<u64>,
29 proxy: powercontrol::AdminProxy,
30 ) -> Result<(), Error> {
31 println!("Rebooting after {:?} seconds...", delay_seconds.unwrap_or(0));
32
33 if let Some(delay) = delay_seconds {
34 fasync::Timer::new(fasync::MonotonicInstant::after(MonotonicDuration::from_seconds(
35 delay.try_into()?,
36 )))
37 .await;
38 }
39
40 proxy
42 .shutdown(&powercontrol::ShutdownOptions {
43 action: Some(powercontrol::ShutdownAction::Reboot),
44 reasons: Some(vec![powercontrol::ShutdownReason::FactoryDataReset]),
45 ..Default::default()
46 })
47 .await?
48 .map_err(zx_status::err_from_raw)?;
49 Ok(())
50 }
51}
52
53#[async_trait(?Send)]
54impl RebootHandler for RebootImpl {
55 async fn reboot(&self, delay_seconds: Option<u64>) -> Result<(), Error> {
56 let proxy = connect_to_protocol::<powercontrol::AdminMarker>()?;
57 self.request_reboot_with_proxy(delay_seconds, proxy).await
58 }
59}
60
61#[cfg(test)]
62mod test {
63 use super::*;
64 use fidl_fuchsia_hardware_power_statecontrol as powercontrol;
65 use fidl_fuchsia_hardware_power_statecontrol::{
66 ShutdownAction, ShutdownOptions, ShutdownReason,
67 };
68 use fuchsia_async as fasync;
69 use fuchsia_async::TimeoutExt;
70 use futures::channel::mpsc;
71 use futures::{StreamExt, TryStreamExt};
72
73 fn create_mock_powercontrol_server()
75 -> Result<(powercontrol::AdminProxy, mpsc::Receiver<powercontrol::ShutdownOptions>), Error>
76 {
77 let (mut sender, receiver) = mpsc::channel(1);
78 let (proxy, mut request_stream) =
79 fidl::endpoints::create_proxy_and_stream::<powercontrol::AdminMarker>();
80
81 fasync::Task::local(async move {
82 while let Some(request) =
83 request_stream.try_next().await.expect("failed to read mock request")
84 {
85 match request {
86 powercontrol::AdminRequest::Shutdown { options, responder } => {
87 sender.start_send(options).unwrap();
88 let result: powercontrol::AdminShutdownResult = { Ok(()) };
89 responder.send(result).ok();
90 }
91 _ => {
92 panic!("Mock server not configured to handle request");
93 }
94 }
95 }
96 })
97 .detach();
98
99 Ok((proxy, receiver))
100 }
101
102 #[fuchsia::test]
103 async fn test_reboot_reason_no_delay() {
104 let (proxy, mut receiver) = create_mock_powercontrol_server().unwrap();
105
106 let reboot = RebootImpl::default();
107 reboot.request_reboot_with_proxy(None, proxy).await.unwrap();
108
109 let options =
110 receiver.next().on_timeout(MonotonicDuration::from_seconds(5), || None).await.unwrap();
111
112 assert_eq!(
113 options,
114 ShutdownOptions {
115 action: Some(ShutdownAction::Reboot),
116 reasons: Some(vec![ShutdownReason::FactoryDataReset]),
117 ..Default::default()
118 }
119 );
120 }
121
122 #[fuchsia::test]
123 async fn test_reboot_with_delay() {
124 let delay_seconds = 1;
125 let (proxy, mut receiver) = create_mock_powercontrol_server().unwrap();
126
127 let start_time = fasync::MonotonicInstant::now();
128 let reboot = RebootImpl::default();
129 reboot.request_reboot_with_proxy(Some(delay_seconds), proxy).await.unwrap();
130
131 let options =
132 receiver.next().on_timeout(MonotonicDuration::from_seconds(5), || None).await.unwrap();
133
134 let end_time = fasync::MonotonicInstant::now();
135
136 assert!((end_time - start_time).into_seconds() >= delay_seconds.try_into().unwrap());
137 assert_eq!(
138 options,
139 ShutdownOptions {
140 action: Some(ShutdownAction::Reboot),
141 reasons: Some(vec![ShutdownReason::FactoryDataReset]),
142 ..Default::default()
143 }
144 );
145 }
146}