settings_setup/
setup_controller.rs1use crate::setup_fidl_handler::InfoPublisher;
6use crate::types::{ConfigurationInterfaceFlags, SetupInfo};
7use anyhow::Error;
8use fidl_fuchsia_hardware_power_statecontrol::{ShutdownAction, ShutdownOptions, ShutdownReason};
9use fuchsia_async as fasync;
10use futures::StreamExt;
11use futures::channel::mpsc::UnboundedReceiver;
12use futures::channel::oneshot::Sender;
13use settings_common::call_async;
14use settings_common::inspect::event::{
15 ExternalEventPublisher, ResponseType, SettingValuePublisher,
16};
17use settings_common::service_context::ServiceContext;
18use settings_storage::UpdateState;
19use settings_storage::device_storage::{DeviceStorage, DeviceStorageCompatible};
20use settings_storage::storage_factory::{NoneT, StorageAccess, StorageFactory};
21use std::borrow::Cow;
22use std::rc::Rc;
23
24#[derive(thiserror::Error, Debug)]
25pub(crate) enum SetupError {
26 #[error(
27 "Call to an external dependency {0:?} for setting type Setup failed. \
28 Request:{1:?}: Error:{2}"
29 )]
30 ExternalFailure(&'static str, Cow<'static, str>, Cow<'static, str>),
31 #[error("Write failed for Setup: {0:?}")]
32 WriteFailure(Error),
33}
34
35impl From<&SetupError> for ResponseType {
36 fn from(error: &SetupError) -> Self {
37 match error {
38 SetupError::ExternalFailure(..) => ResponseType::ExternalFailure,
39 SetupError::WriteFailure(..) => ResponseType::StorageFailure,
40 }
41 }
42}
43
44async fn reboot(
45 service_context: &ServiceContext,
46 external_publisher: ExternalEventPublisher,
47) -> Result<(), SetupError> {
48 let hardware_power_statecontrol_admin = service_context
49 .connect_with_publisher::<fidl_fuchsia_hardware_power_statecontrol::AdminMarker, _>(
50 external_publisher,
51 )
52 .await
53 .map_err(|e| {
54 SetupError::ExternalFailure(
55 "hardware_power_statecontrol_manager",
56 "connect".into(),
57 format!("{e:?}").into(),
58 )
59 })?;
60
61 let reboot_err = |e: String| {
62 SetupError::ExternalFailure(
63 "hardware_power_statecontrol_manager",
64 "reboot".into(),
65 e.into(),
66 )
67 };
68
69 call_async!(hardware_power_statecontrol_admin => shutdown(&ShutdownOptions{
70 action: Some(ShutdownAction::Reboot),
71 reasons: Some(vec![ShutdownReason::UserRequest]), ..Default::default()
72 }))
73 .await
74 .map_err(|e| reboot_err(format!("{e:?}")))
75 .and_then(|r| {
76 r.map_err(|zx_status| reboot_err(format!("{:?}", zx::Status::from_raw(zx_status))))
77 })
78}
79
80impl DeviceStorageCompatible for SetupInfo {
81 type Loader = NoneT;
82 const KEY: &'static str = "setup_info";
83}
84
85pub(crate) enum Request {
86 Set(ConfigurationInterfaceFlags, bool, Sender<Result<(), SetupError>>),
87}
88
89pub struct SetupController {
90 service_context: Rc<ServiceContext>,
91 store: Rc<DeviceStorage>,
92 publisher: Option<InfoPublisher>,
93 setting_value_publisher: SettingValuePublisher<SetupInfo>,
94 external_publisher: ExternalEventPublisher,
95}
96
97impl StorageAccess for SetupController {
98 type Storage = DeviceStorage;
99 type Data = SetupInfo;
100 const STORAGE_KEY: &'static str = SetupInfo::KEY;
101}
102
103impl SetupController {
104 pub(super) async fn new<F>(
105 service_context: Rc<ServiceContext>,
106 storage_factory: Rc<F>,
107 setting_value_publisher: SettingValuePublisher<SetupInfo>,
108 external_publisher: ExternalEventPublisher,
109 ) -> Self
110 where
111 F: StorageFactory<Storage = DeviceStorage>,
112 {
113 SetupController {
114 service_context,
115 store: storage_factory.get_store().await,
116 publisher: None,
117 setting_value_publisher,
118 external_publisher,
119 }
120 }
121
122 pub(super) fn register_publisher(&mut self, publisher: InfoPublisher) {
123 self.publisher = Some(publisher);
124 }
125
126 fn publish(&self, info: SetupInfo) {
127 let _ = self.setting_value_publisher.publish(&info);
128 if let Some(publisher) = self.publisher.as_ref() {
129 publisher.set(info);
130 }
131 }
132
133 pub(super) async fn handle(
134 self,
135 mut request_rx: UnboundedReceiver<Request>,
136 ) -> fasync::Task<()> {
137 fasync::Task::local(async move {
138 while let Some(request) = request_rx.next().await {
139 let Request::Set(config_interfaces_flags, should_reboot, tx) = request;
140 let res = self.set(config_interfaces_flags, should_reboot).await.map(|info| {
141 if let Some(info) = info {
142 self.publish(info);
143 }
144 });
145 let _ = tx.send(res);
146 }
147 })
148 }
149
150 async fn set(
151 &self,
152 config_interfaces_flags: ConfigurationInterfaceFlags,
153 should_reboot: bool,
154 ) -> Result<Option<SetupInfo>, SetupError> {
155 let mut info = self.store.get::<SetupInfo>().await;
156 info.configuration_interfaces = config_interfaces_flags;
157
158 let write_setting_result = self.store.write(&info).await;
159
160 if write_setting_result.is_ok() && should_reboot {
162 reboot(&self.service_context, self.external_publisher.clone()).await?;
163 }
164 write_setting_result
165 .map(|state| (UpdateState::Updated == state).then_some(info))
166 .map_err(SetupError::WriteFailure)
167 }
168
169 pub(super) async fn restore(&self) -> SetupInfo {
170 self.store.get::<SetupInfo>().await
171 }
172}