Skip to main content

settings_setup/
setup_controller.rs

1// Copyright 2019 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 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 the write succeeded, reboot if necessary.
161        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}