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 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 the write succeeded, reboot if necessary.
168        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}