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