Skip to main content

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