Skip to main content

settings_factory_reset/
factory_reset_controller.rs

1// Copyright 2020 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::factory_reset_fidl_handler::Publisher;
6use crate::types::FactoryResetInfo;
7use anyhow::{Context, Error};
8use fidl_fuchsia_recovery_policy::{DeviceMarker, DeviceProxy};
9use fuchsia_async as fasync;
10use futures::StreamExt;
11use futures::channel::mpsc::UnboundedReceiver;
12use futures::channel::oneshot::Sender;
13use settings_common::call;
14use settings_common::inspect::event::{
15    ExternalEventPublisher, ResponseType, SettingValuePublisher,
16};
17use settings_common::service_context::{ExternalServiceProxy, 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
24impl DeviceStorageCompatible for FactoryResetInfo {
25    type Loader = NoneT;
26    const KEY: &'static str = "factory_reset_info";
27}
28
29impl Default for FactoryResetInfo {
30    fn default() -> Self {
31        FactoryResetInfo::new(true)
32    }
33}
34
35// Clippy doesn't like the same "Failure" suffix for all variants, but we prefer
36// to follow the convention used in the other setting error types.
37#[allow(clippy::enum_variant_names)]
38#[derive(thiserror::Error, Debug)]
39pub(crate) enum FactoryResetError {
40    #[error("Failed to initialize controller: {0:?}")]
41    InitFailure(Error),
42    #[error("External failure for FactoryReset: dependency: {0:?} request:{1:?} error:{2}")]
43    ExternalFailure(Cow<'static, str>, Cow<'static, str>, Cow<'static, str>),
44    #[error("Write failed for FactoryReset: {0:?}")]
45    WriteFailure(Error),
46}
47
48impl From<&FactoryResetError> for ResponseType {
49    fn from(error: &FactoryResetError) -> Self {
50        match error {
51            FactoryResetError::InitFailure(..) => ResponseType::InitFailure,
52            FactoryResetError::ExternalFailure(..) => ResponseType::ExternalFailure,
53            FactoryResetError::WriteFailure(..) => ResponseType::StorageFailure,
54        }
55    }
56}
57
58pub(crate) enum Request {
59    Set(FactoryResetInfo, Sender<Result<(), FactoryResetError>>),
60}
61
62pub struct FactoryResetController {
63    store: Rc<DeviceStorage>,
64    is_local_reset_allowed: bool,
65    factory_reset_policy_service: ExternalServiceProxy<DeviceProxy, ExternalEventPublisher>,
66    publisher: Option<Publisher>,
67    setting_value_publisher: SettingValuePublisher<FactoryResetInfo>,
68}
69
70impl StorageAccess for FactoryResetController {
71    type Storage = DeviceStorage;
72    type Data = FactoryResetInfo;
73    const STORAGE_KEY: &'static str = FactoryResetInfo::KEY;
74}
75
76impl FactoryResetController {
77    pub(crate) async fn new<F>(
78        service_context: &ServiceContext,
79        storage_factory: Rc<F>,
80        setting_value_publisher: SettingValuePublisher<FactoryResetInfo>,
81        external_publisher: ExternalEventPublisher,
82    ) -> Result<FactoryResetController, FactoryResetError>
83    where
84        F: StorageFactory<Storage = DeviceStorage>,
85    {
86        let factory_reset_policy_service = service_context
87            .connect_with_publisher::<DeviceMarker, _>(external_publisher)
88            .await
89            .context("connecting to factory reset service")
90            .map_err(FactoryResetError::InitFailure)?;
91        Ok(Self {
92            store: storage_factory.get_store().await,
93            is_local_reset_allowed: true,
94            factory_reset_policy_service,
95            publisher: None,
96            setting_value_publisher,
97        })
98    }
99
100    pub(crate) fn register_publisher(&mut self, publisher: Publisher) {
101        self.publisher = Some(publisher);
102    }
103
104    fn publish(&self, info: FactoryResetInfo) {
105        let _ = self.setting_value_publisher.publish(&info);
106        if let Some(publisher) = self.publisher.as_ref() {
107            publisher.set(info);
108        }
109    }
110
111    pub(crate) async fn handle(
112        mut self,
113        mut request_rx: UnboundedReceiver<Request>,
114    ) -> fasync::Task<()> {
115        fasync::Task::local(async move {
116            while let Some(request) = request_rx.next().await {
117                let Request::Set(info, tx) = request;
118                let res =
119                    self.set_local_reset_allowed(info.is_local_reset_allowed).await.map(|info| {
120                        if let Some(info) = info {
121                            self.publish(info);
122                        }
123                    });
124                let _ = tx.send(res);
125            }
126        })
127    }
128
129    pub(crate) async fn restore(&mut self) -> Result<FactoryResetInfo, FactoryResetError> {
130        let info = self.store.get::<FactoryResetInfo>().await;
131        self.is_local_reset_allowed = info.is_local_reset_allowed;
132        call!(self.factory_reset_policy_service =>
133            set_is_local_reset_allowed(info.is_local_reset_allowed)
134        )
135        .map_err(|e| {
136            FactoryResetError::ExternalFailure(
137                "factory_reset_policy".into(),
138                "restore_reset_state".into(),
139                format!("{e:?}").into(),
140            )
141        })?;
142
143        Ok(info)
144    }
145
146    async fn set_local_reset_allowed(
147        &mut self,
148        is_local_reset_allowed: bool,
149    ) -> Result<Option<FactoryResetInfo>, FactoryResetError> {
150        let mut info = self.store.get::<FactoryResetInfo>().await;
151        self.is_local_reset_allowed = is_local_reset_allowed;
152        info.is_local_reset_allowed = is_local_reset_allowed;
153        call!(self.factory_reset_policy_service =>
154            set_is_local_reset_allowed(info.is_local_reset_allowed)
155        )
156        .map_err(|e| {
157            FactoryResetError::ExternalFailure(
158                "factory_reset_policy".into(),
159                "set_local_reset_allowed".into(),
160                format!("{e:?}").into(),
161            )
162        })?;
163        self.store
164            .write(&info)
165            .await
166            .map(|state| (UpdateState::Updated == state).then_some(info))
167            .context("writing factory reset")
168            .map_err(FactoryResetError::WriteFailure)
169    }
170}