1use crate::reboot_reasons::ShutdownOptionsWrapper;
6
7use either::Either;
8use fidl::endpoints::Proxy;
9use fidl_fuchsia_hardware_power_statecontrol::{self as fpower, ShutdownAction};
10use fuchsia_async::{self as fasync, DurationExt, TimeoutExt};
11use fuchsia_inspect::{self as inspect, NumericProperty};
12use futures::TryStreamExt;
13use futures::lock::Mutex;
14use futures::prelude::*;
15use std::collections::HashMap;
16use std::sync::Arc;
17use zx::AsHandleRef;
18use zx::sys::zx_handle_t;
19
20pub struct ShutdownWatcher {
37 reboot_watchers: Arc<
40 Mutex<
41 HashMap<u32, Either<fpower::RebootMethodsWatcherProxy, fpower::RebootWatcherProxy>>,
45 >,
46 >,
47 shutdown_watchers: Arc<Mutex<HashMap<zx_handle_t, fpower::ShutdownWatcherProxy>>>,
50 inspect: Arc<Mutex<InspectData>>,
51}
52
53impl ShutdownWatcher {
54 const NOTIFY_SHUTDOWN_RESPONSE_TIMEOUT: zx::MonotonicDuration =
55 zx::MonotonicDuration::from_seconds(
56 fpower::MAX_SHUTDOWN_WATCHER_RESPONSE_TIME_SECONDS as i64,
57 );
58
59 const NOTIFY_REBOOT_RESPONSE_TIMEOUT: zx::MonotonicDuration =
60 zx::MonotonicDuration::from_seconds(
61 fpower::MAX_REBOOT_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 inspect: Arc::new(Mutex::new(InspectData::new(
75 inspector.root(),
76 "ShutdownWatcher".to_string(),
77 ))),
78 })
79 }
80
81 pub async fn handle_reboot_register_request(
83 self: Arc<Self>,
84 mut stream: fpower::RebootMethodsWatcherRegisterRequestStream,
85 ) {
86 while let Ok(Some(req)) = stream.try_next().await {
87 match req {
88 fpower::RebootMethodsWatcherRegisterRequest::Register {
91 watcher,
92 control_handle: _,
93 } => {
94 self.add_deprecated_reboot_watcher(watcher.into_proxy()).await;
95 }
96 fpower::RebootMethodsWatcherRegisterRequest::RegisterWithAck {
99 watcher,
100 responder,
101 } => {
102 self.add_deprecated_reboot_watcher(watcher.into_proxy()).await;
103 let _ = responder.send();
104 }
105 fpower::RebootMethodsWatcherRegisterRequest::RegisterWatcher {
106 watcher,
107 responder,
108 } => {
109 self.add_reboot_watcher(watcher.into_proxy()).await;
110 let _ = responder.send();
111 }
112 }
113 }
114 }
115
116 pub async fn handle_shutdown_register_request(
118 self: Arc<Self>,
119 mut stream: fpower::ShutdownWatcherRegisterRequestStream,
120 ) {
121 while let Ok(Some(req)) = stream.try_next().await {
122 match req {
123 fpower::ShutdownWatcherRegisterRequest::RegisterWatcher { watcher, responder } => {
124 self.add_shutdown_watcher(watcher.into_proxy()).await;
125 let _ = responder.send();
126 }
127 fpower::ShutdownWatcherRegisterRequest::_UnknownMethod { ordinal, .. } => {
128 println!("[shutdown-shim]: error, unimplemented method ordinal: {ordinal}")
129 }
130 }
131 }
132 }
133
134 async fn add_deprecated_reboot_watcher(&self, watcher: fpower::RebootMethodsWatcherProxy) {
136 fuchsia_trace::duration!(
137 c"shutdown-shim",
138 c"ShutdownWatcher::add_deprecated_reboot_watcher",
139 "watcher" => watcher.as_channel().raw_handle()
140 );
141
142 println!("[shutdown-shim] Adding a deprecated reboot watcher");
143 let key = watcher.as_channel().raw_handle();
146 let proxy = watcher.clone();
147 let reboot_watchers = self.reboot_watchers.clone();
148 let inspect = self.inspect.clone();
149 fasync::Task::spawn(async move {
150 let _ = proxy.on_closed().await;
151 {
152 reboot_watchers.lock().await.remove(&key);
153 }
154 inspect.lock().await.remove_reboot_watcher();
155 })
156 .detach();
157
158 {
159 let mut watchers_mut = self.reboot_watchers.lock().await;
160 watchers_mut.insert(key, Either::Left(watcher));
161 }
162 self.inspect.lock().await.add_reboot_watcher();
163 }
164
165 async fn add_reboot_watcher(&self, watcher: fpower::RebootWatcherProxy) {
167 fuchsia_trace::duration!(
168 c"shutdown-shim",
169 c"ShutdownWatcher::add_reboot_watcher",
170 "watcher" => watcher.as_channel().raw_handle()
171 );
172
173 println!("[shutdown-shim] Adding a reboot watcher");
175 let key = watcher.as_channel().raw_handle();
176 let proxy = watcher.clone();
177 let reboot_watchers = self.reboot_watchers.clone();
178 let inspect = self.inspect.clone();
179 fasync::Task::spawn(async move {
180 let _ = proxy.on_closed().await;
181 {
182 reboot_watchers.lock().await.remove(&key);
183 }
184 inspect.lock().await.remove_reboot_watcher();
185 })
186 .detach();
187
188 {
189 let mut watchers_mut = self.reboot_watchers.lock().await;
190 watchers_mut.insert(key, Either::Right(watcher));
191 }
192 self.inspect.lock().await.add_reboot_watcher();
193 }
194
195 async fn add_shutdown_watcher(&self, watcher: fpower::ShutdownWatcherProxy) {
197 fuchsia_trace::duration!(
198 c"shutdown-shim",
199 c"ShutdownWatcher::add_shutdown_watcher",
200 "watcher" => watcher.as_channel().raw_handle()
201 );
202
203 println!("[shutdown-shim] Adding a shutdown watcher");
205 let key = watcher.as_channel().raw_handle();
206 let proxy = watcher.clone();
207 let shutdown_watchers = self.shutdown_watchers.clone();
208 let inspect = self.inspect.clone();
209 fasync::Task::spawn(async move {
210 let _ = proxy.on_closed().await;
211 {
212 shutdown_watchers.lock().await.remove(&key);
213 }
214 inspect.lock().await.remove_reboot_watcher();
215 })
216 .detach();
217
218 {
219 let mut watchers_mut = self.shutdown_watchers.lock().await;
220 watchers_mut.insert(key, watcher);
221 }
222 self.inspect.lock().await.add_reboot_watcher();
223 }
224
225 pub async fn handle_system_shutdown_message(&self, options: ShutdownOptionsWrapper) {
227 if options.action == ShutdownAction::Reboot {
228 futures::join!(
229 self.notify_shutdown_watchers(
230 options.clone(),
231 Self::NOTIFY_SHUTDOWN_RESPONSE_TIMEOUT
232 ),
233 self.notify_reboot_watchers(options, Self::NOTIFY_REBOOT_RESPONSE_TIMEOUT)
234 );
235 return;
236 }
237
238 self.notify_shutdown_watchers(options, Self::NOTIFY_SHUTDOWN_RESPONSE_TIMEOUT).await;
239 }
240
241 async fn notify_reboot_watchers(
242 &self,
243 options: ShutdownOptionsWrapper,
244 timeout: zx::MonotonicDuration,
245 ) {
246 fuchsia_trace::duration_begin!(
248 c"shutdown-shim",
249 c"ShutdownWatcher::notify_reboot_watchers",
250 "options" => format!("{:?}", options).as_str()
251 );
252
253 let watcher_futures = {
258 let watchers = self.reboot_watchers.lock().await;
260 println!("[shutdown-shim] notifying {:?} watchers of reboot", watchers.len());
261 watchers.clone().into_iter().map(|(key, watcher_proxy)| {
262 let options = options.clone();
263 async move {
264 let deadline = timeout.after_now();
265 let result = match &watcher_proxy {
266 Either::Left(proxy) => futures::future::Either::Left(
267 proxy.on_reboot(options.to_reboot_reason_deprecated()),
268 ),
269 Either::Right(proxy) => {
270 futures::future::Either::Right(proxy.on_reboot(&options.into()))
271 }
272 }
273 .map_err(|_| ())
274 .on_timeout(deadline, || Err(()))
275 .await;
276
277 match result {
278 Ok(()) => Some((key, watcher_proxy)),
279 Err(()) => None,
280 }
281 }
282 })
283 };
284
285 let new_watchers = futures::future::join_all(watcher_futures)
287 .await
288 .into_iter()
289 .filter_map(|watcher_opt| watcher_opt) .collect();
291
292 *self.reboot_watchers.lock().await = new_watchers;
294
295 fuchsia_trace::duration_end!(
296 c"shutdown-shim",
297 c"ShutdownWatcher::notify_reboot_watchers",
298 "options" => format!("{:?}", options).as_str()
299 );
300 }
301
302 async fn notify_shutdown_watchers(
303 &self,
304 options: ShutdownOptionsWrapper,
305 timeout: zx::MonotonicDuration,
306 ) {
307 fuchsia_trace::duration_begin!(
309 c"shutdown-shim",
310 c"ShutdownWatcher::notify_shutdown_watchers",
311 "options" => format!("{:?}", options).as_str()
312 );
313
314 let watcher_futures = {
319 let watchers = self.shutdown_watchers.lock().await;
322 println!("[shutdown-shim] notifying {:?} watchers of shutdown", watchers.len());
323 watchers.clone().into_iter().map(|(key, watcher_proxy)| {
324 let options = options.clone();
325 async move {
326 let deadline = timeout.after_now();
327 let result = watcher_proxy
328 .on_shutdown(&options.into())
329 .map_err(|_| ())
330 .on_timeout(deadline, || Err(()))
331 .await;
332
333 match result {
334 Ok(()) => Some((key, watcher_proxy)),
335 Err(()) => None,
336 }
337 }
338 })
339 };
340
341 let new_watchers = futures::future::join_all(watcher_futures)
343 .await
344 .into_iter()
345 .filter_map(|watcher_opt| watcher_opt) .collect();
347
348 *self.shutdown_watchers.lock().await = new_watchers;
350
351 fuchsia_trace::duration_end!(
352 c"shutdown-shim",
353 c"ShutdownWatcher::notify_shutdown_watchers",
354 "options" => format!("{:?}", options).as_str()
355 );
356 }
357}
358
359struct InspectData {
360 reboot_watcher_current_connections: inspect::UintProperty,
361 reboot_watcher_total_connections: inspect::UintProperty,
362}
363
364impl InspectData {
368 fn new(parent: &inspect::Node, name: String) -> Self {
369 let root = parent.create_child(name);
371 let reboot_watcher_current_connections =
372 root.create_uint("reboot_watcher_current_connections", 0);
373 let reboot_watcher_total_connections =
374 root.create_uint("reboot_watcher_total_connections", 0);
375
376 parent.record(root);
378
379 InspectData { reboot_watcher_current_connections, reboot_watcher_total_connections }
380 }
381
382 fn add_reboot_watcher(&self) {
383 self.reboot_watcher_current_connections.add(1);
384 self.reboot_watcher_total_connections.add(1);
385 }
386
387 fn remove_reboot_watcher(&self) {
388 self.reboot_watcher_current_connections.subtract(1);
389 }
390}
391
392#[cfg(test)]
393mod tests {
394 use super::*;
395 use assert_matches::assert_matches;
396 use diagnostics_assertions::assert_data_tree;
397 use fidl::endpoints::{ControlHandle, RequestStream};
398 use fidl_fuchsia_hardware_power_statecontrol::{ShutdownAction, ShutdownReason};
399
400 fn seconds(seconds: f64) -> zx::MonotonicDuration {
402 zx::MonotonicDuration::from_seconds(seconds as i64)
403 }
404
405 #[fuchsia::test]
407 async fn test_inspect_data() {
408 let inspector = inspect::Inspector::default();
409 let registrar = ShutdownWatcher::new_with_inspector(&inspector);
410
411 assert_data_tree!(
412 inspector,
413 root: {
414 ShutdownWatcher: {
415 reboot_watcher_current_connections: 0u64,
416 reboot_watcher_total_connections: 0u64
417 }
418 }
419 );
420
421 let (watcher_proxy, s) = fidl::endpoints::create_proxy::<fpower::RebootWatcherMarker>();
422 registrar.add_reboot_watcher(watcher_proxy.clone()).await;
423
424 assert_data_tree!(
425 inspector,
426 root: {
427 ShutdownWatcher: {
428 reboot_watcher_current_connections: 1u64,
429 reboot_watcher_total_connections: 1u64
430 }
431 }
432 );
433
434 drop(s);
435 watcher_proxy.on_closed().await.expect("closed");
436
437 assert_data_tree!(
438 @retry 10,
439 inspector,
440 root: {
441 ShutdownWatcher: {
442 reboot_watcher_current_connections: 0u64,
443 reboot_watcher_total_connections: 1u64
444 }
445 }
446 );
447
448 let (watcher_proxy, s) = fidl::endpoints::create_proxy::<fpower::RebootWatcherMarker>();
449 registrar.add_reboot_watcher(watcher_proxy.clone()).await;
450
451 assert_data_tree!(
452 @retry 10,
453 inspector,
454 root: {
455 ShutdownWatcher: {
456 reboot_watcher_current_connections: 1u64,
457 reboot_watcher_total_connections: 2u64
458 }
459 }
460 );
461 drop(s);
462 watcher_proxy.on_closed().await.expect("closed");
463
464 assert_data_tree!(
465 @retry 10,
466 inspector,
467 root: {
468 ShutdownWatcher: {
469 reboot_watcher_current_connections: 0u64,
470 reboot_watcher_total_connections: 2u64
471 }
472 }
473 );
474
475 let (watcher_proxy, s) = fidl::endpoints::create_proxy::<fpower::ShutdownWatcherMarker>();
476 registrar.add_shutdown_watcher(watcher_proxy.clone()).await;
477
478 assert_data_tree!(
479 @retry 10,
480 inspector,
481 root: {
482 ShutdownWatcher: {
483 reboot_watcher_current_connections: 1u64,
484 reboot_watcher_total_connections: 3u64
485 }
486 }
487 );
488 drop(s);
489 watcher_proxy.on_closed().await.expect("closed");
490
491 assert_data_tree!(
492 @retry 10,
493 inspector,
494 root: {
495 ShutdownWatcher: {
496 reboot_watcher_current_connections: 0u64,
497 reboot_watcher_total_connections: 3u64
498 }
499 }
500 );
501 }
502
503 #[fasync::run_singlethreaded(test)]
506 async fn test_add_reboot_watcher() {
507 let registrar = ShutdownWatcher::new();
508
509 let (register_proxy, register_stream) = fidl::endpoints::create_proxy_and_stream::<
511 fpower::RebootMethodsWatcherRegisterMarker,
512 >();
513
514 let registrar_clone = registrar.clone();
517 fasync::Task::local(async move {
518 registrar_clone.handle_reboot_register_request(register_stream).await;
519 })
520 .detach();
521
522 let (watcher_client, mut watcher_stream) =
524 fidl::endpoints::create_request_stream::<fpower::RebootWatcherMarker>();
525
526 assert_matches!(register_proxy.register_watcher(watcher_client).await, Ok(()));
528 registrar
530 .notify_reboot_watchers(
531 ShutdownOptionsWrapper::new(ShutdownAction::Reboot, ShutdownReason::UserRequest),
532 seconds(0.0),
533 )
534 .await;
535
536 let reasons = assert_matches!(
538 watcher_stream.try_next().await.unwrap().unwrap(),
539 fpower::RebootWatcherRequest::OnReboot {
540 options: fpower::RebootOptions{reasons: Some(reasons), ..},
541 ..
542 } => reasons
543 );
544 assert_eq!(&reasons[..], [fpower::RebootReason2::UserRequest]);
545 }
546
547 #[fasync::run_singlethreaded(test)]
550 async fn test_add_shutdown_watcher() {
551 let registrar = ShutdownWatcher::new();
552
553 let (register_proxy, register_stream) =
555 fidl::endpoints::create_proxy_and_stream::<fpower::ShutdownWatcherRegisterMarker>();
556
557 let registrar_clone = registrar.clone();
560 fasync::Task::local(async move {
561 registrar_clone.handle_shutdown_register_request(register_stream).await;
562 })
563 .detach();
564
565 let (watcher_client, mut watcher_stream) =
567 fidl::endpoints::create_request_stream::<fpower::ShutdownWatcherMarker>();
568
569 assert_matches!(register_proxy.register_watcher(watcher_client).await, Ok(()));
571
572 registrar
574 .notify_shutdown_watchers(
575 ShutdownOptionsWrapper::new(ShutdownAction::Poweroff, ShutdownReason::UserRequest),
576 seconds(0.0),
577 )
578 .await;
579
580 let (action, reasons) = assert_matches!(
582 watcher_stream.try_next().await.unwrap().unwrap(),
583 fpower::ShutdownWatcherRequest::OnShutdown {
584 options: fpower::ShutdownOptions{action: Some(action), reasons: Some(reasons), ..},
585 ..
586 } => (action, reasons)
587 );
588 assert_eq!(action, ShutdownAction::Poweroff);
589 assert_eq!(&reasons[..], [ShutdownReason::UserRequest]);
590 }
591
592 #[fasync::run_singlethreaded(test)]
594 async fn test_reboot_watcher_reason() {
595 let registrar = ShutdownWatcher::new();
596 let (watcher_proxy, mut watcher_stream) =
597 fidl::endpoints::create_proxy_and_stream::<fpower::RebootWatcherMarker>();
598 registrar.add_reboot_watcher(watcher_proxy).await;
599 registrar
600 .notify_reboot_watchers(
601 ShutdownOptionsWrapper::new(
602 ShutdownAction::Reboot,
603 ShutdownReason::HighTemperature,
604 ),
605 seconds(0.0),
606 )
607 .await;
608
609 let reasons = match watcher_stream.try_next().await {
610 Ok(Some(fpower::RebootWatcherRequest::OnReboot {
611 options: fpower::RebootOptions { reasons: Some(reasons), .. },
612 ..
613 })) => reasons,
614 e => panic!("Unexpected watcher_stream result: {:?}", e),
615 };
616
617 assert_eq!(&reasons[..], [fpower::RebootReason2::HighTemperature]);
618 }
619
620 #[fasync::run_singlethreaded(test)]
623 async fn test_shutdown_watcher_reason() {
624 let registrar = ShutdownWatcher::new();
625 let (watcher_proxy, mut watcher_stream) =
626 fidl::endpoints::create_proxy_and_stream::<fpower::ShutdownWatcherMarker>();
627 registrar.add_shutdown_watcher(watcher_proxy).await;
628 registrar
629 .notify_shutdown_watchers(
630 ShutdownOptionsWrapper::new(
631 ShutdownAction::Poweroff,
632 ShutdownReason::HighTemperature,
633 ),
634 seconds(0.0),
635 )
636 .await;
637
638 let (action, reasons) = assert_matches!(
639 watcher_stream.try_next().await.unwrap().unwrap(),
640 fpower::ShutdownWatcherRequest::OnShutdown {
641 options: fpower::ShutdownOptions{action: Some(action), reasons: Some(reasons), ..},
642 ..
643 } => (action, reasons)
644 );
645 assert_eq!(action, ShutdownAction::Poweroff);
646 assert_eq!(&reasons[..], [ShutdownReason::HighTemperature]);
647 }
648
649 #[fasync::run_singlethreaded(test)]
652 async fn test_multiple_reboot_watchers() {
653 let registrar = ShutdownWatcher::new();
654
655 let (watcher_proxy1, mut watcher_stream1) =
657 fidl::endpoints::create_proxy_and_stream::<fpower::RebootWatcherMarker>();
658 registrar.add_reboot_watcher(watcher_proxy1).await;
659
660 let (watcher_proxy2, mut watcher_stream2) =
661 fidl::endpoints::create_proxy_and_stream::<fpower::RebootWatcherMarker>();
662 registrar.add_reboot_watcher(watcher_proxy2).await;
663
664 let (watcher_proxy3, mut watcher_stream3) =
665 fidl::endpoints::create_proxy_and_stream::<fpower::RebootWatcherMarker>();
666 registrar.add_reboot_watcher(watcher_proxy3).await;
667
668 watcher_stream1.control_handle().shutdown();
671
672 registrar
673 .notify_reboot_watchers(
674 ShutdownOptionsWrapper::new(
675 ShutdownAction::Reboot,
676 ShutdownReason::HighTemperature,
677 ),
678 seconds(0.0),
679 )
680 .await;
681
682 match watcher_stream1.try_next().await {
684 Ok(None) => {}
685 e => panic!("Unexpected watcher_stream1 result: {:?}", e),
686 };
687
688 match watcher_stream2.try_next().await {
690 Ok(Some(fpower::RebootWatcherRequest::OnReboot {
691 options: fpower::RebootOptions { reasons: Some(reasons), .. },
692 ..
693 })) => {
694 assert_eq!(&reasons[..], [fpower::RebootReason2::HighTemperature])
695 }
696 e => panic!("Unexpected watcher_stream2 result: {:?}", e),
697 };
698
699 match watcher_stream3.try_next().await {
701 Ok(Some(fpower::RebootWatcherRequest::OnReboot {
702 options: fpower::RebootOptions { reasons: Some(reasons), .. },
703 ..
704 })) => assert_eq!(&reasons[..], [fpower::RebootReason2::HighTemperature]),
705 e => panic!("Unexpected watcher_stream3 result: {:?}", e),
706 };
707 }
708
709 #[fasync::run_singlethreaded(test)]
712 async fn test_multiple_shutdown_watchers() {
713 let registrar = ShutdownWatcher::new();
714
715 let (watcher_proxy1, mut watcher_stream1) =
717 fidl::endpoints::create_proxy_and_stream::<fpower::ShutdownWatcherMarker>();
718 registrar.add_shutdown_watcher(watcher_proxy1).await;
719
720 let (watcher_proxy2, mut watcher_stream2) =
721 fidl::endpoints::create_proxy_and_stream::<fpower::ShutdownWatcherMarker>();
722 registrar.add_shutdown_watcher(watcher_proxy2).await;
723
724 let (watcher_proxy3, mut watcher_stream3) =
725 fidl::endpoints::create_proxy_and_stream::<fpower::ShutdownWatcherMarker>();
726 registrar.add_shutdown_watcher(watcher_proxy3).await;
727
728 watcher_stream1.control_handle().shutdown();
731
732 registrar
733 .notify_shutdown_watchers(
734 ShutdownOptionsWrapper::new(
735 ShutdownAction::Reboot,
736 ShutdownReason::HighTemperature,
737 ),
738 seconds(0.0),
739 )
740 .await;
741
742 match watcher_stream1.try_next().await {
744 Ok(None) => {}
745 e => panic!("Unexpected watcher_stream1 result: {:?}", e),
746 };
747
748 let (action, reasons) = assert_matches!(
750 watcher_stream2.try_next().await.unwrap().unwrap(),
751 fpower::ShutdownWatcherRequest::OnShutdown {
752 options: fpower::ShutdownOptions{action: Some(action), reasons: Some(reasons), ..},
753 ..
754 } => (action, reasons)
755 );
756 assert_eq!(action, ShutdownAction::Reboot);
757 assert_eq!(&reasons[..], [ShutdownReason::HighTemperature]);
758
759 let (action, reasons) = assert_matches!(
761 watcher_stream3.try_next().await.unwrap().unwrap(),
762 fpower::ShutdownWatcherRequest::OnShutdown {
763 options: fpower::ShutdownOptions{action: Some(action), reasons: Some(reasons), ..},
764 ..
765 } => (action, reasons)
766 );
767 assert_eq!(action, ShutdownAction::Reboot);
768 assert_eq!(&reasons[..], [ShutdownReason::HighTemperature]);
769 }
770
771 #[fasync::run_singlethreaded(test)]
774 async fn reboot_watchers_only_notified_of_reboots() {
775 let registrar = ShutdownWatcher::new();
776 let (watcher_proxy, mut watcher_stream) =
777 fidl::endpoints::create_proxy_and_stream::<fpower::RebootWatcherMarker>();
778 registrar.add_reboot_watcher(watcher_proxy).await;
779
780 registrar
781 .handle_system_shutdown_message(ShutdownOptionsWrapper::new(
782 ShutdownAction::Poweroff,
783 ShutdownReason::HighTemperature,
784 ))
785 .await;
786 assert!(futures::poll!(watcher_stream.try_next()).is_pending());
787
788 registrar
789 .handle_system_shutdown_message(ShutdownOptionsWrapper::new(
790 ShutdownAction::RebootToBootloader,
791 ShutdownReason::HighTemperature,
792 ))
793 .await;
794 assert!(futures::poll!(watcher_stream.try_next()).is_pending());
795
796 registrar
797 .handle_system_shutdown_message(ShutdownOptionsWrapper::new(
798 ShutdownAction::RebootToRecovery,
799 ShutdownReason::HighTemperature,
800 ))
801 .await;
802 assert!(futures::poll!(watcher_stream.try_next()).is_pending());
803
804 registrar
805 .handle_system_shutdown_message(ShutdownOptionsWrapper::new(
806 ShutdownAction::Reboot,
807 ShutdownReason::HighTemperature,
808 ))
809 .await;
810 match watcher_stream.try_next().await {
811 Ok(Some(fpower::RebootWatcherRequest::OnReboot {
812 options: fpower::RebootOptions { reasons: Some(reasons), .. },
813 ..
814 })) => {
815 assert_eq!(&reasons[..], [fpower::RebootReason2::HighTemperature])
816 }
817 e => panic!("Unexpected watcher_stream result: {:?}", e),
818 };
819 }
820
821 #[fasync::run_singlethreaded(test)]
824 async fn shutdown_and_reboot_watchers_notified() {
825 let registrar = ShutdownWatcher::new();
826
827 let (shutdown_watcher_proxy, mut shutdown_watcher_stream) =
828 fidl::endpoints::create_proxy_and_stream::<fpower::ShutdownWatcherMarker>();
829 registrar.add_shutdown_watcher(shutdown_watcher_proxy).await;
830
831 let (reboot_watcher_proxy, mut reboot_watcher_stream) =
832 fidl::endpoints::create_proxy_and_stream::<fpower::RebootWatcherMarker>();
833 registrar.add_reboot_watcher(reboot_watcher_proxy).await;
834
835 registrar
836 .handle_system_shutdown_message(ShutdownOptionsWrapper::new(
837 ShutdownAction::Reboot,
838 ShutdownReason::HighTemperature,
839 ))
840 .await;
841
842 let (action, reasons) = assert_matches!(
843 shutdown_watcher_stream.try_next().await.unwrap().unwrap(),
844 fpower::ShutdownWatcherRequest::OnShutdown {
845 options: fpower::ShutdownOptions{action: Some(action), reasons: Some(reasons), ..},
846 ..
847 } => (action, reasons)
848 );
849 assert_eq!(action, ShutdownAction::Reboot);
850 assert_eq!(&reasons[..], [ShutdownReason::HighTemperature]);
851
852 match reboot_watcher_stream.try_next().await {
853 Ok(Some(fpower::RebootWatcherRequest::OnReboot {
854 options: fpower::RebootOptions { reasons: Some(reasons), .. },
855 ..
856 })) => {
857 assert_eq!(&reasons[..], [fpower::RebootReason2::HighTemperature])
858 }
859 e => panic!("Unexpected watcher_stream result: {:?}", e),
860 };
861 }
862
863 #[fasync::run_singlethreaded(test)]
864 async fn shutdown_watchers_notified_for_poweroff() {
865 let registrar = ShutdownWatcher::new();
866
867 let (watcher_proxy, mut watcher_stream) =
868 fidl::endpoints::create_proxy_and_stream::<fpower::ShutdownWatcherMarker>();
869 registrar.add_shutdown_watcher(watcher_proxy).await;
870
871 registrar
872 .handle_system_shutdown_message(ShutdownOptionsWrapper::new(
873 ShutdownAction::Poweroff,
874 ShutdownReason::HighTemperature,
875 ))
876 .await;
877 let (action, reasons) = assert_matches!(
878 watcher_stream.try_next().await.unwrap().unwrap(),
879 fpower::ShutdownWatcherRequest::OnShutdown {
880 options: fpower::ShutdownOptions{action: Some(action), reasons: Some(reasons), ..},
881 ..
882 } => (action, reasons)
883 );
884 assert_eq!(action, ShutdownAction::Poweroff);
885 assert_eq!(&reasons[..], [ShutdownReason::HighTemperature]);
886 }
887
888 #[fasync::run_singlethreaded(test)]
889 async fn shutdown_watchers_notified_for_reboot_to_bootloader() {
890 let registrar = ShutdownWatcher::new();
891
892 let (watcher_proxy, mut watcher_stream) =
893 fidl::endpoints::create_proxy_and_stream::<fpower::ShutdownWatcherMarker>();
894 registrar.add_shutdown_watcher(watcher_proxy).await;
895
896 registrar
897 .handle_system_shutdown_message(ShutdownOptionsWrapper::new(
898 ShutdownAction::RebootToBootloader,
899 ShutdownReason::HighTemperature,
900 ))
901 .await;
902 let (action, reasons) = assert_matches!(
903 watcher_stream.try_next().await.unwrap().unwrap(),
904 fpower::ShutdownWatcherRequest::OnShutdown {
905 options: fpower::ShutdownOptions{action: Some(action), reasons: Some(reasons), ..},
906 ..
907 } => (action, reasons)
908 );
909 assert_eq!(action, ShutdownAction::RebootToBootloader);
910 assert_eq!(&reasons[..], [ShutdownReason::HighTemperature]);
911 }
912
913 #[fasync::run_singlethreaded(test)]
914 async fn shutdown_watchers_notified_for_reboot_to_recovery() {
915 let registrar = ShutdownWatcher::new();
916
917 let (watcher_proxy, mut watcher_stream) =
918 fidl::endpoints::create_proxy_and_stream::<fpower::ShutdownWatcherMarker>();
919 registrar.add_shutdown_watcher(watcher_proxy).await;
920
921 registrar
922 .handle_system_shutdown_message(ShutdownOptionsWrapper::new(
923 ShutdownAction::RebootToRecovery,
924 ShutdownReason::HighTemperature,
925 ))
926 .await;
927 let (action, reasons) = assert_matches!(
928 watcher_stream.try_next().await.unwrap().unwrap(),
929 fpower::ShutdownWatcherRequest::OnShutdown {
930 options: fpower::ShutdownOptions{action: Some(action), reasons: Some(reasons), ..},
931 ..
932 } => (action, reasons)
933 );
934 assert_eq!(action, ShutdownAction::RebootToRecovery);
935 assert_eq!(&reasons[..], [ShutdownReason::HighTemperature]);
936 }
937
938 #[test]
939 fn test_reboot_watcher_response_delay() {
940 let mut exec = fasync::TestExecutor::new();
941 let registrar = ShutdownWatcher::new();
942
943 let (watcher_proxy, mut watcher_stream) =
945 fidl::endpoints::create_proxy_and_stream::<fpower::RebootWatcherMarker>();
946 let fut = async {
947 registrar.add_reboot_watcher(watcher_proxy).await;
948 assert_eq!(registrar.reboot_watchers.lock().await.len(), 1);
949 };
950 exec.run_singlethreaded(fut);
951
952 let notify_future = registrar.notify_reboot_watchers(
954 ShutdownOptionsWrapper::new(ShutdownAction::Reboot, ShutdownReason::HighTemperature),
955 seconds(1.0),
956 );
957 futures::pin_mut!(notify_future);
958
959 assert!(exec.run_until_stalled(&mut notify_future).is_pending());
962
963 let fpower::RebootWatcherRequest::OnReboot { responder, .. } =
965 exec.run_singlethreaded(&mut watcher_stream.try_next()).unwrap().unwrap();
966 assert_matches!(responder.send(), Ok(()));
967
968 assert!(exec.run_until_stalled(&mut notify_future).is_ready());
970 }
971
972 #[test]
973 fn test_shutdown_watcher_response_delay() {
974 let mut exec = fasync::TestExecutor::new();
975 let registrar = ShutdownWatcher::new();
976
977 let (watcher_proxy, mut watcher_stream) =
979 fidl::endpoints::create_proxy_and_stream::<fpower::ShutdownWatcherMarker>();
980 let fut = async {
981 registrar.add_shutdown_watcher(watcher_proxy).await;
982 assert_eq!(registrar.shutdown_watchers.lock().await.len(), 1);
983 };
984 exec.run_singlethreaded(fut);
985
986 let notify_future = registrar.notify_shutdown_watchers(
988 ShutdownOptionsWrapper::new(ShutdownAction::Reboot, ShutdownReason::HighTemperature),
989 seconds(1.0),
990 );
991 futures::pin_mut!(notify_future);
992
993 assert!(exec.run_until_stalled(&mut notify_future).is_pending());
996
997 match exec.run_singlethreaded(&mut watcher_stream.try_next()).unwrap().unwrap() {
999 fpower::ShutdownWatcherRequest::OnShutdown { responder, .. } => {
1000 assert_matches!(responder.send(), Ok(()));
1001 }
1002 fpower::ShutdownWatcherRequest::_UnknownMethod { .. } => unimplemented!(),
1003 }
1004
1005 assert!(exec.run_until_stalled(&mut notify_future).is_ready());
1007 }
1008
1009 #[test]
1013 fn test_reboot_watcher_response_timeout() {
1014 let mut exec = fasync::TestExecutor::new_with_fake_time();
1015 let registrar = ShutdownWatcher::new();
1016 exec.set_fake_time(fasync::MonotonicInstant::from_nanos(0));
1017
1018 let (watcher_proxy, _watcher_stream) =
1020 fidl::endpoints::create_proxy_and_stream::<fpower::RebootWatcherMarker>();
1021 let fut = async {
1022 registrar.add_reboot_watcher(watcher_proxy).await;
1023 assert_eq!(registrar.reboot_watchers.lock().await.len(), 1);
1024 };
1025 futures::pin_mut!(fut);
1026 exec.run_until_stalled(&mut fut).is_ready();
1027
1028 let notify_future = registrar.notify_reboot_watchers(
1030 ShutdownOptionsWrapper::new(ShutdownAction::Reboot, ShutdownReason::HighTemperature),
1031 seconds(1.0),
1032 );
1033 futures::pin_mut!(notify_future);
1034
1035 assert!(exec.run_until_stalled(&mut notify_future).is_pending());
1038
1039 assert_eq!(exec.wake_next_timer(), Some(fasync::MonotonicInstant::from_nanos(1e9 as i64)));
1041
1042 assert!(exec.run_until_stalled(&mut notify_future).is_ready());
1044
1045 let fut = async {
1047 assert_eq!(registrar.reboot_watchers.lock().await.len(), 0);
1048 };
1049 futures::pin_mut!(fut);
1050 exec.run_until_stalled(&mut fut).is_ready();
1051 }
1052
1053 #[test]
1057 fn test_shutdown_watcher_response_timeout() {
1058 let mut exec = fasync::TestExecutor::new_with_fake_time();
1059 let registrar = ShutdownWatcher::new();
1060 exec.set_fake_time(fasync::MonotonicInstant::from_nanos(0));
1061
1062 let (watcher_proxy, _watcher_stream) =
1064 fidl::endpoints::create_proxy_and_stream::<fpower::ShutdownWatcherMarker>();
1065 let fut = async {
1066 registrar.add_shutdown_watcher(watcher_proxy).await;
1067 assert_eq!(registrar.shutdown_watchers.lock().await.len(), 1);
1068 };
1069 futures::pin_mut!(fut);
1070 exec.run_until_stalled(&mut fut).is_ready();
1071
1072 let notify_future = registrar.notify_shutdown_watchers(
1074 ShutdownOptionsWrapper::new(ShutdownAction::Reboot, ShutdownReason::HighTemperature),
1075 seconds(1.0),
1076 );
1077 futures::pin_mut!(notify_future);
1078
1079 assert!(exec.run_until_stalled(&mut notify_future).is_pending());
1082
1083 assert_eq!(exec.wake_next_timer(), Some(fasync::MonotonicInstant::from_nanos(1e9 as i64)));
1085
1086 assert!(exec.run_until_stalled(&mut notify_future).is_ready());
1088
1089 let fut = async {
1091 assert_eq!(registrar.shutdown_watchers.lock().await.len(), 0);
1092 };
1093 futures::pin_mut!(fut);
1094 exec.run_until_stalled(&mut fut).is_ready();
1095 }
1096
1097 #[fasync::run_singlethreaded(test)]
1100 async fn test_watcher_register_fail() {
1101 let registrar = ShutdownWatcher::new();
1102
1103 let (register_proxy, register_stream) = fidl::endpoints::create_proxy_and_stream::<
1105 fpower::RebootMethodsWatcherRegisterMarker,
1106 >();
1107
1108 fasync::Task::local(async move {
1111 registrar.handle_reboot_register_request(register_stream).await;
1112 })
1113 .detach();
1114
1115 assert_matches!(register_proxy.as_channel().write(&[], &mut []), Ok(()));
1117
1118 assert_matches!(register_proxy.on_closed().await, Ok(zx::Signals::CHANNEL_PEER_CLOSED));
1120 }
1121
1122 #[fasync::run_singlethreaded(test)]
1125 async fn test_shutdown_watcher_register_fail() {
1126 let registrar = ShutdownWatcher::new();
1127
1128 let (register_proxy, register_stream) =
1130 fidl::endpoints::create_proxy_and_stream::<fpower::ShutdownWatcherRegisterMarker>();
1131
1132 fasync::Task::local(async move {
1135 registrar.handle_shutdown_register_request(register_stream).await;
1136 })
1137 .detach();
1138
1139 assert_matches!(register_proxy.as_channel().write(&[], &mut []), Ok(()));
1141
1142 assert_matches!(register_proxy.on_closed().await, Ok(zx::Signals::CHANNEL_PEER_CLOSED));
1144 }
1145}