1use crate::reboot_reasons::ShutdownOptionsWrapper;
6
7use fidl::endpoints::Proxy;
8use fidl_fuchsia_hardware_power_statecontrol::{self as fpower, ShutdownAction};
9use fuchsia_async::{self as fasync, DurationExt, TimeoutExt};
10use fuchsia_inspect::{self as inspect, NumericProperty};
11use futures::TryStreamExt;
12use futures::lock::Mutex;
13use futures::prelude::*;
14use std::collections::HashMap;
15use std::sync::Arc;
16use zx::AsHandleRef;
17use zx::sys::zx_handle_t;
18
19pub struct ShutdownWatcher {
36 reboot_watchers: Arc<Mutex<HashMap<u32, fpower::RebootWatcherProxy>>>,
39 shutdown_watchers: Arc<Mutex<HashMap<zx_handle_t, fpower::ShutdownWatcherProxy>>>,
42 terminal_state_watchers: Arc<Mutex<HashMap<zx_handle_t, fpower::TerminalStateWatcherProxy>>>,
45 inspect: Arc<Mutex<InspectData>>,
46}
47
48impl ShutdownWatcher {
49 const NOTIFY_SHUTDOWN_RESPONSE_TIMEOUT: zx::MonotonicDuration =
50 zx::MonotonicDuration::from_seconds(
51 fpower::MAX_SHUTDOWN_WATCHER_RESPONSE_TIME_SECONDS as i64,
52 );
53
54 const NOTIFY_REBOOT_RESPONSE_TIMEOUT: zx::MonotonicDuration =
55 zx::MonotonicDuration::from_seconds(
56 fpower::MAX_REBOOT_WATCHER_RESPONSE_TIME_SECONDS as i64,
57 );
58
59 const NOTIFY_TERMINAL_STATE_RESPONSE_TIMEOUT: zx::MonotonicDuration =
60 zx::MonotonicDuration::from_seconds(
61 fpower::MAX_TERMINAL_STATE_WATCHER_RESPONSE_TIME_SECONDS as i64,
62 );
63
64 #[cfg(test)]
65 fn new() -> Arc<Self> {
66 let inspector = inspect::Inspector::new(fuchsia_inspect::InspectorConfig::default());
67 Self::new_with_inspector(&inspector)
68 }
69
70 pub fn new_with_inspector(inspector: &inspect::Inspector) -> Arc<Self> {
71 Arc::new(Self {
72 reboot_watchers: Arc::new(Mutex::new(HashMap::new())),
73 shutdown_watchers: Arc::new(Mutex::new(HashMap::new())),
74 terminal_state_watchers: Arc::new(Mutex::new(HashMap::new())),
75 inspect: Arc::new(Mutex::new(InspectData::new(
76 inspector.root(),
77 "ShutdownWatcher".to_string(),
78 ))),
79 })
80 }
81
82 pub async fn handle_reboot_register_request(
84 self: Arc<Self>,
85 mut stream: fpower::RebootMethodsWatcherRegisterRequestStream,
86 ) {
87 while let Ok(Some(req)) = stream.try_next().await {
88 match req {
89 fpower::RebootMethodsWatcherRegisterRequest::RegisterWatcher {
90 watcher,
91 responder,
92 } => {
93 self.add_reboot_watcher(watcher.into_proxy()).await;
94 let _ = responder.send();
95 }
96 }
97 }
98 }
99
100 pub async fn handle_shutdown_register_request(
102 self: Arc<Self>,
103 mut stream: fpower::ShutdownWatcherRegisterRequestStream,
104 ) {
105 while let Ok(Some(req)) = stream.try_next().await {
106 match req {
107 fpower::ShutdownWatcherRegisterRequest::RegisterWatcher { watcher, responder } => {
108 self.add_shutdown_watcher(watcher.into_proxy()).await;
109 let _ = responder.send();
110 }
111 fpower::ShutdownWatcherRegisterRequest::RegisterTerminalStateWatcher {
112 watcher,
113 responder,
114 } => {
115 self.add_terminal_state_watcher(watcher.into_proxy()).await;
116 let _ = responder.send();
117 }
118 fpower::ShutdownWatcherRegisterRequest::_UnknownMethod { ordinal, .. } => {
119 println!("[shutdown-shim]: error, unimplemented method ordinal: {ordinal}")
120 }
121 }
122 }
123 }
124
125 async fn add_reboot_watcher(&self, watcher: fpower::RebootWatcherProxy) {
127 fuchsia_trace::duration!(
128 c"shutdown-shim",
129 c"ShutdownWatcher::add_reboot_watcher",
130 "watcher" => watcher.as_channel().as_handle_ref().raw_handle()
131 );
132
133 println!("[shutdown-shim] Adding a reboot watcher");
135 let key = watcher.as_channel().as_handle_ref().raw_handle();
136 let proxy = watcher.clone();
137 let reboot_watchers = self.reboot_watchers.clone();
138 let inspect = self.inspect.clone();
139 fasync::Task::spawn(async move {
140 let _ = proxy.on_closed().await;
141 {
142 reboot_watchers.lock().await.remove(&key);
143 }
144 inspect.lock().await.remove_reboot_watcher();
145 })
146 .detach();
147
148 {
149 let mut watchers_mut = self.reboot_watchers.lock().await;
150 watchers_mut.insert(key, watcher);
151 }
152 self.inspect.lock().await.add_reboot_watcher();
153 }
154
155 async fn add_shutdown_watcher(&self, watcher: fpower::ShutdownWatcherProxy) {
157 fuchsia_trace::duration!(
158 c"shutdown-shim",
159 c"ShutdownWatcher::add_shutdown_watcher",
160 "watcher" => watcher.as_channel().as_handle_ref().raw_handle()
161 );
162
163 println!("[shutdown-shim] Adding a shutdown watcher");
165 let key = watcher.as_channel().as_handle_ref().raw_handle();
166 let proxy = watcher.clone();
167 let shutdown_watchers = self.shutdown_watchers.clone();
168 let inspect = self.inspect.clone();
169 fasync::Task::spawn(async move {
170 let _ = proxy.on_closed().await;
171 {
172 shutdown_watchers.lock().await.remove(&key);
173 }
174 inspect.lock().await.remove_reboot_watcher();
175 })
176 .detach();
177
178 {
179 let mut watchers_mut = self.shutdown_watchers.lock().await;
180 watchers_mut.insert(key, watcher);
181 }
182 self.inspect.lock().await.add_reboot_watcher();
183 }
184
185 async fn add_terminal_state_watcher(&self, watcher: fpower::TerminalStateWatcherProxy) {
187 fuchsia_trace::duration!(
188 c"shutdown-shim",
189 c"ShutdownWatcher::add_terminal_state_watcher",
190 "watcher" => watcher.as_channel().as_handle_ref().raw_handle()
191 );
192
193 println!("[shutdown-shim] Adding a terminal state watcher");
195 let key = watcher.as_channel().as_handle_ref().raw_handle();
196 let proxy = watcher.clone();
197 let terminal_state_watchers = self.terminal_state_watchers.clone();
198 let inspect = self.inspect.clone();
199 fasync::Task::spawn(async move {
200 let _ = proxy.on_closed().await;
201 {
202 terminal_state_watchers.lock().await.remove(&key);
203 }
204 inspect.lock().await.remove_terminal_state_watcher();
205 })
206 .detach();
207
208 {
209 let mut watchers_mut = self.terminal_state_watchers.lock().await;
210 watchers_mut.insert(key, watcher);
211 }
212 self.inspect.lock().await.add_terminal_state_watcher();
213 }
214
215 pub async fn handle_system_shutdown_message(&self, options: Option<ShutdownOptionsWrapper>) {
217 let terminal_state_fut =
218 self.notify_terminal_state_watchers(Self::NOTIFY_TERMINAL_STATE_RESPONSE_TIMEOUT);
219
220 let Some(options) = options else {
221 terminal_state_fut.await;
222 return;
223 };
224
225 if options.action == ShutdownAction::Reboot && !options.reasons.is_empty() {
226 futures::join!(
227 self.notify_shutdown_watchers(
228 options.clone(),
229 Self::NOTIFY_SHUTDOWN_RESPONSE_TIMEOUT
230 ),
231 self.notify_reboot_watchers(options, Self::NOTIFY_REBOOT_RESPONSE_TIMEOUT),
232 terminal_state_fut
233 );
234 } else {
235 futures::join!(
236 self.notify_shutdown_watchers(options, Self::NOTIFY_SHUTDOWN_RESPONSE_TIMEOUT),
237 terminal_state_fut
238 );
239 }
240 }
241
242 async fn notify_reboot_watchers(
243 &self,
244 options: ShutdownOptionsWrapper,
245 timeout: zx::MonotonicDuration,
246 ) {
247 fuchsia_trace::duration_begin!(
249 c"shutdown-shim",
250 c"ShutdownWatcher::notify_reboot_watchers",
251 "options" => format!("{:?}", options).as_str()
252 );
253
254 let watcher_futures = {
259 let watchers = self.reboot_watchers.lock().await;
261 println!("[shutdown-shim] notifying {:?} watchers of reboot", watchers.len());
262 watchers.clone().into_iter().map(|(key, watcher_proxy)| {
263 let options = options.clone();
264 async move {
265 let deadline = timeout.after_now();
266 let result = watcher_proxy
267 .on_reboot(&options.into())
268 .map_err(|_| ())
269 .on_timeout(deadline, || Err(()))
270 .await;
271 match result {
272 Ok(()) => Some((key, watcher_proxy)),
273 Err(()) => None,
274 }
275 }
276 })
277 };
278
279 let new_watchers = futures::future::join_all(watcher_futures)
281 .await
282 .into_iter()
283 .filter_map(|watcher_opt| watcher_opt) .collect();
285
286 *self.reboot_watchers.lock().await = new_watchers;
288
289 fuchsia_trace::duration_end!(
290 c"shutdown-shim",
291 c"ShutdownWatcher::notify_reboot_watchers",
292 "options" => format!("{:?}", options).as_str()
293 );
294 }
295
296 async fn notify_shutdown_watchers(
297 &self,
298 options: ShutdownOptionsWrapper,
299 timeout: zx::MonotonicDuration,
300 ) {
301 fuchsia_trace::duration_begin!(
303 c"shutdown-shim",
304 c"ShutdownWatcher::notify_shutdown_watchers",
305 "options" => format!("{:?}", options).as_str()
306 );
307
308 let watcher_futures = {
313 let watchers = self.shutdown_watchers.lock().await;
316 println!("[shutdown-shim] notifying {:?} watchers of shutdown", watchers.len());
317 watchers.clone().into_iter().map(|(key, watcher_proxy)| {
318 let options = options.clone();
319 async move {
320 let deadline = timeout.after_now();
321 let result = watcher_proxy
322 .on_shutdown(&options.into())
323 .map_err(|_| ())
324 .on_timeout(deadline, || Err(()))
325 .await;
326
327 match result {
328 Ok(()) => Some((key, watcher_proxy)),
329 Err(()) => None,
330 }
331 }
332 })
333 };
334
335 let new_watchers = futures::future::join_all(watcher_futures)
337 .await
338 .into_iter()
339 .filter_map(|watcher_opt| watcher_opt) .collect();
341
342 *self.shutdown_watchers.lock().await = new_watchers;
344
345 fuchsia_trace::duration_end!(
346 c"shutdown-shim",
347 c"ShutdownWatcher::notify_shutdown_watchers",
348 "options" => format!("{:?}", options).as_str()
349 );
350 }
351
352 async fn notify_terminal_state_watchers(&self, timeout: zx::MonotonicDuration) {
353 fuchsia_trace::duration!(
354 c"shutdown-shim",
355 c"ShutdownWatcher::notify_terminal_state_watchers",
356 );
357
358 let watcher_futures = {
363 let watchers = self.terminal_state_watchers.lock().await;
366 println!("[shutdown-shim] notifying {:?} watchers of terminal state", watchers.len());
367 watchers.clone().into_iter().map(|(key, watcher_proxy)| async move {
368 let deadline = timeout.after_now();
369 let result = watcher_proxy
370 .on_terminal_state_transition_started()
371 .map_err(|_| ())
372 .on_timeout(deadline, || Err(()))
373 .await;
374
375 match result {
376 Ok(()) => Some((key, watcher_proxy)),
377 Err(()) => None,
378 }
379 })
380 };
381
382 let new_watchers = futures::future::join_all(watcher_futures)
384 .await
385 .into_iter()
386 .filter_map(|watcher_opt| watcher_opt) .collect();
388
389 *self.terminal_state_watchers.lock().await = new_watchers;
391 }
392}
393
394struct InspectData {
395 reboot_watcher_current_connections: inspect::UintProperty,
396 reboot_watcher_total_connections: inspect::UintProperty,
397 terminal_state_watcher_current_connections: inspect::UintProperty,
398 terminal_state_watcher_total_connections: inspect::UintProperty,
399}
400
401impl InspectData {
405 fn new(parent: &inspect::Node, name: String) -> Self {
406 let root = parent.create_child(name);
408 let reboot_watcher_current_connections =
409 root.create_uint("reboot_watcher_current_connections", 0);
410 let reboot_watcher_total_connections =
411 root.create_uint("reboot_watcher_total_connections", 0);
412 let terminal_state_watcher_current_connections =
413 root.create_uint("terminal_state_watcher_current_connections", 0);
414 let terminal_state_watcher_total_connections =
415 root.create_uint("terminal_state_watcher_total_connections", 0);
416
417 parent.record(root);
419
420 InspectData {
421 reboot_watcher_current_connections,
422 reboot_watcher_total_connections,
423 terminal_state_watcher_current_connections,
424 terminal_state_watcher_total_connections,
425 }
426 }
427
428 fn add_reboot_watcher(&self) {
429 self.reboot_watcher_current_connections.add(1);
430 self.reboot_watcher_total_connections.add(1);
431 }
432
433 fn remove_reboot_watcher(&self) {
434 self.reboot_watcher_current_connections.subtract(1);
435 }
436
437 fn add_terminal_state_watcher(&self) {
438 self.terminal_state_watcher_current_connections.add(1);
439 self.terminal_state_watcher_total_connections.add(1);
440 }
441
442 fn remove_terminal_state_watcher(&self) {
443 self.terminal_state_watcher_current_connections.subtract(1);
444 }
445}
446
447#[cfg(test)]
448mod tests {
449 use super::*;
450 use assert_matches::assert_matches;
451 use diagnostics_assertions::assert_data_tree;
452 use fidl::endpoints::{ControlHandle, RequestStream};
453 use fidl_fuchsia_hardware_power_statecontrol::{ShutdownAction, ShutdownReason};
454
455 fn seconds(seconds: f64) -> zx::MonotonicDuration {
457 zx::MonotonicDuration::from_seconds(seconds as i64)
458 }
459
460 #[fuchsia::test]
462 async fn test_inspect_data() {
463 let inspector = inspect::Inspector::default();
464 let registrar = ShutdownWatcher::new_with_inspector(&inspector);
465
466 assert_data_tree!(
467 inspector,
468 root: {
469 ShutdownWatcher: {
470 reboot_watcher_current_connections: 0u64,
471 reboot_watcher_total_connections: 0u64,
472 terminal_state_watcher_current_connections: 0u64,
473 terminal_state_watcher_total_connections: 0u64,
474 }
475 }
476 );
477
478 let (watcher_proxy, s) = fidl::endpoints::create_proxy::<fpower::RebootWatcherMarker>();
479 registrar.add_reboot_watcher(watcher_proxy.clone()).await;
480
481 assert_data_tree!(
482 inspector,
483 root: {
484 ShutdownWatcher: {
485 reboot_watcher_current_connections: 1u64,
486 reboot_watcher_total_connections: 1u64,
487 terminal_state_watcher_current_connections: 0u64,
488 terminal_state_watcher_total_connections: 0u64,
489 }
490 }
491 );
492
493 drop(s);
494 watcher_proxy.on_closed().await.expect("closed");
495
496 assert_data_tree!(
497 @retry 10,
498 inspector,
499 root: {
500 ShutdownWatcher: {
501 reboot_watcher_current_connections: 0u64,
502 reboot_watcher_total_connections: 1u64,
503 terminal_state_watcher_current_connections: 0u64,
504 terminal_state_watcher_total_connections: 0u64,
505 }
506 }
507 );
508
509 let (watcher_proxy, s) = fidl::endpoints::create_proxy::<fpower::RebootWatcherMarker>();
510 registrar.add_reboot_watcher(watcher_proxy.clone()).await;
511
512 assert_data_tree!(
513 @retry 10,
514 inspector,
515 root: {
516 ShutdownWatcher: {
517 reboot_watcher_current_connections: 1u64,
518 reboot_watcher_total_connections: 2u64,
519 terminal_state_watcher_current_connections: 0u64,
520 terminal_state_watcher_total_connections: 0u64,
521 }
522 }
523 );
524 drop(s);
525 watcher_proxy.on_closed().await.expect("closed");
526
527 assert_data_tree!(
528 @retry 10,
529 inspector,
530 root: {
531 ShutdownWatcher: {
532 reboot_watcher_current_connections: 0u64,
533 reboot_watcher_total_connections: 2u64,
534 terminal_state_watcher_current_connections: 0u64,
535 terminal_state_watcher_total_connections: 0u64,
536 }
537 }
538 );
539
540 let (watcher_proxy, s) = fidl::endpoints::create_proxy::<fpower::ShutdownWatcherMarker>();
541 registrar.add_shutdown_watcher(watcher_proxy.clone()).await;
542
543 assert_data_tree!(
544 @retry 10,
545 inspector,
546 root: {
547 ShutdownWatcher: {
548 reboot_watcher_current_connections: 1u64,
549 reboot_watcher_total_connections: 3u64,
550 terminal_state_watcher_current_connections: 0u64,
551 terminal_state_watcher_total_connections: 0u64,
552 }
553 }
554 );
555 drop(s);
556 watcher_proxy.on_closed().await.expect("closed");
557
558 assert_data_tree!(
559 @retry 10,
560 inspector,
561 root: {
562 ShutdownWatcher: {
563 reboot_watcher_current_connections: 0u64,
564 reboot_watcher_total_connections: 3u64,
565 terminal_state_watcher_current_connections: 0u64,
566 terminal_state_watcher_total_connections: 0u64,
567 }
568 }
569 );
570
571 let (watcher_proxy, s) =
572 fidl::endpoints::create_proxy::<fpower::TerminalStateWatcherMarker>();
573 registrar.add_terminal_state_watcher(watcher_proxy.clone()).await;
574
575 assert_data_tree!(
576 @retry 10,
577 inspector,
578 root: {
579 ShutdownWatcher: {
580 reboot_watcher_current_connections: 0u64,
581 reboot_watcher_total_connections: 3u64,
582 terminal_state_watcher_current_connections: 1u64,
583 terminal_state_watcher_total_connections: 1u64,
584 }
585 }
586 );
587 drop(s);
588 watcher_proxy.on_closed().await.expect("closed");
589
590 assert_data_tree!(
591 @retry 10,
592 inspector,
593 root: {
594 ShutdownWatcher: {
595 reboot_watcher_current_connections: 0u64,
596 reboot_watcher_total_connections: 3u64,
597 terminal_state_watcher_current_connections: 0u64,
598 terminal_state_watcher_total_connections: 1u64,
599 }
600 }
601 );
602 }
603
604 #[fasync::run_singlethreaded(test)]
607 async fn test_add_reboot_watcher() {
608 let registrar = ShutdownWatcher::new();
609
610 let (register_proxy, register_stream) = fidl::endpoints::create_proxy_and_stream::<
612 fpower::RebootMethodsWatcherRegisterMarker,
613 >();
614
615 let registrar_clone = registrar.clone();
618 fasync::Task::local(async move {
619 registrar_clone.handle_reboot_register_request(register_stream).await;
620 })
621 .detach();
622
623 let (watcher_client, mut watcher_stream) =
625 fidl::endpoints::create_request_stream::<fpower::RebootWatcherMarker>();
626
627 assert_matches!(register_proxy.register_watcher(watcher_client).await, Ok(()));
629 registrar
631 .notify_reboot_watchers(
632 ShutdownOptionsWrapper::new(ShutdownAction::Reboot, ShutdownReason::UserRequest),
633 seconds(0.0),
634 )
635 .await;
636
637 let reasons = assert_matches!(
639 watcher_stream.try_next().await.unwrap().unwrap(),
640 fpower::RebootWatcherRequest::OnReboot {
641 options: fpower::RebootOptions{reasons: Some(reasons), ..},
642 ..
643 } => reasons
644 );
645 assert_eq!(&reasons[..], [fpower::RebootReason2::UserRequest]);
646 }
647
648 #[fasync::run_singlethreaded(test)]
651 async fn test_add_shutdown_watcher() {
652 let registrar = ShutdownWatcher::new();
653
654 let (register_proxy, register_stream) =
656 fidl::endpoints::create_proxy_and_stream::<fpower::ShutdownWatcherRegisterMarker>();
657
658 let registrar_clone = registrar.clone();
661 fasync::Task::local(async move {
662 registrar_clone.handle_shutdown_register_request(register_stream).await;
663 })
664 .detach();
665
666 let (watcher_client, mut watcher_stream) =
668 fidl::endpoints::create_request_stream::<fpower::ShutdownWatcherMarker>();
669
670 assert_matches!(register_proxy.register_watcher(watcher_client).await, Ok(()));
672
673 registrar
675 .notify_shutdown_watchers(
676 ShutdownOptionsWrapper::new(ShutdownAction::Poweroff, ShutdownReason::UserRequest),
677 seconds(0.0),
678 )
679 .await;
680
681 let (action, reasons) = assert_matches!(
683 watcher_stream.try_next().await.unwrap().unwrap(),
684 fpower::ShutdownWatcherRequest::OnShutdown {
685 options: fpower::ShutdownOptions{action: Some(action), reasons: Some(reasons), ..},
686 ..
687 } => (action, reasons)
688 );
689 assert_eq!(action, ShutdownAction::Poweroff);
690 assert_eq!(&reasons[..], [ShutdownReason::UserRequest]);
691 }
692
693 #[fasync::run_singlethreaded(test)]
696 async fn test_add_terminal_state_watcher() {
697 let registrar = ShutdownWatcher::new();
698
699 let (register_proxy, register_stream) =
701 fidl::endpoints::create_proxy_and_stream::<fpower::ShutdownWatcherRegisterMarker>();
702
703 let registrar_clone = registrar.clone();
706 fasync::Task::local(async move {
707 registrar_clone.handle_shutdown_register_request(register_stream).await;
708 })
709 .detach();
710
711 let (watcher_client, mut watcher_stream) =
713 fidl::endpoints::create_request_stream::<fpower::TerminalStateWatcherMarker>();
714
715 assert_matches!(
717 register_proxy.register_terminal_state_watcher(watcher_client).await,
718 Ok(())
719 );
720
721 registrar.notify_terminal_state_watchers(seconds(0.0)).await;
723
724 assert_matches!(
726 watcher_stream.try_next().await.unwrap().unwrap(),
727 fpower::TerminalStateWatcherRequest::OnTerminalStateTransitionStarted { .. }
728 );
729 }
730
731 #[fasync::run_singlethreaded(test)]
733 async fn test_reboot_watcher_reason() {
734 let registrar = ShutdownWatcher::new();
735 let (watcher_proxy, mut watcher_stream) =
736 fidl::endpoints::create_proxy_and_stream::<fpower::RebootWatcherMarker>();
737 registrar.add_reboot_watcher(watcher_proxy).await;
738 registrar
739 .notify_reboot_watchers(
740 ShutdownOptionsWrapper::new(
741 ShutdownAction::Reboot,
742 ShutdownReason::HighTemperature,
743 ),
744 seconds(0.0),
745 )
746 .await;
747
748 let reasons = match watcher_stream.try_next().await {
749 Ok(Some(fpower::RebootWatcherRequest::OnReboot {
750 options: fpower::RebootOptions { reasons: Some(reasons), .. },
751 ..
752 })) => reasons,
753 e => panic!("Unexpected watcher_stream result: {:?}", e),
754 };
755
756 assert_eq!(&reasons[..], [fpower::RebootReason2::HighTemperature]);
757 }
758
759 #[fasync::run_singlethreaded(test)]
762 async fn test_shutdown_watcher_reason() {
763 let registrar = ShutdownWatcher::new();
764 let (watcher_proxy, mut watcher_stream) =
765 fidl::endpoints::create_proxy_and_stream::<fpower::ShutdownWatcherMarker>();
766 registrar.add_shutdown_watcher(watcher_proxy).await;
767 registrar
768 .notify_shutdown_watchers(
769 ShutdownOptionsWrapper::new(
770 ShutdownAction::Poweroff,
771 ShutdownReason::HighTemperature,
772 ),
773 seconds(0.0),
774 )
775 .await;
776
777 let (action, reasons) = assert_matches!(
778 watcher_stream.try_next().await.unwrap().unwrap(),
779 fpower::ShutdownWatcherRequest::OnShutdown {
780 options: fpower::ShutdownOptions{action: Some(action), reasons: Some(reasons), ..},
781 ..
782 } => (action, reasons)
783 );
784 assert_eq!(action, ShutdownAction::Poweroff);
785 assert_eq!(&reasons[..], [ShutdownReason::HighTemperature]);
786 }
787
788 #[fasync::run_singlethreaded(test)]
791 async fn test_multiple_reboot_watchers() {
792 let registrar = ShutdownWatcher::new();
793
794 let (watcher_proxy1, mut watcher_stream1) =
796 fidl::endpoints::create_proxy_and_stream::<fpower::RebootWatcherMarker>();
797 registrar.add_reboot_watcher(watcher_proxy1).await;
798
799 let (watcher_proxy2, mut watcher_stream2) =
800 fidl::endpoints::create_proxy_and_stream::<fpower::RebootWatcherMarker>();
801 registrar.add_reboot_watcher(watcher_proxy2).await;
802
803 let (watcher_proxy3, mut watcher_stream3) =
804 fidl::endpoints::create_proxy_and_stream::<fpower::RebootWatcherMarker>();
805 registrar.add_reboot_watcher(watcher_proxy3).await;
806
807 watcher_stream1.control_handle().shutdown();
810
811 registrar
812 .notify_reboot_watchers(
813 ShutdownOptionsWrapper::new(
814 ShutdownAction::Reboot,
815 ShutdownReason::HighTemperature,
816 ),
817 seconds(0.0),
818 )
819 .await;
820
821 match watcher_stream1.try_next().await {
823 Ok(None) => {}
824 e => panic!("Unexpected watcher_stream1 result: {:?}", e),
825 };
826
827 match watcher_stream2.try_next().await {
829 Ok(Some(fpower::RebootWatcherRequest::OnReboot {
830 options: fpower::RebootOptions { reasons: Some(reasons), .. },
831 ..
832 })) => {
833 assert_eq!(&reasons[..], [fpower::RebootReason2::HighTemperature])
834 }
835 e => panic!("Unexpected watcher_stream2 result: {:?}", e),
836 };
837
838 match watcher_stream3.try_next().await {
840 Ok(Some(fpower::RebootWatcherRequest::OnReboot {
841 options: fpower::RebootOptions { reasons: Some(reasons), .. },
842 ..
843 })) => assert_eq!(&reasons[..], [fpower::RebootReason2::HighTemperature]),
844 e => panic!("Unexpected watcher_stream3 result: {:?}", e),
845 };
846 }
847
848 #[fasync::run_singlethreaded(test)]
851 async fn test_multiple_shutdown_watchers() {
852 let registrar = ShutdownWatcher::new();
853
854 let (watcher_proxy1, mut watcher_stream1) =
856 fidl::endpoints::create_proxy_and_stream::<fpower::ShutdownWatcherMarker>();
857 registrar.add_shutdown_watcher(watcher_proxy1).await;
858
859 let (watcher_proxy2, mut watcher_stream2) =
860 fidl::endpoints::create_proxy_and_stream::<fpower::ShutdownWatcherMarker>();
861 registrar.add_shutdown_watcher(watcher_proxy2).await;
862
863 let (watcher_proxy3, mut watcher_stream3) =
864 fidl::endpoints::create_proxy_and_stream::<fpower::ShutdownWatcherMarker>();
865 registrar.add_shutdown_watcher(watcher_proxy3).await;
866
867 watcher_stream1.control_handle().shutdown();
870
871 registrar
872 .notify_shutdown_watchers(
873 ShutdownOptionsWrapper::new(
874 ShutdownAction::Reboot,
875 ShutdownReason::HighTemperature,
876 ),
877 seconds(0.0),
878 )
879 .await;
880
881 match watcher_stream1.try_next().await {
883 Ok(None) => {}
884 e => panic!("Unexpected watcher_stream1 result: {:?}", e),
885 };
886
887 let (action, reasons) = assert_matches!(
889 watcher_stream2.try_next().await.unwrap().unwrap(),
890 fpower::ShutdownWatcherRequest::OnShutdown {
891 options: fpower::ShutdownOptions{action: Some(action), reasons: Some(reasons), ..},
892 ..
893 } => (action, reasons)
894 );
895 assert_eq!(action, ShutdownAction::Reboot);
896 assert_eq!(&reasons[..], [ShutdownReason::HighTemperature]);
897
898 let (action, reasons) = assert_matches!(
900 watcher_stream3.try_next().await.unwrap().unwrap(),
901 fpower::ShutdownWatcherRequest::OnShutdown {
902 options: fpower::ShutdownOptions{action: Some(action), reasons: Some(reasons), ..},
903 ..
904 } => (action, reasons)
905 );
906 assert_eq!(action, ShutdownAction::Reboot);
907 assert_eq!(&reasons[..], [ShutdownReason::HighTemperature]);
908 }
909
910 #[fasync::run_singlethreaded(test)]
913 async fn reboot_watchers_only_notified_of_reboots() {
914 let registrar = ShutdownWatcher::new();
915 let (watcher_proxy, mut watcher_stream) =
916 fidl::endpoints::create_proxy_and_stream::<fpower::RebootWatcherMarker>();
917 registrar.add_reboot_watcher(watcher_proxy).await;
918
919 registrar
920 .handle_system_shutdown_message(Some(ShutdownOptionsWrapper::new(
921 ShutdownAction::Poweroff,
922 ShutdownReason::HighTemperature,
923 )))
924 .await;
925 assert!(futures::poll!(watcher_stream.try_next()).is_pending());
926
927 registrar
928 .handle_system_shutdown_message(Some(ShutdownOptionsWrapper::new(
929 ShutdownAction::RebootToBootloader,
930 ShutdownReason::HighTemperature,
931 )))
932 .await;
933 assert!(futures::poll!(watcher_stream.try_next()).is_pending());
934
935 registrar
936 .handle_system_shutdown_message(Some(ShutdownOptionsWrapper::new(
937 ShutdownAction::RebootToRecovery,
938 ShutdownReason::HighTemperature,
939 )))
940 .await;
941 assert!(futures::poll!(watcher_stream.try_next()).is_pending());
942
943 registrar
944 .handle_system_shutdown_message(Some(ShutdownOptionsWrapper::new(
945 ShutdownAction::Reboot,
946 ShutdownReason::HighTemperature,
947 )))
948 .await;
949 match watcher_stream.try_next().await {
950 Ok(Some(fpower::RebootWatcherRequest::OnReboot {
951 options: fpower::RebootOptions { reasons: Some(reasons), .. },
952 ..
953 })) => {
954 assert_eq!(&reasons[..], [fpower::RebootReason2::HighTemperature])
955 }
956 e => panic!("Unexpected watcher_stream result: {:?}", e),
957 };
958 }
959
960 #[fasync::run_singlethreaded(test)]
964 async fn shutdown_terminal_state_and_reboot_watchers_notified() {
965 let registrar = ShutdownWatcher::new();
966
967 let (shutdown_watcher_proxy, mut shutdown_watcher_stream) =
968 fidl::endpoints::create_proxy_and_stream::<fpower::ShutdownWatcherMarker>();
969 registrar.add_shutdown_watcher(shutdown_watcher_proxy).await;
970
971 let (reboot_watcher_proxy, mut reboot_watcher_stream) =
972 fidl::endpoints::create_proxy_and_stream::<fpower::RebootWatcherMarker>();
973 registrar.add_reboot_watcher(reboot_watcher_proxy).await;
974
975 let (terminal_state_watcher_proxy, mut terminal_state_watcher_stream) =
976 fidl::endpoints::create_proxy_and_stream::<fpower::TerminalStateWatcherMarker>();
977 registrar.add_terminal_state_watcher(terminal_state_watcher_proxy).await;
978
979 registrar
980 .handle_system_shutdown_message(Some(ShutdownOptionsWrapper::new(
981 ShutdownAction::Reboot,
982 ShutdownReason::HighTemperature,
983 )))
984 .await;
985
986 let (action, reasons) = assert_matches!(
987 shutdown_watcher_stream.try_next().await.unwrap().unwrap(),
988 fpower::ShutdownWatcherRequest::OnShutdown {
989 options: fpower::ShutdownOptions{action: Some(action), reasons: Some(reasons), ..},
990 ..
991 } => (action, reasons)
992 );
993 assert_eq!(action, ShutdownAction::Reboot);
994 assert_eq!(&reasons[..], [ShutdownReason::HighTemperature]);
995
996 match reboot_watcher_stream.try_next().await {
997 Ok(Some(fpower::RebootWatcherRequest::OnReboot {
998 options: fpower::RebootOptions { reasons: Some(reasons), .. },
999 ..
1000 })) => {
1001 assert_eq!(&reasons[..], [fpower::RebootReason2::HighTemperature])
1002 }
1003 e => panic!("Unexpected watcher_stream result: {:?}", e),
1004 };
1005
1006 assert_matches!(
1007 terminal_state_watcher_stream.try_next().await.unwrap().unwrap(),
1008 fpower::TerminalStateWatcherRequest::OnTerminalStateTransitionStarted { .. }
1009 );
1010 }
1011
1012 #[fasync::run_singlethreaded(test)]
1013 async fn shutdown_watchers_notified_for_poweroff() {
1014 let registrar = ShutdownWatcher::new();
1015
1016 let (watcher_proxy, mut watcher_stream) =
1017 fidl::endpoints::create_proxy_and_stream::<fpower::ShutdownWatcherMarker>();
1018 registrar.add_shutdown_watcher(watcher_proxy).await;
1019
1020 registrar
1021 .handle_system_shutdown_message(Some(ShutdownOptionsWrapper::new(
1022 ShutdownAction::Poweroff,
1023 ShutdownReason::HighTemperature,
1024 )))
1025 .await;
1026 let (action, reasons) = assert_matches!(
1027 watcher_stream.try_next().await.unwrap().unwrap(),
1028 fpower::ShutdownWatcherRequest::OnShutdown {
1029 options: fpower::ShutdownOptions{action: Some(action), reasons: Some(reasons), ..},
1030 ..
1031 } => (action, reasons)
1032 );
1033 assert_eq!(action, ShutdownAction::Poweroff);
1034 assert_eq!(&reasons[..], [ShutdownReason::HighTemperature]);
1035 }
1036
1037 #[fasync::run_singlethreaded(test)]
1038 async fn shutdown_watchers_notified_for_reboot_to_bootloader() {
1039 let registrar = ShutdownWatcher::new();
1040
1041 let (watcher_proxy, mut watcher_stream) =
1042 fidl::endpoints::create_proxy_and_stream::<fpower::ShutdownWatcherMarker>();
1043 registrar.add_shutdown_watcher(watcher_proxy).await;
1044
1045 registrar
1046 .handle_system_shutdown_message(Some(ShutdownOptionsWrapper::new(
1047 ShutdownAction::RebootToBootloader,
1048 ShutdownReason::HighTemperature,
1049 )))
1050 .await;
1051 let (action, reasons) = assert_matches!(
1052 watcher_stream.try_next().await.unwrap().unwrap(),
1053 fpower::ShutdownWatcherRequest::OnShutdown {
1054 options: fpower::ShutdownOptions{action: Some(action), reasons: Some(reasons), ..},
1055 ..
1056 } => (action, reasons)
1057 );
1058 assert_eq!(action, ShutdownAction::RebootToBootloader);
1059 assert_eq!(&reasons[..], [ShutdownReason::HighTemperature]);
1060 }
1061
1062 #[fasync::run_singlethreaded(test)]
1063 async fn shutdown_watchers_notified_for_reboot_to_recovery() {
1064 let registrar = ShutdownWatcher::new();
1065
1066 let (watcher_proxy, mut watcher_stream) =
1067 fidl::endpoints::create_proxy_and_stream::<fpower::ShutdownWatcherMarker>();
1068 registrar.add_shutdown_watcher(watcher_proxy).await;
1069
1070 registrar
1071 .handle_system_shutdown_message(Some(ShutdownOptionsWrapper::new(
1072 ShutdownAction::RebootToRecovery,
1073 ShutdownReason::HighTemperature,
1074 )))
1075 .await;
1076 let (action, reasons) = assert_matches!(
1077 watcher_stream.try_next().await.unwrap().unwrap(),
1078 fpower::ShutdownWatcherRequest::OnShutdown {
1079 options: fpower::ShutdownOptions{action: Some(action), reasons: Some(reasons), ..},
1080 ..
1081 } => (action, reasons)
1082 );
1083 assert_eq!(action, ShutdownAction::RebootToRecovery);
1084 assert_eq!(&reasons[..], [ShutdownReason::HighTemperature]);
1085 }
1086
1087 #[fuchsia::test]
1088 fn test_reboot_watcher_response_delay() {
1089 let mut exec = fasync::TestExecutor::new();
1090 let registrar = ShutdownWatcher::new();
1091
1092 let (watcher_proxy, mut watcher_stream) =
1094 fidl::endpoints::create_proxy_and_stream::<fpower::RebootWatcherMarker>();
1095 let fut = async {
1096 registrar.add_reboot_watcher(watcher_proxy).await;
1097 assert_eq!(registrar.reboot_watchers.lock().await.len(), 1);
1098 };
1099 exec.run_singlethreaded(fut);
1100
1101 let notify_future = registrar.notify_reboot_watchers(
1103 ShutdownOptionsWrapper::new(ShutdownAction::Reboot, ShutdownReason::HighTemperature),
1104 seconds(1.0),
1105 );
1106 futures::pin_mut!(notify_future);
1107
1108 assert!(exec.run_until_stalled(&mut notify_future).is_pending());
1111
1112 let fpower::RebootWatcherRequest::OnReboot { responder, .. } =
1114 exec.run_singlethreaded(&mut watcher_stream.try_next()).unwrap().unwrap();
1115 assert_matches!(responder.send(), Ok(()));
1116
1117 assert!(exec.run_until_stalled(&mut notify_future).is_ready());
1119 }
1120
1121 #[fuchsia::test]
1122 fn test_shutdown_watcher_response_delay() {
1123 let mut exec = fasync::TestExecutor::new();
1124 let registrar = ShutdownWatcher::new();
1125
1126 let (watcher_proxy, mut watcher_stream) =
1128 fidl::endpoints::create_proxy_and_stream::<fpower::ShutdownWatcherMarker>();
1129 let fut = async {
1130 registrar.add_shutdown_watcher(watcher_proxy).await;
1131 assert_eq!(registrar.shutdown_watchers.lock().await.len(), 1);
1132 };
1133 exec.run_singlethreaded(fut);
1134
1135 let notify_future = registrar.notify_shutdown_watchers(
1137 ShutdownOptionsWrapper::new(ShutdownAction::Reboot, ShutdownReason::HighTemperature),
1138 seconds(1.0),
1139 );
1140 futures::pin_mut!(notify_future);
1141
1142 assert!(exec.run_until_stalled(&mut notify_future).is_pending());
1145
1146 match exec.run_singlethreaded(&mut watcher_stream.try_next()).unwrap().unwrap() {
1148 fpower::ShutdownWatcherRequest::OnShutdown { responder, .. } => {
1149 assert_matches!(responder.send(), Ok(()));
1150 }
1151 fpower::ShutdownWatcherRequest::_UnknownMethod { .. } => unimplemented!(),
1152 }
1153
1154 assert!(exec.run_until_stalled(&mut notify_future).is_ready());
1156 }
1157
1158 #[fuchsia::test]
1162 fn test_reboot_watcher_response_timeout() {
1163 let mut exec = fasync::TestExecutor::new_with_fake_time();
1164 let registrar = ShutdownWatcher::new();
1165 exec.set_fake_time(fasync::MonotonicInstant::from_nanos(0));
1166
1167 let (watcher_proxy, _watcher_stream) =
1169 fidl::endpoints::create_proxy_and_stream::<fpower::RebootWatcherMarker>();
1170 let fut = async {
1171 registrar.add_reboot_watcher(watcher_proxy).await;
1172 assert_eq!(registrar.reboot_watchers.lock().await.len(), 1);
1173 };
1174 futures::pin_mut!(fut);
1175 exec.run_until_stalled(&mut fut).is_ready();
1176
1177 let notify_future = registrar.notify_reboot_watchers(
1179 ShutdownOptionsWrapper::new(ShutdownAction::Reboot, ShutdownReason::HighTemperature),
1180 seconds(1.0),
1181 );
1182 futures::pin_mut!(notify_future);
1183
1184 assert!(exec.run_until_stalled(&mut notify_future).is_pending());
1187
1188 assert_eq!(exec.wake_next_timer(), Some(fasync::MonotonicInstant::from_nanos(1e9 as i64)));
1190
1191 assert!(exec.run_until_stalled(&mut notify_future).is_ready());
1193
1194 let fut = async {
1196 assert_eq!(registrar.reboot_watchers.lock().await.len(), 0);
1197 };
1198 futures::pin_mut!(fut);
1199 exec.run_until_stalled(&mut fut).is_ready();
1200 }
1201
1202 #[fuchsia::test]
1206 fn test_shutdown_watcher_response_timeout() {
1207 let mut exec = fasync::TestExecutor::new_with_fake_time();
1208 let registrar = ShutdownWatcher::new();
1209 exec.set_fake_time(fasync::MonotonicInstant::from_nanos(0));
1210
1211 let (watcher_proxy, _watcher_stream) =
1213 fidl::endpoints::create_proxy_and_stream::<fpower::ShutdownWatcherMarker>();
1214 let fut = async {
1215 registrar.add_shutdown_watcher(watcher_proxy).await;
1216 assert_eq!(registrar.shutdown_watchers.lock().await.len(), 1);
1217 };
1218 futures::pin_mut!(fut);
1219 exec.run_until_stalled(&mut fut).is_ready();
1220
1221 let notify_future = registrar.notify_shutdown_watchers(
1223 ShutdownOptionsWrapper::new(ShutdownAction::Reboot, ShutdownReason::HighTemperature),
1224 seconds(1.0),
1225 );
1226 futures::pin_mut!(notify_future);
1227
1228 assert!(exec.run_until_stalled(&mut notify_future).is_pending());
1231
1232 assert_eq!(exec.wake_next_timer(), Some(fasync::MonotonicInstant::from_nanos(1e9 as i64)));
1234
1235 assert!(exec.run_until_stalled(&mut notify_future).is_ready());
1237
1238 let fut = async {
1240 assert_eq!(registrar.shutdown_watchers.lock().await.len(), 0);
1241 };
1242 futures::pin_mut!(fut);
1243 exec.run_until_stalled(&mut fut).is_ready();
1244 }
1245
1246 #[fasync::run_singlethreaded(test)]
1249 async fn test_watcher_register_fail() {
1250 let registrar = ShutdownWatcher::new();
1251
1252 let (register_proxy, register_stream) = fidl::endpoints::create_proxy_and_stream::<
1254 fpower::RebootMethodsWatcherRegisterMarker,
1255 >();
1256
1257 fasync::Task::local(async move {
1260 registrar.handle_reboot_register_request(register_stream).await;
1261 })
1262 .detach();
1263
1264 assert_matches!(register_proxy.as_channel().write(&[], &mut []), Ok(()));
1266
1267 assert_matches!(register_proxy.on_closed().await, Ok(zx::Signals::CHANNEL_PEER_CLOSED));
1269 }
1270
1271 #[fasync::run_singlethreaded(test)]
1274 async fn test_shutdown_watcher_register_fail() {
1275 let registrar = ShutdownWatcher::new();
1276
1277 let (register_proxy, register_stream) =
1279 fidl::endpoints::create_proxy_and_stream::<fpower::ShutdownWatcherRegisterMarker>();
1280
1281 fasync::Task::local(async move {
1284 registrar.handle_shutdown_register_request(register_stream).await;
1285 })
1286 .detach();
1287
1288 assert_matches!(register_proxy.as_channel().write(&[], &mut []), Ok(()));
1290
1291 assert_matches!(register_proxy.on_closed().await, Ok(zx::Signals::CHANNEL_PEER_CLOSED));
1293 }
1294
1295 #[fasync::run_singlethreaded(test)]
1298 async fn test_multiple_terminal_state_watchers() {
1299 let registrar = ShutdownWatcher::new();
1300
1301 let (watcher_proxy1, mut watcher_stream1) =
1303 fidl::endpoints::create_proxy_and_stream::<fpower::TerminalStateWatcherMarker>();
1304 registrar.add_terminal_state_watcher(watcher_proxy1).await;
1305
1306 let (watcher_proxy2, mut watcher_stream2) =
1307 fidl::endpoints::create_proxy_and_stream::<fpower::TerminalStateWatcherMarker>();
1308 registrar.add_terminal_state_watcher(watcher_proxy2).await;
1309
1310 let (watcher_proxy3, mut watcher_stream3) =
1311 fidl::endpoints::create_proxy_and_stream::<fpower::TerminalStateWatcherMarker>();
1312 registrar.add_terminal_state_watcher(watcher_proxy3).await;
1313
1314 registrar.notify_terminal_state_watchers(seconds(0.0)).await;
1316
1317 assert_matches!(
1319 watcher_stream1.try_next().await.unwrap().unwrap(),
1320 fpower::TerminalStateWatcherRequest::OnTerminalStateTransitionStarted { .. }
1321 );
1322 assert_matches!(
1323 watcher_stream2.try_next().await.unwrap().unwrap(),
1324 fpower::TerminalStateWatcherRequest::OnTerminalStateTransitionStarted { .. }
1325 );
1326 assert_matches!(
1327 watcher_stream3.try_next().await.unwrap().unwrap(),
1328 fpower::TerminalStateWatcherRequest::OnTerminalStateTransitionStarted { .. }
1329 );
1330 }
1331
1332 #[fuchsia::test]
1333 fn test_terminal_state_watcher_response_delay() {
1334 let mut exec = fasync::TestExecutor::new();
1335 let registrar = ShutdownWatcher::new();
1336
1337 let (watcher_proxy, mut watcher_stream) =
1339 fidl::endpoints::create_proxy_and_stream::<fpower::TerminalStateWatcherMarker>();
1340 let fut = async {
1341 registrar.add_terminal_state_watcher(watcher_proxy).await;
1342 assert_eq!(registrar.terminal_state_watchers.lock().await.len(), 1);
1343 };
1344 exec.run_singlethreaded(fut);
1345
1346 let notify_future = registrar.notify_terminal_state_watchers(seconds(1.0));
1348 futures::pin_mut!(notify_future);
1349
1350 assert!(exec.run_until_stalled(&mut notify_future).is_pending());
1353
1354 match exec.run_singlethreaded(&mut watcher_stream.try_next()).unwrap().unwrap() {
1356 fpower::TerminalStateWatcherRequest::OnTerminalStateTransitionStarted {
1357 responder,
1358 ..
1359 } => {
1360 assert_matches!(responder.send(), Ok(()));
1361 }
1362 fpower::TerminalStateWatcherRequest::_UnknownMethod { .. } => unimplemented!(),
1363 }
1364
1365 assert!(exec.run_until_stalled(&mut notify_future).is_ready());
1367 }
1368
1369 #[fuchsia::test]
1373 fn test_terminal_state_watcher_response_timeout() {
1374 let mut exec = fasync::TestExecutor::new_with_fake_time();
1375 let registrar = ShutdownWatcher::new();
1376 exec.set_fake_time(fasync::MonotonicInstant::from_nanos(0));
1377
1378 let (watcher_proxy, _watcher_stream) =
1380 fidl::endpoints::create_proxy_and_stream::<fpower::TerminalStateWatcherMarker>();
1381 let fut = async {
1382 registrar.add_terminal_state_watcher(watcher_proxy).await;
1383 assert_eq!(registrar.terminal_state_watchers.lock().await.len(), 1);
1384 };
1385 futures::pin_mut!(fut);
1386 exec.run_until_stalled(&mut fut).is_ready();
1387
1388 let notify_future = registrar.notify_terminal_state_watchers(seconds(1.0));
1390 futures::pin_mut!(notify_future);
1391
1392 assert!(exec.run_until_stalled(&mut notify_future).is_pending());
1395
1396 assert_eq!(exec.wake_next_timer(), Some(fasync::MonotonicInstant::from_nanos(1e9 as i64)));
1398
1399 assert!(exec.run_until_stalled(&mut notify_future).is_ready());
1401
1402 let fut = async {
1404 assert_eq!(registrar.terminal_state_watchers.lock().await.len(), 0);
1405 };
1406 futures::pin_mut!(fut);
1407 exec.run_until_stalled(&mut fut).is_ready();
1408 }
1409}