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::UserRequestDeviceStuck => RebootReason2::UserRequest,
73                ShutdownReason::DeveloperRequest => RebootReason2::DeveloperRequest,
74                ShutdownReason::SystemUpdate => RebootReason2::SystemUpdate,
75                ShutdownReason::RetrySystemUpdate => RebootReason2::RetrySystemUpdate,
76                ShutdownReason::HighTemperature => RebootReason2::HighTemperature,
77                ShutdownReason::FactoryDataReset => RebootReason2::FactoryDataReset,
78                ShutdownReason::SessionFailure => RebootReason2::SessionFailure,
79                ShutdownReason::CriticalComponentFailure => RebootReason2::CriticalComponentFailure,
80                ShutdownReason::ZbiSwap => RebootReason2::ZbiSwap,
81                ShutdownReason::OutOfMemory => RebootReason2::OutOfMemory,
82                ShutdownReason::NetstackMigration => RebootReason2::NetstackMigration,
83                ShutdownReason::AndroidUnexpectedReason => RebootReason2::AndroidUnexpectedReason,
84                ShutdownReason::StarnixContainerNoReason => RebootReason2::UserRequest,
85                ShutdownReason::AndroidRescueParty => RebootReason2::AndroidRescueParty,
86                ShutdownReason::AndroidCriticalProcessFailure => {
87                    RebootReason2::AndroidCriticalProcessFailure
88                }
89                ShutdownReason::__SourceBreaking { unknown_ordinal } => {
90                    println!("[shutdown-shim]: error, unrecognized ShutdownReason ordinal: {unknown_ordinal}");
91                    RebootReason2::unknown()
92                }
93            })
94            .collect()
95    }
96}
97
98impl From<ShutdownOptionsWrapper> for fpower::RebootOptions {
99    fn from(options: ShutdownOptionsWrapper) -> Self {
100        fpower::RebootOptions {
101            reasons: Some(options.to_reboot_reason2_deprecated()),
102            __source_breaking: SourceBreaking,
103        }
104    }
105}
106
107impl From<ShutdownOptionsWrapper> for ShutdownOptions {
108    fn from(options: ShutdownOptionsWrapper) -> Self {
109        ShutdownOptions {
110            action: Some(options.action),
111            reasons: Some(options.reasons),
112            __source_breaking: SourceBreaking,
113        }
114    }
115}
116
117/// The reasons a `fpower::RebootOptions` may be invalid.
118#[derive(Debug, PartialEq)]
119pub enum InvalidRebootOptions {
120    /// No reasons were provided.
121    NoReasons,
122}
123
124impl TryFrom<fpower::RebootOptions> for ShutdownOptionsWrapper {
125    type Error = InvalidRebootOptions;
126    fn try_from(options: fpower::RebootOptions) -> Result<Self, Self::Error> {
127        let fpower::RebootOptions { reasons, __source_breaking } = options;
128        if let Some(reasons) = reasons {
129            if !reasons.is_empty() {
130                return Ok(ShutdownOptionsWrapper::from_reboot_reason2_deprecated(&reasons));
131            }
132        }
133
134        Err(InvalidRebootOptions::NoReasons)
135    }
136}
137
138#[cfg(test)]
139mod tests {
140    use super::*;
141    use test_case::test_case;
142
143    #[test_case(None => Err(InvalidRebootOptions::NoReasons); "no_reasons")]
144    #[test_case(Some(vec![]) => Err(InvalidRebootOptions::NoReasons); "empty_reasons")]
145    #[test_case(Some(vec![fpower::RebootReason2::UserRequest]) => Ok(()); "success")]
146    fn reboot_reasons(
147        reasons: Option<Vec<fpower::RebootReason2>>,
148    ) -> Result<(), InvalidRebootOptions> {
149        let options = fpower::RebootOptions { reasons, __source_breaking: SourceBreaking };
150        ShutdownOptionsWrapper::try_from(options).map(|_reasons| {})
151    }
152}