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::base::{SettingInfo, SettingType};
6use crate::call;
7use crate::factory_reset::types::FactoryResetInfo;
8use crate::handler::base::Request;
9use crate::handler::setting_handler::controller::Handle;
10use crate::handler::setting_handler::persist::{controller, ClientProxy};
11use crate::handler::setting_handler::{
12    ControllerError, ControllerStateResult, SettingHandlerResult, State,
13};
14use crate::service_context::ExternalServiceProxy;
15use async_trait::async_trait;
16use fidl_fuchsia_recovery_policy::{DeviceMarker, DeviceProxy};
17use futures::lock::Mutex;
18use settings_storage::device_storage::{DeviceStorage, DeviceStorageCompatible};
19use settings_storage::storage_factory::{NoneT, StorageAccess};
20use std::rc::Rc;
21
22impl DeviceStorageCompatible for FactoryResetInfo {
23    type Loader = NoneT;
24    const KEY: &'static str = "factory_reset_info";
25}
26
27impl Default for FactoryResetInfo {
28    fn default() -> Self {
29        FactoryResetInfo::new(true)
30    }
31}
32
33impl From<FactoryResetInfo> for SettingInfo {
34    fn from(info: FactoryResetInfo) -> SettingInfo {
35        SettingInfo::FactoryReset(info)
36    }
37}
38
39type FactoryResetHandle = Rc<Mutex<FactoryResetManager>>;
40
41/// Handles the mapping between [`Request`]s/[`State`] changes and the
42/// [`FactoryResetManager`] logic. Wraps an Rc Mutex of the manager so that each field
43/// doesn't need to be individually locked within the manager.
44///
45/// [`Request`]: crate::handler::base::Request
46/// [`State`]: crate::handler::setting_handler::State
47pub struct FactoryResetController {
48    handle: FactoryResetHandle,
49}
50
51impl StorageAccess for FactoryResetController {
52    type Storage = DeviceStorage;
53    type Data = FactoryResetInfo;
54    const STORAGE_KEY: &'static str = FactoryResetInfo::KEY;
55}
56
57/// Keeps track of the current state of factory reset, is responsible for persisting that state to
58/// disk and notifying the fuchsia.recovery.policy.Device fidl interface of any changes.
59pub struct FactoryResetManager {
60    client: ClientProxy,
61    is_local_reset_allowed: bool,
62    factory_reset_policy_service: ExternalServiceProxy<DeviceProxy>,
63}
64
65impl FactoryResetManager {
66    async fn from_client(client: ClientProxy) -> Result<FactoryResetHandle, ControllerError> {
67        client
68            .get_service_context()
69            .connect::<DeviceMarker>()
70            .await
71            .map(|factory_reset_policy_service| {
72                Rc::new(Mutex::new(Self {
73                    client,
74                    is_local_reset_allowed: true,
75                    factory_reset_policy_service,
76                }))
77            })
78            .map_err(|_| {
79                ControllerError::InitFailure("could not connect to factory reset service".into())
80            })
81    }
82
83    async fn restore(&mut self) -> SettingHandlerResult {
84        self.restore_reset_state(true).await.map(|_| None)
85    }
86
87    async fn restore_reset_state(&mut self, send_event: bool) -> ControllerStateResult {
88        let info = self.client.read_setting::<FactoryResetInfo>(fuchsia_trace::Id::new()).await;
89        self.is_local_reset_allowed = info.is_local_reset_allowed;
90        if send_event {
91            call!(self.factory_reset_policy_service =>
92                set_is_local_reset_allowed(info.is_local_reset_allowed)
93            )
94            .map_err(|e| {
95                ControllerError::ExternalFailure(
96                    SettingType::FactoryReset,
97                    "factory_reset_policy".into(),
98                    "restore_reset_state".into(),
99                    format!("{e:?}").into(),
100                )
101            })?;
102        }
103
104        Ok(())
105    }
106
107    #[allow(clippy::result_large_err)] // TODO(https://fxbug.dev/42069089)
108    fn get(&self) -> SettingHandlerResult {
109        Ok(Some(FactoryResetInfo::new(self.is_local_reset_allowed).into()))
110    }
111
112    async fn set_local_reset_allowed(
113        &mut self,
114        is_local_reset_allowed: bool,
115    ) -> SettingHandlerResult {
116        let id = fuchsia_trace::Id::new();
117        let mut info = self.client.read_setting::<FactoryResetInfo>(id).await;
118        self.is_local_reset_allowed = is_local_reset_allowed;
119        info.is_local_reset_allowed = is_local_reset_allowed;
120        call!(self.factory_reset_policy_service =>
121            set_is_local_reset_allowed(info.is_local_reset_allowed)
122        )
123        .map_err(|e| {
124            ControllerError::ExternalFailure(
125                SettingType::FactoryReset,
126                "factory_reset_policy".into(),
127                "set_local_reset_allowed".into(),
128                format!("{e:?}").into(),
129            )
130        })?;
131        self.client.write_setting(info.into(), id).await.map(|_| None)
132    }
133}
134
135#[async_trait(?Send)]
136impl controller::Create for FactoryResetController {
137    async fn create(client: ClientProxy) -> Result<Self, ControllerError> {
138        Ok(Self { handle: FactoryResetManager::from_client(client).await? })
139    }
140}
141
142#[async_trait(?Send)]
143impl Handle for FactoryResetController {
144    async fn handle(&self, request: Request) -> Option<SettingHandlerResult> {
145        match request {
146            Request::Restore => Some(self.handle.lock().await.restore().await),
147            Request::Get => Some(self.handle.lock().await.get()),
148            Request::SetLocalResetAllowed(is_local_reset_allowed) => {
149                Some(self.handle.lock().await.set_local_reset_allowed(is_local_reset_allowed).await)
150            }
151            _ => None,
152        }
153    }
154
155    async fn change_state(&mut self, state: State) -> Option<ControllerStateResult> {
156        match state {
157            State::Startup => {
158                // Restore the factory reset state locally but do not push to
159                // the factory reset policy.
160                Some(self.handle.lock().await.restore_reset_state(false).await)
161            }
162            _ => None,
163        }
164    }
165}