1use fidl::marker::SourceBreaking;
6use fidl_fuchsia_hardware_power_statecontrol::{
7 self as fpower, RebootReason2, ShutdownAction, ShutdownOptions, ShutdownReason,
8};
9
10#[derive(Debug, PartialEq, PartialOrd, Clone)]
16pub struct ShutdownOptionsWrapper {
17 pub action: ShutdownAction,
18 pub reasons: Vec<ShutdownReason>,
19}
20
21impl ShutdownOptionsWrapper {
22 pub fn new(action: fpower::ShutdownAction, reason: ShutdownReason) -> Self {
24 Self { action, reasons: vec![reason] }
25 }
26
27 pub(crate) fn from_reboot_reason_deprecated(reason: &fpower::RebootReason) -> Self {
32 let reason = match reason {
33 fpower::RebootReason::UserRequest => fpower::ShutdownReason::UserRequest,
34 fpower::RebootReason::SystemUpdate => fpower::ShutdownReason::SystemUpdate,
35 fpower::RebootReason::RetrySystemUpdate => fpower::ShutdownReason::RetrySystemUpdate,
36 fpower::RebootReason::HighTemperature => fpower::ShutdownReason::HighTemperature,
37 fpower::RebootReason::FactoryDataReset => fpower::ShutdownReason::FactoryDataReset,
38 fpower::RebootReason::SessionFailure => fpower::ShutdownReason::SessionFailure,
39 fpower::RebootReason::SysmgrFailure => {
40 println!(
42 "[shutdown-shim]: error, unexpectedly received RebootReason::SysmgrFailure"
43 );
44 fpower::ShutdownReason::unknown()
45 }
46 fpower::RebootReason::CriticalComponentFailure => {
47 fpower::ShutdownReason::CriticalComponentFailure
48 }
49 fpower::RebootReason::ZbiSwap => fpower::ShutdownReason::ZbiSwap,
50 fpower::RebootReason::OutOfMemory => fpower::ShutdownReason::OutOfMemory,
51 };
52 Self::new(fpower::ShutdownAction::Reboot, reason)
53 }
54
55 pub(crate) fn from_reboot_reason2_deprecated(reasons: &Vec<RebootReason2>) -> Self {
57 let reasons = reasons
58 .iter()
59 .map(|reason| match reason {
60 RebootReason2::UserRequest => ShutdownReason::UserRequest,
61 RebootReason2::DeveloperRequest => ShutdownReason::DeveloperRequest,
62 RebootReason2::SystemUpdate => ShutdownReason::SystemUpdate,
63 RebootReason2::RetrySystemUpdate => ShutdownReason::RetrySystemUpdate,
64 RebootReason2::HighTemperature => ShutdownReason::HighTemperature,
65 RebootReason2::FactoryDataReset => ShutdownReason::FactoryDataReset,
66 RebootReason2::SessionFailure => ShutdownReason::SessionFailure,
67 RebootReason2::SysmgrFailure => {
68 println!(
70 "[shutdown-shim]: error, unexpectedly received RebootReason2::SysmgrFailure"
71 );
72 fpower::ShutdownReason::unknown()
73 }
74 RebootReason2::CriticalComponentFailure => ShutdownReason::CriticalComponentFailure,
75 RebootReason2::ZbiSwap => ShutdownReason::ZbiSwap,
76 RebootReason2::OutOfMemory => ShutdownReason::OutOfMemory,
77 RebootReason2::NetstackMigration => ShutdownReason::NetstackMigration,
78 RebootReason2::AndroidUnexpectedReason => ShutdownReason::AndroidUnexpectedReason,
79 RebootReason2::AndroidRescueParty => ShutdownReason::AndroidRescueParty,
80 RebootReason2::AndroidCriticalProcessFailure => {
81 ShutdownReason::AndroidCriticalProcessFailure
82 }
83 RebootReason2::__SourceBreaking { unknown_ordinal } => {
84 println!("[shutdown-shim]: error, unrecognized RebootReason2 ordinal: {unknown_ordinal}");
85 ShutdownReason::unknown()
86 }
87 })
88 .collect();
89 Self { action: ShutdownAction::Reboot, reasons }
90 }
91
92 pub(crate) fn to_reboot_reason_deprecated(&self) -> fpower::RebootReason {
101 enum FoldState {
102 Direct(fpower::RebootReason),
103 Indirect(fpower::RebootReason),
104 None,
105 }
106 let state = self.reasons.iter().fold(FoldState::None, |state, reason| {
107 match (&state, &reason) {
108 (FoldState::Direct(_), _) => state,
110 (_, fpower::ShutdownReason::UserRequest) => {
112 FoldState::Direct(fpower::RebootReason::UserRequest)
113 }
114 (_, fpower::ShutdownReason::SystemUpdate) => {
115 FoldState::Direct(fpower::RebootReason::SystemUpdate)
116 }
117 (_, fpower::ShutdownReason::RetrySystemUpdate) => {
118 FoldState::Direct(fpower::RebootReason::RetrySystemUpdate)
119 }
120 (_, fpower::ShutdownReason::HighTemperature) => {
121 FoldState::Direct(fpower::RebootReason::HighTemperature)
122 }
123 (_, fpower::ShutdownReason::FactoryDataReset) => {
124 FoldState::Direct(fpower::RebootReason::FactoryDataReset)
125 }
126 (_, fpower::ShutdownReason::SessionFailure) => {
127 FoldState::Direct(fpower::RebootReason::SessionFailure)
128 }
129 (_, fpower::ShutdownReason::CriticalComponentFailure) => {
130 FoldState::Direct(fpower::RebootReason::CriticalComponentFailure)
131 }
132 (_, fpower::ShutdownReason::ZbiSwap) => {
133 FoldState::Direct(fpower::RebootReason::ZbiSwap)
134 }
135 (_, fpower::ShutdownReason::OutOfMemory) => {
136 FoldState::Direct(fpower::RebootReason::OutOfMemory)
137 }
138 (_, fpower::ShutdownReason::AndroidUnexpectedReason) => {
139 FoldState::Direct(fpower::RebootReason::UserRequest)
140 }
141 (_, fpower::ShutdownReason::AndroidRescueParty) => {
142 FoldState::Direct(fpower::RebootReason::UserRequest)
143 }
144 (_, fpower::ShutdownReason::AndroidCriticalProcessFailure) => {
145 FoldState::Direct(fpower::RebootReason::UserRequest)
146 }
147 (_, fpower::ShutdownReason::DeveloperRequest) => {
148 FoldState::Direct(fpower::RebootReason::UserRequest)
149 }
150 (FoldState::Indirect(_), fpower::ShutdownReason::NetstackMigration) => state,
153 (FoldState::None, fpower::ShutdownReason::NetstackMigration) => {
155 FoldState::Indirect(fpower::RebootReason::SystemUpdate)
156 }
157 (_, fpower::ShutdownReason::__SourceBreaking { unknown_ordinal: _ }) => {
158 unreachable!()
159 }
160 }
161 });
162 match state {
163 FoldState::Direct(reason) | FoldState::Indirect(reason) => reason,
164 FoldState::None => {
165 unreachable!("Called to_reboot_reason with no reason(s) specified")
166 }
167 }
168 }
169
170 pub(crate) fn to_reboot_reason2_deprecated(&self) -> Vec<RebootReason2> {
174 self.reasons
175 .iter()
176 .map(|item| match item {
177 ShutdownReason::UserRequest => RebootReason2::UserRequest,
178 ShutdownReason::DeveloperRequest => RebootReason2::DeveloperRequest,
179 ShutdownReason::SystemUpdate => RebootReason2::SystemUpdate,
180 ShutdownReason::RetrySystemUpdate => RebootReason2::RetrySystemUpdate,
181 ShutdownReason::HighTemperature => RebootReason2::HighTemperature,
182 ShutdownReason::FactoryDataReset => RebootReason2::FactoryDataReset,
183 ShutdownReason::SessionFailure => RebootReason2::SessionFailure,
184 ShutdownReason::CriticalComponentFailure => RebootReason2::CriticalComponentFailure,
185 ShutdownReason::ZbiSwap => RebootReason2::ZbiSwap,
186 ShutdownReason::OutOfMemory => RebootReason2::OutOfMemory,
187 ShutdownReason::NetstackMigration => RebootReason2::NetstackMigration,
188 ShutdownReason::AndroidUnexpectedReason => RebootReason2::AndroidUnexpectedReason,
189 ShutdownReason::AndroidRescueParty => RebootReason2::AndroidRescueParty,
190 ShutdownReason::AndroidCriticalProcessFailure => {
191 RebootReason2::AndroidCriticalProcessFailure
192 }
193 ShutdownReason::__SourceBreaking { unknown_ordinal } => {
194 println!("[shutdown-shim]: error, unrecognized ShutdownReason ordinal: {unknown_ordinal}");
195 RebootReason2::unknown()
196 }
197 })
198 .collect()
199 }
200}
201
202impl From<ShutdownOptionsWrapper> for fpower::RebootOptions {
203 fn from(options: ShutdownOptionsWrapper) -> Self {
204 fpower::RebootOptions {
205 reasons: Some(options.to_reboot_reason2_deprecated()),
206 __source_breaking: SourceBreaking,
207 }
208 }
209}
210
211impl From<ShutdownOptionsWrapper> for ShutdownOptions {
212 fn from(options: ShutdownOptionsWrapper) -> Self {
213 ShutdownOptions {
214 action: Some(options.action),
215 reasons: Some(options.reasons),
216 __source_breaking: SourceBreaking,
217 }
218 }
219}
220
221#[derive(Debug, PartialEq)]
223pub enum InvalidRebootOptions {
224 NoReasons,
226}
227
228impl TryFrom<fpower::RebootOptions> for ShutdownOptionsWrapper {
229 type Error = InvalidRebootOptions;
230 fn try_from(options: fpower::RebootOptions) -> Result<Self, Self::Error> {
231 let fpower::RebootOptions { reasons, __source_breaking } = options;
232 if let Some(reasons) = reasons {
233 if !reasons.is_empty() {
234 return Ok(ShutdownOptionsWrapper::from_reboot_reason2_deprecated(&reasons));
235 }
236 }
237
238 Err(InvalidRebootOptions::NoReasons)
239 }
240}
241
242#[cfg(test)]
243mod tests {
244 use super::*;
245 use test_case::test_case;
246
247 #[test_case(None => Err(InvalidRebootOptions::NoReasons); "no_reasons")]
248 #[test_case(Some(vec![]) => Err(InvalidRebootOptions::NoReasons); "empty_reasons")]
249 #[test_case(Some(vec![fpower::RebootReason2::UserRequest]) => Ok(()); "success")]
250 fn reboot_reasons(
251 reasons: Option<Vec<fpower::RebootReason2>>,
252 ) -> Result<(), InvalidRebootOptions> {
253 let options = fpower::RebootOptions { reasons, __source_breaking: SourceBreaking };
254 ShutdownOptionsWrapper::try_from(options).map(|_reasons| {})
255 }
256
257 #[test_case(
258 vec![fpower::RebootReason2::UserRequest, fpower::RebootReason2::SystemUpdate] =>
259 fpower::RebootReason::UserRequest;
260 "prefer_first_a")]
261 #[test_case(
262 vec![fpower::RebootReason2::SystemUpdate, fpower::RebootReason2::UserRequest] =>
263 fpower::RebootReason::SystemUpdate;
264 "prefer_first_b")]
265 #[test_case(
266 vec![fpower::RebootReason2::NetstackMigration, fpower::RebootReason2::UserRequest] =>
267 fpower::RebootReason::UserRequest;
268 "prefer_direct")]
269 #[test_case(
270 vec![fpower::RebootReason2::NetstackMigration] =>
271 fpower::RebootReason::SystemUpdate;
272 "netstack_migration")]
273 fn reasons_to_deprecated(reasons: Vec<fpower::RebootReason2>) -> fpower::RebootReason {
274 let options =
275 fpower::RebootOptions { reasons: Some(reasons), __source_breaking: SourceBreaking };
276 ShutdownOptionsWrapper::try_from(options).unwrap().to_reboot_reason_deprecated()
277 }
278}