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 inspect: Arc<Mutex<InspectData>>,
43}
44
45impl ShutdownWatcher {
46 const NOTIFY_SHUTDOWN_RESPONSE_TIMEOUT: zx::MonotonicDuration =
47 zx::MonotonicDuration::from_seconds(
48 fpower::MAX_SHUTDOWN_WATCHER_RESPONSE_TIME_SECONDS as i64,
49 );
50
51 const NOTIFY_REBOOT_RESPONSE_TIMEOUT: zx::MonotonicDuration =
52 zx::MonotonicDuration::from_seconds(
53 fpower::MAX_REBOOT_WATCHER_RESPONSE_TIME_SECONDS as i64,
54 );
55
56 #[cfg(test)]
57 fn new() -> Arc<Self> {
58 let inspector = inspect::Inspector::new(fuchsia_inspect::InspectorConfig::default());
59 Self::new_with_inspector(&inspector)
60 }
61
62 pub fn new_with_inspector(inspector: &inspect::Inspector) -> Arc<Self> {
63 Arc::new(Self {
64 reboot_watchers: Arc::new(Mutex::new(HashMap::new())),
65 shutdown_watchers: Arc::new(Mutex::new(HashMap::new())),
66 inspect: Arc::new(Mutex::new(InspectData::new(
67 inspector.root(),
68 "ShutdownWatcher".to_string(),
69 ))),
70 })
71 }
72
73 pub async fn handle_reboot_register_request(
75 self: Arc<Self>,
76 mut stream: fpower::RebootMethodsWatcherRegisterRequestStream,
77 ) {
78 while let Ok(Some(req)) = stream.try_next().await {
79 match req {
80 fpower::RebootMethodsWatcherRegisterRequest::RegisterWatcher {
81 watcher,
82 responder,
83 } => {
84 self.add_reboot_watcher(watcher.into_proxy()).await;
85 let _ = responder.send();
86 }
87 }
88 }
89 }
90
91 pub async fn handle_shutdown_register_request(
93 self: Arc<Self>,
94 mut stream: fpower::ShutdownWatcherRegisterRequestStream,
95 ) {
96 while let Ok(Some(req)) = stream.try_next().await {
97 match req {
98 fpower::ShutdownWatcherRegisterRequest::RegisterWatcher { watcher, responder } => {
99 self.add_shutdown_watcher(watcher.into_proxy()).await;
100 let _ = responder.send();
101 }
102 fpower::ShutdownWatcherRegisterRequest::_UnknownMethod { ordinal, .. } => {
103 println!("[shutdown-shim]: error, unimplemented method ordinal: {ordinal}")
104 }
105 }
106 }
107 }
108
109 async fn add_reboot_watcher(&self, watcher: fpower::RebootWatcherProxy) {
111 fuchsia_trace::duration!(
112 c"shutdown-shim",
113 c"ShutdownWatcher::add_reboot_watcher",
114 "watcher" => watcher.as_channel().raw_handle()
115 );
116
117 println!("[shutdown-shim] Adding a reboot watcher");
119 let key = watcher.as_channel().raw_handle();
120 let proxy = watcher.clone();
121 let reboot_watchers = self.reboot_watchers.clone();
122 let inspect = self.inspect.clone();
123 fasync::Task::spawn(async move {
124 let _ = proxy.on_closed().await;
125 {
126 reboot_watchers.lock().await.remove(&key);
127 }
128 inspect.lock().await.remove_reboot_watcher();
129 })
130 .detach();
131
132 {
133 let mut watchers_mut = self.reboot_watchers.lock().await;
134 watchers_mut.insert(key, watcher);
135 }
136 self.inspect.lock().await.add_reboot_watcher();
137 }
138
139 async fn add_shutdown_watcher(&self, watcher: fpower::ShutdownWatcherProxy) {
141 fuchsia_trace::duration!(
142 c"shutdown-shim",
143 c"ShutdownWatcher::add_shutdown_watcher",
144 "watcher" => watcher.as_channel().raw_handle()
145 );
146
147 println!("[shutdown-shim] Adding a shutdown watcher");
149 let key = watcher.as_channel().raw_handle();
150 let proxy = watcher.clone();
151 let shutdown_watchers = self.shutdown_watchers.clone();
152 let inspect = self.inspect.clone();
153 fasync::Task::spawn(async move {
154 let _ = proxy.on_closed().await;
155 {
156 shutdown_watchers.lock().await.remove(&key);
157 }
158 inspect.lock().await.remove_reboot_watcher();
159 })
160 .detach();
161
162 {
163 let mut watchers_mut = self.shutdown_watchers.lock().await;
164 watchers_mut.insert(key, watcher);
165 }
166 self.inspect.lock().await.add_reboot_watcher();
167 }
168
169 pub async fn handle_system_shutdown_message(&self, options: ShutdownOptionsWrapper) {
171 if options.action == ShutdownAction::Reboot && !options.reasons.is_empty() {
172 futures::join!(
173 self.notify_shutdown_watchers(
174 options.clone(),
175 Self::NOTIFY_SHUTDOWN_RESPONSE_TIMEOUT
176 ),
177 self.notify_reboot_watchers(options, Self::NOTIFY_REBOOT_RESPONSE_TIMEOUT)
178 );
179 return;
180 }
181
182 self.notify_shutdown_watchers(options, Self::NOTIFY_SHUTDOWN_RESPONSE_TIMEOUT).await;
183 }
184
185 async fn notify_reboot_watchers(
186 &self,
187 options: ShutdownOptionsWrapper,
188 timeout: zx::MonotonicDuration,
189 ) {
190 fuchsia_trace::duration_begin!(
192 c"shutdown-shim",
193 c"ShutdownWatcher::notify_reboot_watchers",
194 "options" => format!("{:?}", options).as_str()
195 );
196
197 let watcher_futures = {
202 let watchers = self.reboot_watchers.lock().await;
204 println!("[shutdown-shim] notifying {:?} watchers of reboot", watchers.len());
205 watchers.clone().into_iter().map(|(key, watcher_proxy)| {
206 let options = options.clone();
207 async move {
208 let deadline = timeout.after_now();
209 let result = watcher_proxy
210 .on_reboot(&options.into())
211 .map_err(|_| ())
212 .on_timeout(deadline, || Err(()))
213 .await;
214 match result {
215 Ok(()) => Some((key, watcher_proxy)),
216 Err(()) => None,
217 }
218 }
219 })
220 };
221
222 let new_watchers = futures::future::join_all(watcher_futures)
224 .await
225 .into_iter()
226 .filter_map(|watcher_opt| watcher_opt) .collect();
228
229 *self.reboot_watchers.lock().await = new_watchers;
231
232 fuchsia_trace::duration_end!(
233 c"shutdown-shim",
234 c"ShutdownWatcher::notify_reboot_watchers",
235 "options" => format!("{:?}", options).as_str()
236 );
237 }
238
239 async fn notify_shutdown_watchers(
240 &self,
241 options: ShutdownOptionsWrapper,
242 timeout: zx::MonotonicDuration,
243 ) {
244 fuchsia_trace::duration_begin!(
246 c"shutdown-shim",
247 c"ShutdownWatcher::notify_shutdown_watchers",
248 "options" => format!("{:?}", options).as_str()
249 );
250
251 let watcher_futures = {
256 let watchers = self.shutdown_watchers.lock().await;
259 println!("[shutdown-shim] notifying {:?} watchers of shutdown", watchers.len());
260 watchers.clone().into_iter().map(|(key, watcher_proxy)| {
261 let options = options.clone();
262 async move {
263 let deadline = timeout.after_now();
264 let result = watcher_proxy
265 .on_shutdown(&options.into())
266 .map_err(|_| ())
267 .on_timeout(deadline, || Err(()))
268 .await;
269
270 match result {
271 Ok(()) => Some((key, watcher_proxy)),
272 Err(()) => None,
273 }
274 }
275 })
276 };
277
278 let new_watchers = futures::future::join_all(watcher_futures)
280 .await
281 .into_iter()
282 .filter_map(|watcher_opt| watcher_opt) .collect();
284
285 *self.shutdown_watchers.lock().await = new_watchers;
287
288 fuchsia_trace::duration_end!(
289 c"shutdown-shim",
290 c"ShutdownWatcher::notify_shutdown_watchers",
291 "options" => format!("{:?}", options).as_str()
292 );
293 }
294}
295
296struct InspectData {
297 reboot_watcher_current_connections: inspect::UintProperty,
298 reboot_watcher_total_connections: inspect::UintProperty,
299}
300
301impl InspectData {
305 fn new(parent: &inspect::Node, name: String) -> Self {
306 let root = parent.create_child(name);
308 let reboot_watcher_current_connections =
309 root.create_uint("reboot_watcher_current_connections", 0);
310 let reboot_watcher_total_connections =
311 root.create_uint("reboot_watcher_total_connections", 0);
312
313 parent.record(root);
315
316 InspectData { reboot_watcher_current_connections, reboot_watcher_total_connections }
317 }
318
319 fn add_reboot_watcher(&self) {
320 self.reboot_watcher_current_connections.add(1);
321 self.reboot_watcher_total_connections.add(1);
322 }
323
324 fn remove_reboot_watcher(&self) {
325 self.reboot_watcher_current_connections.subtract(1);
326 }
327}
328
329#[cfg(test)]
330mod tests {
331 use super::*;
332 use assert_matches::assert_matches;
333 use diagnostics_assertions::assert_data_tree;
334 use fidl::endpoints::{ControlHandle, RequestStream};
335 use fidl_fuchsia_hardware_power_statecontrol::{ShutdownAction, ShutdownReason};
336
337 fn seconds(seconds: f64) -> zx::MonotonicDuration {
339 zx::MonotonicDuration::from_seconds(seconds as i64)
340 }
341
342 #[fuchsia::test]
344 async fn test_inspect_data() {
345 let inspector = inspect::Inspector::default();
346 let registrar = ShutdownWatcher::new_with_inspector(&inspector);
347
348 assert_data_tree!(
349 inspector,
350 root: {
351 ShutdownWatcher: {
352 reboot_watcher_current_connections: 0u64,
353 reboot_watcher_total_connections: 0u64
354 }
355 }
356 );
357
358 let (watcher_proxy, s) = fidl::endpoints::create_proxy::<fpower::RebootWatcherMarker>();
359 registrar.add_reboot_watcher(watcher_proxy.clone()).await;
360
361 assert_data_tree!(
362 inspector,
363 root: {
364 ShutdownWatcher: {
365 reboot_watcher_current_connections: 1u64,
366 reboot_watcher_total_connections: 1u64
367 }
368 }
369 );
370
371 drop(s);
372 watcher_proxy.on_closed().await.expect("closed");
373
374 assert_data_tree!(
375 @retry 10,
376 inspector,
377 root: {
378 ShutdownWatcher: {
379 reboot_watcher_current_connections: 0u64,
380 reboot_watcher_total_connections: 1u64
381 }
382 }
383 );
384
385 let (watcher_proxy, s) = fidl::endpoints::create_proxy::<fpower::RebootWatcherMarker>();
386 registrar.add_reboot_watcher(watcher_proxy.clone()).await;
387
388 assert_data_tree!(
389 @retry 10,
390 inspector,
391 root: {
392 ShutdownWatcher: {
393 reboot_watcher_current_connections: 1u64,
394 reboot_watcher_total_connections: 2u64
395 }
396 }
397 );
398 drop(s);
399 watcher_proxy.on_closed().await.expect("closed");
400
401 assert_data_tree!(
402 @retry 10,
403 inspector,
404 root: {
405 ShutdownWatcher: {
406 reboot_watcher_current_connections: 0u64,
407 reboot_watcher_total_connections: 2u64
408 }
409 }
410 );
411
412 let (watcher_proxy, s) = fidl::endpoints::create_proxy::<fpower::ShutdownWatcherMarker>();
413 registrar.add_shutdown_watcher(watcher_proxy.clone()).await;
414
415 assert_data_tree!(
416 @retry 10,
417 inspector,
418 root: {
419 ShutdownWatcher: {
420 reboot_watcher_current_connections: 1u64,
421 reboot_watcher_total_connections: 3u64
422 }
423 }
424 );
425 drop(s);
426 watcher_proxy.on_closed().await.expect("closed");
427
428 assert_data_tree!(
429 @retry 10,
430 inspector,
431 root: {
432 ShutdownWatcher: {
433 reboot_watcher_current_connections: 0u64,
434 reboot_watcher_total_connections: 3u64
435 }
436 }
437 );
438 }
439
440 #[fasync::run_singlethreaded(test)]
443 async fn test_add_reboot_watcher() {
444 let registrar = ShutdownWatcher::new();
445
446 let (register_proxy, register_stream) = fidl::endpoints::create_proxy_and_stream::<
448 fpower::RebootMethodsWatcherRegisterMarker,
449 >();
450
451 let registrar_clone = registrar.clone();
454 fasync::Task::local(async move {
455 registrar_clone.handle_reboot_register_request(register_stream).await;
456 })
457 .detach();
458
459 let (watcher_client, mut watcher_stream) =
461 fidl::endpoints::create_request_stream::<fpower::RebootWatcherMarker>();
462
463 assert_matches!(register_proxy.register_watcher(watcher_client).await, Ok(()));
465 registrar
467 .notify_reboot_watchers(
468 ShutdownOptionsWrapper::new(ShutdownAction::Reboot, ShutdownReason::UserRequest),
469 seconds(0.0),
470 )
471 .await;
472
473 let reasons = assert_matches!(
475 watcher_stream.try_next().await.unwrap().unwrap(),
476 fpower::RebootWatcherRequest::OnReboot {
477 options: fpower::RebootOptions{reasons: Some(reasons), ..},
478 ..
479 } => reasons
480 );
481 assert_eq!(&reasons[..], [fpower::RebootReason2::UserRequest]);
482 }
483
484 #[fasync::run_singlethreaded(test)]
487 async fn test_add_shutdown_watcher() {
488 let registrar = ShutdownWatcher::new();
489
490 let (register_proxy, register_stream) =
492 fidl::endpoints::create_proxy_and_stream::<fpower::ShutdownWatcherRegisterMarker>();
493
494 let registrar_clone = registrar.clone();
497 fasync::Task::local(async move {
498 registrar_clone.handle_shutdown_register_request(register_stream).await;
499 })
500 .detach();
501
502 let (watcher_client, mut watcher_stream) =
504 fidl::endpoints::create_request_stream::<fpower::ShutdownWatcherMarker>();
505
506 assert_matches!(register_proxy.register_watcher(watcher_client).await, Ok(()));
508
509 registrar
511 .notify_shutdown_watchers(
512 ShutdownOptionsWrapper::new(ShutdownAction::Poweroff, ShutdownReason::UserRequest),
513 seconds(0.0),
514 )
515 .await;
516
517 let (action, reasons) = assert_matches!(
519 watcher_stream.try_next().await.unwrap().unwrap(),
520 fpower::ShutdownWatcherRequest::OnShutdown {
521 options: fpower::ShutdownOptions{action: Some(action), reasons: Some(reasons), ..},
522 ..
523 } => (action, reasons)
524 );
525 assert_eq!(action, ShutdownAction::Poweroff);
526 assert_eq!(&reasons[..], [ShutdownReason::UserRequest]);
527 }
528
529 #[fasync::run_singlethreaded(test)]
531 async fn test_reboot_watcher_reason() {
532 let registrar = ShutdownWatcher::new();
533 let (watcher_proxy, mut watcher_stream) =
534 fidl::endpoints::create_proxy_and_stream::<fpower::RebootWatcherMarker>();
535 registrar.add_reboot_watcher(watcher_proxy).await;
536 registrar
537 .notify_reboot_watchers(
538 ShutdownOptionsWrapper::new(
539 ShutdownAction::Reboot,
540 ShutdownReason::HighTemperature,
541 ),
542 seconds(0.0),
543 )
544 .await;
545
546 let reasons = match watcher_stream.try_next().await {
547 Ok(Some(fpower::RebootWatcherRequest::OnReboot {
548 options: fpower::RebootOptions { reasons: Some(reasons), .. },
549 ..
550 })) => reasons,
551 e => panic!("Unexpected watcher_stream result: {:?}", e),
552 };
553
554 assert_eq!(&reasons[..], [fpower::RebootReason2::HighTemperature]);
555 }
556
557 #[fasync::run_singlethreaded(test)]
560 async fn test_shutdown_watcher_reason() {
561 let registrar = ShutdownWatcher::new();
562 let (watcher_proxy, mut watcher_stream) =
563 fidl::endpoints::create_proxy_and_stream::<fpower::ShutdownWatcherMarker>();
564 registrar.add_shutdown_watcher(watcher_proxy).await;
565 registrar
566 .notify_shutdown_watchers(
567 ShutdownOptionsWrapper::new(
568 ShutdownAction::Poweroff,
569 ShutdownReason::HighTemperature,
570 ),
571 seconds(0.0),
572 )
573 .await;
574
575 let (action, reasons) = assert_matches!(
576 watcher_stream.try_next().await.unwrap().unwrap(),
577 fpower::ShutdownWatcherRequest::OnShutdown {
578 options: fpower::ShutdownOptions{action: Some(action), reasons: Some(reasons), ..},
579 ..
580 } => (action, reasons)
581 );
582 assert_eq!(action, ShutdownAction::Poweroff);
583 assert_eq!(&reasons[..], [ShutdownReason::HighTemperature]);
584 }
585
586 #[fasync::run_singlethreaded(test)]
589 async fn test_multiple_reboot_watchers() {
590 let registrar = ShutdownWatcher::new();
591
592 let (watcher_proxy1, mut watcher_stream1) =
594 fidl::endpoints::create_proxy_and_stream::<fpower::RebootWatcherMarker>();
595 registrar.add_reboot_watcher(watcher_proxy1).await;
596
597 let (watcher_proxy2, mut watcher_stream2) =
598 fidl::endpoints::create_proxy_and_stream::<fpower::RebootWatcherMarker>();
599 registrar.add_reboot_watcher(watcher_proxy2).await;
600
601 let (watcher_proxy3, mut watcher_stream3) =
602 fidl::endpoints::create_proxy_and_stream::<fpower::RebootWatcherMarker>();
603 registrar.add_reboot_watcher(watcher_proxy3).await;
604
605 watcher_stream1.control_handle().shutdown();
608
609 registrar
610 .notify_reboot_watchers(
611 ShutdownOptionsWrapper::new(
612 ShutdownAction::Reboot,
613 ShutdownReason::HighTemperature,
614 ),
615 seconds(0.0),
616 )
617 .await;
618
619 match watcher_stream1.try_next().await {
621 Ok(None) => {}
622 e => panic!("Unexpected watcher_stream1 result: {:?}", e),
623 };
624
625 match watcher_stream2.try_next().await {
627 Ok(Some(fpower::RebootWatcherRequest::OnReboot {
628 options: fpower::RebootOptions { reasons: Some(reasons), .. },
629 ..
630 })) => {
631 assert_eq!(&reasons[..], [fpower::RebootReason2::HighTemperature])
632 }
633 e => panic!("Unexpected watcher_stream2 result: {:?}", e),
634 };
635
636 match watcher_stream3.try_next().await {
638 Ok(Some(fpower::RebootWatcherRequest::OnReboot {
639 options: fpower::RebootOptions { reasons: Some(reasons), .. },
640 ..
641 })) => assert_eq!(&reasons[..], [fpower::RebootReason2::HighTemperature]),
642 e => panic!("Unexpected watcher_stream3 result: {:?}", e),
643 };
644 }
645
646 #[fasync::run_singlethreaded(test)]
649 async fn test_multiple_shutdown_watchers() {
650 let registrar = ShutdownWatcher::new();
651
652 let (watcher_proxy1, mut watcher_stream1) =
654 fidl::endpoints::create_proxy_and_stream::<fpower::ShutdownWatcherMarker>();
655 registrar.add_shutdown_watcher(watcher_proxy1).await;
656
657 let (watcher_proxy2, mut watcher_stream2) =
658 fidl::endpoints::create_proxy_and_stream::<fpower::ShutdownWatcherMarker>();
659 registrar.add_shutdown_watcher(watcher_proxy2).await;
660
661 let (watcher_proxy3, mut watcher_stream3) =
662 fidl::endpoints::create_proxy_and_stream::<fpower::ShutdownWatcherMarker>();
663 registrar.add_shutdown_watcher(watcher_proxy3).await;
664
665 watcher_stream1.control_handle().shutdown();
668
669 registrar
670 .notify_shutdown_watchers(
671 ShutdownOptionsWrapper::new(
672 ShutdownAction::Reboot,
673 ShutdownReason::HighTemperature,
674 ),
675 seconds(0.0),
676 )
677 .await;
678
679 match watcher_stream1.try_next().await {
681 Ok(None) => {}
682 e => panic!("Unexpected watcher_stream1 result: {:?}", e),
683 };
684
685 let (action, reasons) = assert_matches!(
687 watcher_stream2.try_next().await.unwrap().unwrap(),
688 fpower::ShutdownWatcherRequest::OnShutdown {
689 options: fpower::ShutdownOptions{action: Some(action), reasons: Some(reasons), ..},
690 ..
691 } => (action, reasons)
692 );
693 assert_eq!(action, ShutdownAction::Reboot);
694 assert_eq!(&reasons[..], [ShutdownReason::HighTemperature]);
695
696 let (action, reasons) = assert_matches!(
698 watcher_stream3.try_next().await.unwrap().unwrap(),
699 fpower::ShutdownWatcherRequest::OnShutdown {
700 options: fpower::ShutdownOptions{action: Some(action), reasons: Some(reasons), ..},
701 ..
702 } => (action, reasons)
703 );
704 assert_eq!(action, ShutdownAction::Reboot);
705 assert_eq!(&reasons[..], [ShutdownReason::HighTemperature]);
706 }
707
708 #[fasync::run_singlethreaded(test)]
711 async fn reboot_watchers_only_notified_of_reboots() {
712 let registrar = ShutdownWatcher::new();
713 let (watcher_proxy, mut watcher_stream) =
714 fidl::endpoints::create_proxy_and_stream::<fpower::RebootWatcherMarker>();
715 registrar.add_reboot_watcher(watcher_proxy).await;
716
717 registrar
718 .handle_system_shutdown_message(ShutdownOptionsWrapper::new(
719 ShutdownAction::Poweroff,
720 ShutdownReason::HighTemperature,
721 ))
722 .await;
723 assert!(futures::poll!(watcher_stream.try_next()).is_pending());
724
725 registrar
726 .handle_system_shutdown_message(ShutdownOptionsWrapper::new(
727 ShutdownAction::RebootToBootloader,
728 ShutdownReason::HighTemperature,
729 ))
730 .await;
731 assert!(futures::poll!(watcher_stream.try_next()).is_pending());
732
733 registrar
734 .handle_system_shutdown_message(ShutdownOptionsWrapper::new(
735 ShutdownAction::RebootToRecovery,
736 ShutdownReason::HighTemperature,
737 ))
738 .await;
739 assert!(futures::poll!(watcher_stream.try_next()).is_pending());
740
741 registrar
742 .handle_system_shutdown_message(ShutdownOptionsWrapper::new(
743 ShutdownAction::Reboot,
744 ShutdownReason::HighTemperature,
745 ))
746 .await;
747 match watcher_stream.try_next().await {
748 Ok(Some(fpower::RebootWatcherRequest::OnReboot {
749 options: fpower::RebootOptions { reasons: Some(reasons), .. },
750 ..
751 })) => {
752 assert_eq!(&reasons[..], [fpower::RebootReason2::HighTemperature])
753 }
754 e => panic!("Unexpected watcher_stream result: {:?}", e),
755 };
756 }
757
758 #[fasync::run_singlethreaded(test)]
761 async fn shutdown_and_reboot_watchers_notified() {
762 let registrar = ShutdownWatcher::new();
763
764 let (shutdown_watcher_proxy, mut shutdown_watcher_stream) =
765 fidl::endpoints::create_proxy_and_stream::<fpower::ShutdownWatcherMarker>();
766 registrar.add_shutdown_watcher(shutdown_watcher_proxy).await;
767
768 let (reboot_watcher_proxy, mut reboot_watcher_stream) =
769 fidl::endpoints::create_proxy_and_stream::<fpower::RebootWatcherMarker>();
770 registrar.add_reboot_watcher(reboot_watcher_proxy).await;
771
772 registrar
773 .handle_system_shutdown_message(ShutdownOptionsWrapper::new(
774 ShutdownAction::Reboot,
775 ShutdownReason::HighTemperature,
776 ))
777 .await;
778
779 let (action, reasons) = assert_matches!(
780 shutdown_watcher_stream.try_next().await.unwrap().unwrap(),
781 fpower::ShutdownWatcherRequest::OnShutdown {
782 options: fpower::ShutdownOptions{action: Some(action), reasons: Some(reasons), ..},
783 ..
784 } => (action, reasons)
785 );
786 assert_eq!(action, ShutdownAction::Reboot);
787 assert_eq!(&reasons[..], [ShutdownReason::HighTemperature]);
788
789 match reboot_watcher_stream.try_next().await {
790 Ok(Some(fpower::RebootWatcherRequest::OnReboot {
791 options: fpower::RebootOptions { reasons: Some(reasons), .. },
792 ..
793 })) => {
794 assert_eq!(&reasons[..], [fpower::RebootReason2::HighTemperature])
795 }
796 e => panic!("Unexpected watcher_stream result: {:?}", e),
797 };
798 }
799
800 #[fasync::run_singlethreaded(test)]
801 async fn shutdown_watchers_notified_for_poweroff() {
802 let registrar = ShutdownWatcher::new();
803
804 let (watcher_proxy, mut watcher_stream) =
805 fidl::endpoints::create_proxy_and_stream::<fpower::ShutdownWatcherMarker>();
806 registrar.add_shutdown_watcher(watcher_proxy).await;
807
808 registrar
809 .handle_system_shutdown_message(ShutdownOptionsWrapper::new(
810 ShutdownAction::Poweroff,
811 ShutdownReason::HighTemperature,
812 ))
813 .await;
814 let (action, reasons) = assert_matches!(
815 watcher_stream.try_next().await.unwrap().unwrap(),
816 fpower::ShutdownWatcherRequest::OnShutdown {
817 options: fpower::ShutdownOptions{action: Some(action), reasons: Some(reasons), ..},
818 ..
819 } => (action, reasons)
820 );
821 assert_eq!(action, ShutdownAction::Poweroff);
822 assert_eq!(&reasons[..], [ShutdownReason::HighTemperature]);
823 }
824
825 #[fasync::run_singlethreaded(test)]
826 async fn shutdown_watchers_notified_for_reboot_to_bootloader() {
827 let registrar = ShutdownWatcher::new();
828
829 let (watcher_proxy, mut watcher_stream) =
830 fidl::endpoints::create_proxy_and_stream::<fpower::ShutdownWatcherMarker>();
831 registrar.add_shutdown_watcher(watcher_proxy).await;
832
833 registrar
834 .handle_system_shutdown_message(ShutdownOptionsWrapper::new(
835 ShutdownAction::RebootToBootloader,
836 ShutdownReason::HighTemperature,
837 ))
838 .await;
839 let (action, reasons) = assert_matches!(
840 watcher_stream.try_next().await.unwrap().unwrap(),
841 fpower::ShutdownWatcherRequest::OnShutdown {
842 options: fpower::ShutdownOptions{action: Some(action), reasons: Some(reasons), ..},
843 ..
844 } => (action, reasons)
845 );
846 assert_eq!(action, ShutdownAction::RebootToBootloader);
847 assert_eq!(&reasons[..], [ShutdownReason::HighTemperature]);
848 }
849
850 #[fasync::run_singlethreaded(test)]
851 async fn shutdown_watchers_notified_for_reboot_to_recovery() {
852 let registrar = ShutdownWatcher::new();
853
854 let (watcher_proxy, mut watcher_stream) =
855 fidl::endpoints::create_proxy_and_stream::<fpower::ShutdownWatcherMarker>();
856 registrar.add_shutdown_watcher(watcher_proxy).await;
857
858 registrar
859 .handle_system_shutdown_message(ShutdownOptionsWrapper::new(
860 ShutdownAction::RebootToRecovery,
861 ShutdownReason::HighTemperature,
862 ))
863 .await;
864 let (action, reasons) = assert_matches!(
865 watcher_stream.try_next().await.unwrap().unwrap(),
866 fpower::ShutdownWatcherRequest::OnShutdown {
867 options: fpower::ShutdownOptions{action: Some(action), reasons: Some(reasons), ..},
868 ..
869 } => (action, reasons)
870 );
871 assert_eq!(action, ShutdownAction::RebootToRecovery);
872 assert_eq!(&reasons[..], [ShutdownReason::HighTemperature]);
873 }
874
875 #[fuchsia::test]
876 fn test_reboot_watcher_response_delay() {
877 let mut exec = fasync::TestExecutor::new();
878 let registrar = ShutdownWatcher::new();
879
880 let (watcher_proxy, mut watcher_stream) =
882 fidl::endpoints::create_proxy_and_stream::<fpower::RebootWatcherMarker>();
883 let fut = async {
884 registrar.add_reboot_watcher(watcher_proxy).await;
885 assert_eq!(registrar.reboot_watchers.lock().await.len(), 1);
886 };
887 exec.run_singlethreaded(fut);
888
889 let notify_future = registrar.notify_reboot_watchers(
891 ShutdownOptionsWrapper::new(ShutdownAction::Reboot, ShutdownReason::HighTemperature),
892 seconds(1.0),
893 );
894 futures::pin_mut!(notify_future);
895
896 assert!(exec.run_until_stalled(&mut notify_future).is_pending());
899
900 let fpower::RebootWatcherRequest::OnReboot { responder, .. } =
902 exec.run_singlethreaded(&mut watcher_stream.try_next()).unwrap().unwrap();
903 assert_matches!(responder.send(), Ok(()));
904
905 assert!(exec.run_until_stalled(&mut notify_future).is_ready());
907 }
908
909 #[fuchsia::test]
910 fn test_shutdown_watcher_response_delay() {
911 let mut exec = fasync::TestExecutor::new();
912 let registrar = ShutdownWatcher::new();
913
914 let (watcher_proxy, mut watcher_stream) =
916 fidl::endpoints::create_proxy_and_stream::<fpower::ShutdownWatcherMarker>();
917 let fut = async {
918 registrar.add_shutdown_watcher(watcher_proxy).await;
919 assert_eq!(registrar.shutdown_watchers.lock().await.len(), 1);
920 };
921 exec.run_singlethreaded(fut);
922
923 let notify_future = registrar.notify_shutdown_watchers(
925 ShutdownOptionsWrapper::new(ShutdownAction::Reboot, ShutdownReason::HighTemperature),
926 seconds(1.0),
927 );
928 futures::pin_mut!(notify_future);
929
930 assert!(exec.run_until_stalled(&mut notify_future).is_pending());
933
934 match exec.run_singlethreaded(&mut watcher_stream.try_next()).unwrap().unwrap() {
936 fpower::ShutdownWatcherRequest::OnShutdown { responder, .. } => {
937 assert_matches!(responder.send(), Ok(()));
938 }
939 fpower::ShutdownWatcherRequest::_UnknownMethod { .. } => unimplemented!(),
940 }
941
942 assert!(exec.run_until_stalled(&mut notify_future).is_ready());
944 }
945
946 #[fuchsia::test]
950 fn test_reboot_watcher_response_timeout() {
951 let mut exec = fasync::TestExecutor::new_with_fake_time();
952 let registrar = ShutdownWatcher::new();
953 exec.set_fake_time(fasync::MonotonicInstant::from_nanos(0));
954
955 let (watcher_proxy, _watcher_stream) =
957 fidl::endpoints::create_proxy_and_stream::<fpower::RebootWatcherMarker>();
958 let fut = async {
959 registrar.add_reboot_watcher(watcher_proxy).await;
960 assert_eq!(registrar.reboot_watchers.lock().await.len(), 1);
961 };
962 futures::pin_mut!(fut);
963 exec.run_until_stalled(&mut fut).is_ready();
964
965 let notify_future = registrar.notify_reboot_watchers(
967 ShutdownOptionsWrapper::new(ShutdownAction::Reboot, ShutdownReason::HighTemperature),
968 seconds(1.0),
969 );
970 futures::pin_mut!(notify_future);
971
972 assert!(exec.run_until_stalled(&mut notify_future).is_pending());
975
976 assert_eq!(exec.wake_next_timer(), Some(fasync::MonotonicInstant::from_nanos(1e9 as i64)));
978
979 assert!(exec.run_until_stalled(&mut notify_future).is_ready());
981
982 let fut = async {
984 assert_eq!(registrar.reboot_watchers.lock().await.len(), 0);
985 };
986 futures::pin_mut!(fut);
987 exec.run_until_stalled(&mut fut).is_ready();
988 }
989
990 #[fuchsia::test]
994 fn test_shutdown_watcher_response_timeout() {
995 let mut exec = fasync::TestExecutor::new_with_fake_time();
996 let registrar = ShutdownWatcher::new();
997 exec.set_fake_time(fasync::MonotonicInstant::from_nanos(0));
998
999 let (watcher_proxy, _watcher_stream) =
1001 fidl::endpoints::create_proxy_and_stream::<fpower::ShutdownWatcherMarker>();
1002 let fut = async {
1003 registrar.add_shutdown_watcher(watcher_proxy).await;
1004 assert_eq!(registrar.shutdown_watchers.lock().await.len(), 1);
1005 };
1006 futures::pin_mut!(fut);
1007 exec.run_until_stalled(&mut fut).is_ready();
1008
1009 let notify_future = registrar.notify_shutdown_watchers(
1011 ShutdownOptionsWrapper::new(ShutdownAction::Reboot, ShutdownReason::HighTemperature),
1012 seconds(1.0),
1013 );
1014 futures::pin_mut!(notify_future);
1015
1016 assert!(exec.run_until_stalled(&mut notify_future).is_pending());
1019
1020 assert_eq!(exec.wake_next_timer(), Some(fasync::MonotonicInstant::from_nanos(1e9 as i64)));
1022
1023 assert!(exec.run_until_stalled(&mut notify_future).is_ready());
1025
1026 let fut = async {
1028 assert_eq!(registrar.shutdown_watchers.lock().await.len(), 0);
1029 };
1030 futures::pin_mut!(fut);
1031 exec.run_until_stalled(&mut fut).is_ready();
1032 }
1033
1034 #[fasync::run_singlethreaded(test)]
1037 async fn test_watcher_register_fail() {
1038 let registrar = ShutdownWatcher::new();
1039
1040 let (register_proxy, register_stream) = fidl::endpoints::create_proxy_and_stream::<
1042 fpower::RebootMethodsWatcherRegisterMarker,
1043 >();
1044
1045 fasync::Task::local(async move {
1048 registrar.handle_reboot_register_request(register_stream).await;
1049 })
1050 .detach();
1051
1052 assert_matches!(register_proxy.as_channel().write(&[], &mut []), Ok(()));
1054
1055 assert_matches!(register_proxy.on_closed().await, Ok(zx::Signals::CHANNEL_PEER_CLOSED));
1057 }
1058
1059 #[fasync::run_singlethreaded(test)]
1062 async fn test_shutdown_watcher_register_fail() {
1063 let registrar = ShutdownWatcher::new();
1064
1065 let (register_proxy, register_stream) =
1067 fidl::endpoints::create_proxy_and_stream::<fpower::ShutdownWatcherRegisterMarker>();
1068
1069 fasync::Task::local(async move {
1072 registrar.handle_shutdown_register_request(register_stream).await;
1073 })
1074 .detach();
1075
1076 assert_matches!(register_proxy.as_channel().write(&[], &mut []), Ok(()));
1078
1079 assert_matches!(register_proxy.on_closed().await, Ok(zx::Signals::CHANNEL_PEER_CLOSED));
1081 }
1082}