shutdown_shim/
reboot_reasons.rs

1// Copyright 2020 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use fidl::marker::SourceBreaking;
6use fidl_fuchsia_hardware_power_statecontrol::{
7    self as fpower, RebootReason2, ShutdownAction, ShutdownOptions, ShutdownReason,
8};
9
10/// The action and reasons of a shutdown.
11///
12/// This type provides translation functions for supporting deprecated enums.
13// TODO(https://fxbug.dev/414413282): This type may not be necessary once `RebootReason2` is removed
14// from the API.
15#[derive(Debug, PartialEq, PartialOrd, Clone)]
16pub struct ShutdownOptionsWrapper {
17    pub action: ShutdownAction,
18    pub reasons: Vec<ShutdownReason>,
19}
20
21impl ShutdownOptionsWrapper {
22    /// Construct a new `ShutdownOptionsWrapper` with the given reason.
23    pub fn new(action: fpower::ShutdownAction, reason: ShutdownReason) -> Self {
24        Self { action, reasons: vec![reason] }
25    }
26
27    /// Construct a new `ShutdownOptionsWrapper` from the given Vec of deprecated `RebootReason2`.
28    pub(crate) fn from_reboot_reason2_deprecated(reasons: &Vec<RebootReason2>) -> Self {
29        let reasons = reasons
30            .iter()
31            .map(|reason| match reason {
32                RebootReason2::UserRequest => ShutdownReason::UserRequest,
33                RebootReason2::DeveloperRequest => ShutdownReason::DeveloperRequest,
34                RebootReason2::SystemUpdate => ShutdownReason::SystemUpdate,
35                RebootReason2::RetrySystemUpdate => ShutdownReason::RetrySystemUpdate,
36                RebootReason2::HighTemperature => ShutdownReason::HighTemperature,
37                RebootReason2::FactoryDataReset => ShutdownReason::FactoryDataReset,
38                RebootReason2::SessionFailure => ShutdownReason::SessionFailure,
39                RebootReason2::SysmgrFailure => {
40                    // sysmgr doesn't exist anymore.
41                    println!(
42                        "[shutdown-shim]: error, unexpectedly received RebootReason2::SysmgrFailure"
43                    );
44                    fpower::ShutdownReason::unknown()
45                }
46                RebootReason2::CriticalComponentFailure => ShutdownReason::CriticalComponentFailure,
47                RebootReason2::ZbiSwap => ShutdownReason::ZbiSwap,
48                RebootReason2::OutOfMemory => ShutdownReason::OutOfMemory,
49                RebootReason2::NetstackMigration => ShutdownReason::NetstackMigration,
50                RebootReason2::AndroidUnexpectedReason => ShutdownReason::AndroidUnexpectedReason,
51                RebootReason2::AndroidRescueParty => ShutdownReason::AndroidRescueParty,
52                RebootReason2::AndroidCriticalProcessFailure => {
53                    ShutdownReason::AndroidCriticalProcessFailure
54                }
55                RebootReason2::__SourceBreaking { unknown_ordinal } => {
56                    println!("[shutdown-shim]: error, unrecognized RebootReason2 ordinal: {unknown_ordinal}");
57                    ShutdownReason::unknown()
58                }
59            })
60            .collect();
61        Self { action: ShutdownAction::Reboot, reasons }
62    }
63
64    /// Convert into a vector of deprecated `RebootReason2`. It's a backwards compatible
65    /// implementation. If the reason has no equivalent deprecated `RebootReason2`, do a best-effort
66    /// translation.
67    pub(crate) fn to_reboot_reason2_deprecated(&self) -> Vec<RebootReason2> {
68        self.reasons
69            .iter()
70            .map(|item| match item {
71                ShutdownReason::UserRequest => RebootReason2::UserRequest,
72                ShutdownReason::DeveloperRequest => RebootReason2::DeveloperRequest,
73                ShutdownReason::SystemUpdate => RebootReason2::SystemUpdate,
74                ShutdownReason::RetrySystemUpdate => RebootReason2::RetrySystemUpdate,
75                ShutdownReason::HighTemperature => RebootReason2::HighTemperature,
76                ShutdownReason::FactoryDataReset => RebootReason2::FactoryDataReset,
77                ShutdownReason::SessionFailure => RebootReason2::SessionFailure,
78                ShutdownReason::CriticalComponentFailure => RebootReason2::CriticalComponentFailure,
79                ShutdownReason::ZbiSwap => RebootReason2::ZbiSwap,
80                ShutdownReason::OutOfMemory => RebootReason2::OutOfMemory,
81                ShutdownReason::NetstackMigration => RebootReason2::NetstackMigration,
82                ShutdownReason::AndroidUnexpectedReason => RebootReason2::AndroidUnexpectedReason,
83                ShutdownReason::StarnixContainerNoReason => RebootReason2::UserRequest,
84                ShutdownReason::AndroidRescueParty => RebootReason2::AndroidRescueParty,
85                ShutdownReason::AndroidCriticalProcessFailure => {
86                    RebootReason2::AndroidCriticalProcessFailure
87                }
88                ShutdownReason::__SourceBreaking { unknown_ordinal } => {
89                    println!("[shutdown-shim]: error, unrecognized ShutdownReason ordinal: {unknown_ordinal}");
90                    RebootReason2::unknown()
91                }
92            })
93            .collect()
94    }
95}
96
97impl From<ShutdownOptionsWrapper> for fpower::RebootOptions {
98    fn from(options: ShutdownOptionsWrapper) -> Self {
99        fpower::RebootOptions {
100            reasons: Some(options.to_reboot_reason2_deprecated()),
101            __source_breaking: SourceBreaking,
102        }
103    }
104}
105
106impl From<ShutdownOptionsWrapper> for ShutdownOptions {
107    fn from(options: ShutdownOptionsWrapper) -> Self {
108        ShutdownOptions {
109            action: Some(options.action),
110            reasons: Some(options.reasons),
111            __source_breaking: SourceBreaking,
112        }
113    }
114}
115
116/// The reasons a `fpower::RebootOptions` may be invalid.
117#[derive(Debug, PartialEq)]
118pub enum InvalidRebootOptions {
119    /// No reasons were provided.
120    NoReasons,
121}
122
123impl TryFrom<fpower::RebootOptions> for ShutdownOptionsWrapper {
124    type Error = InvalidRebootOptions;
125    fn try_from(options: fpower::RebootOptions) -> Result<Self, Self::Error> {
126        let fpower::RebootOptions { reasons, __source_breaking } = options;
127        if let Some(reasons) = reasons {
128            if !reasons.is_empty() {
129                return Ok(ShutdownOptionsWrapper::from_reboot_reason2_deprecated(&reasons));
130            }
131        }
132
133        Err(InvalidRebootOptions::NoReasons)
134    }
135}
136
137#[cfg(test)]
138mod tests {
139    use super::*;
140    use test_case::test_case;
141
142    #[test_case(None => Err(InvalidRebootOptions::NoReasons); "no_reasons")]
143    #[test_case(Some(vec![]) => Err(InvalidRebootOptions::NoReasons); "empty_reasons")]
144    #[test_case(Some(vec![fpower::RebootReason2::UserRequest]) => Ok(()); "success")]
145    fn reboot_reasons(
146        reasons: Option<Vec<fpower::RebootReason2>>,
147    ) -> Result<(), InvalidRebootOptions> {
148        let options = fpower::RebootOptions { reasons, __source_breaking: SourceBreaking };
149        ShutdownOptionsWrapper::try_from(options).map(|_reasons| {})
150    }
151}