1use crate::base::{HasSettingType, SettingInfo, SettingType};
5use crate::handler::base::{Context, ControllerGenerateResult, Request};
6use crate::message::base::Audience;
7use crate::service::message::{MessageClient, Messenger, Signature};
8use crate::service_context::ServiceContext;
9use crate::{payload_convert, trace, trace_guard};
10use async_trait::async_trait;
11use core::convert::TryFrom;
12use fuchsia_async as fasync;
13use futures::future::LocalBoxFuture;
14use futures::lock::Mutex;
15use settings_storage::device_storage::DeviceStorage;
16use settings_storage::storage_factory::StorageFactory as StorageFactoryTrait;
17use std::borrow::Cow;
18use std::marker::PhantomData;
19use std::rc::Rc;
20use thiserror::Error;
21
22pub type ExitResult = Result<(), ControllerError>;
23pub type SettingHandlerResult = Result<Option<SettingInfo>, ControllerError>;
24pub type ControllerStateResult = Result<(), ControllerError>;
26
27#[derive(Clone, Debug, PartialEq)]
29pub enum Payload {
30 Command(Command),
32 Event(Event),
34 Result(SettingHandlerResult),
36}
37
38payload_convert!(Controller, Payload);
39
40#[derive(Debug, Clone, PartialEq)]
42pub enum Command {
43 HandleRequest(Request),
44 ChangeState(State),
45}
46
47impl TryFrom<crate::handler::setting_handler::Payload> for Command {
48 type Error = &'static str;
49
50 fn try_from(value: crate::handler::setting_handler::Payload) -> Result<Self, Self::Error> {
51 match value {
52 crate::handler::setting_handler::Payload::Command(command) => Ok(command),
53 _ => Err("wrong payload type"),
54 }
55 }
56}
57
58#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
59pub enum State {
60 Startup,
63
64 Listen,
67
68 EndListen,
71
72 Teardown,
76}
77
78#[derive(Clone, Debug, PartialEq)]
84pub enum Event {
85 Changed(SettingInfo),
88 Exited(ExitResult),
89 StateChanged(State),
90}
91
92#[allow(dead_code)]
93pub(crate) trait StorageFactory: StorageFactoryTrait {}
94impl<T: StorageFactoryTrait> StorageFactory for T {}
95
96#[derive(Error, Debug, Clone, PartialEq)]
97pub enum ControllerError {
98 #[error("Unimplemented Request:{1:?} for setting type: {0:?}")]
99 UnimplementedRequest(SettingType, Request),
100 #[error("Write failed. setting type: {0:?}")]
101 WriteFailure(SettingType),
102 #[error("Initialization failure: cause {0:?}")]
103 InitFailure(Cow<'static, str>),
104 #[error("Restoration of setting on controller startup failed: cause {0:?}")]
105 RestoreFailure(Cow<'static, str>),
106 #[error(
107 "Call to an external dependency {1:?} for setting type {0:?} failed. \
108 Request:{2:?}: Error:{3}"
109 )]
110 ExternalFailure(SettingType, Cow<'static, str>, Cow<'static, str>, Cow<'static, str>),
111 #[error("Invalid input argument for setting type: {0:?} argument:{1:?} value:{2:?}")]
112 InvalidArgument(SettingType, Cow<'static, str>, Cow<'static, str>),
113 #[error(
114 "Incompatible argument values passed: {setting_type:?} argument:{main_arg:?} cannot be \
115 combined with arguments:[{other_args:?}] with respective values:[{values:?}]. {reason:?}"
116 )]
117 IncompatibleArguments {
118 setting_type: SettingType,
119 main_arg: Cow<'static, str>,
120 other_args: Cow<'static, str>,
121 values: Cow<'static, str>,
122 reason: Cow<'static, str>,
123 },
124 #[error("Unhandled type: {0:?}")]
125 UnhandledType(SettingType),
126 #[error("Unexpected error: {0:?}")]
127 UnexpectedError(Cow<'static, str>),
128 #[error("Undeliverable Request:{1:?} for setting type: {0:?}")]
129 UndeliverableError(SettingType, Request),
130 #[error("Unsupported request for setting type: {0:?}")]
131 UnsupportedError(SettingType),
132 #[error("Delivery error for type: {0:?} received by: {1:?}")]
133 DeliveryError(SettingType, SettingType),
134 #[error("Irrecoverable error")]
135 IrrecoverableError,
136 #[error("Timeout occurred")]
137 TimeoutError,
138 #[error("Exit occurred")]
139 ExitError,
140}
141
142pub(crate) type BoxedController = Box<dyn controller::Handle>;
143pub(crate) type BoxedControllerResult = Result<BoxedController, ControllerError>;
144
145pub(crate) type GenerateController =
146 Box<dyn Fn(Rc<ClientImpl>) -> LocalBoxFuture<'static, BoxedControllerResult>>;
147
148pub(crate) mod controller {
149 use super::*;
150
151 #[async_trait(?Send)]
152 #[cfg(test)]
153 pub(crate) trait Create: Sized {
154 async fn create(client: Rc<ClientImpl>) -> Result<Self, ControllerError>;
155 }
156
157 #[async_trait(?Send)]
158 pub(crate) trait Handle {
159 async fn handle(&self, request: Request) -> Option<SettingHandlerResult>;
162
163 async fn change_state(&mut self, _state: State) -> Option<ControllerStateResult> {
165 None
166 }
167 }
168}
169
170pub struct ClientImpl {
171 notify: Mutex<bool>,
173 messenger: Messenger,
174 notifier_signature: Signature,
175 service_context: Rc<ServiceContext>,
176 setting_type: SettingType,
177}
178
179impl ClientImpl {
180 fn new(context: &Context) -> Self {
181 Self {
182 messenger: context.messenger.clone(),
183 setting_type: context.setting_type,
184 notifier_signature: context.notifier_signature,
185 notify: Mutex::new(false),
186 service_context: Rc::clone(&context.environment.service_context),
187 }
188 }
189
190 #[cfg(test)]
192 pub fn for_test(
193 notify: Mutex<bool>,
194 messenger: Messenger,
195 notifier_signature: Signature,
196 service_context: Rc<ServiceContext>,
197 setting_type: SettingType,
198 ) -> Self {
199 Self { notify, messenger, notifier_signature, service_context, setting_type }
200 }
201
202 async fn process_request(
203 setting_type: SettingType,
204 controller: &BoxedController,
205 request: Request,
206 ) -> SettingHandlerResult {
207 let result = controller.handle(request.clone()).await;
208 match result {
209 Some(response_result) => response_result,
210 None => Err(ControllerError::UnimplementedRequest(setting_type, request)),
211 }
212 }
213
214 pub(crate) async fn create(
215 mut context: Context,
216 generate_controller: GenerateController,
217 ) -> ControllerGenerateResult {
218 let client = Rc::new(Self::new(&context));
219
220 let mut controller = generate_controller(Rc::clone(&client)).await?;
221
222 fasync::Task::local(async move {
224 let _ = &context;
225 let id = fuchsia_trace::Id::new();
226 trace!(
227 id,
228 c"setting handler",
229 "setting_type" => format!("{:?}", client.setting_type).as_str()
230 );
231 while let Ok((payload, message_client)) = context.receptor.next_of::<Payload>().await {
232 let setting_type = client.setting_type;
233
234 match Command::try_from(payload).expect("should only receive commands") {
236 Command::HandleRequest(Request::Rebroadcast) => {
240 trace!(id, c"handle rebroadcast");
241 let controller_reply =
243 Self::process_request(setting_type, &controller, Request::Get).await;
244
245 if let Ok(Some(info)) = &controller_reply {
247 client.notify(Event::Changed(info.clone())).await;
248 }
249
250 reply(message_client, controller_reply);
251 }
252 Command::HandleRequest(request) => {
253 trace!(id, c"handle request");
254 reply(
255 message_client,
256 Self::process_request(setting_type, &controller, request.clone()).await,
257 );
258 }
259 Command::ChangeState(state) => {
260 trace!(
261 id,
262 c"change state",
263 "state" => format!("{state:?}").as_str()
264 );
265 match state {
266 State::Startup => {
267 if let Some(Err(e)) = controller.change_state(state).await {
268 log::error!(
269 "Failed startup phase for SettingType {:?} {}",
270 setting_type,
271 e
272 );
273 }
274 reply(message_client, Ok(None));
275 continue;
276 }
277 State::Listen => {
278 *client.notify.lock().await = true;
279 }
280 State::EndListen => {
281 *client.notify.lock().await = false;
282 }
283 State::Teardown => {
284 if let Some(Err(e)) = controller.change_state(state).await {
285 log::error!(
286 "Failed teardown phase for SettingType {:?} {}",
287 setting_type,
288 e
289 );
290 }
291 reply(message_client, Ok(None));
292 continue;
293 }
294 }
295
296 let _ = controller.change_state(state).await;
298 }
299 }
300 }
301 })
302 .detach();
303
304 Ok(())
305 }
306
307 pub(crate) fn get_service_context(&self) -> Rc<ServiceContext> {
308 Rc::clone(&self.service_context)
309 }
310
311 pub(crate) async fn notify(&self, event: Event) {
312 let notify = self.notify.lock().await;
313 if *notify {
314 let _ = self.messenger.message(
316 Payload::Event(event).into(),
317 Audience::Messenger(self.notifier_signature),
318 );
319 }
320 }
321
322 #[cfg(test)]
323 pub(crate) fn emit_state_event(&self, state: State) {
324 let event = Payload::Event(Event::StateChanged(state));
325 let _ = self.messenger.message(event.into(), Audience::EventSink);
326 }
327}
328
329pub(crate) trait IntoHandlerResult {
331 #[allow(clippy::result_large_err)] fn into_handler_result(self) -> SettingHandlerResult;
334}
335
336impl IntoHandlerResult for SettingInfo {
337 fn into_handler_result(self) -> SettingHandlerResult {
338 Ok(Some(self))
339 }
340}
341
342pub mod persist {
343 use super::{ClientImpl as BaseProxy, *};
344 use crate::trace;
345 use fuchsia_trace as ftrace;
346 use settings_storage::device_storage::DeviceStorageConvertible;
347 use settings_storage::UpdateState;
348
349 pub trait Storage: DeviceStorageConvertible + Into<SettingInfo> {}
350 impl<T: DeviceStorageConvertible + Into<SettingInfo>> Storage for T {}
351
352 pub(crate) mod controller {
353 use super::*;
354
355 #[async_trait(?Send)]
356 pub(crate) trait CreateWithAsync: Sized {
357 type Data;
358
359 async fn create_with(
361 handler: ClientProxy,
362 data: Self::Data,
363 ) -> Result<Self, ControllerError>;
364 }
365 }
366
367 pub struct ClientProxy {
368 base: Rc<BaseProxy>,
369 setting_type: SettingType,
370 }
371
372 impl Clone for ClientProxy {
373 fn clone(&self) -> Self {
374 Self { base: Rc::clone(&self.base), setting_type: self.setting_type }
375 }
376 }
377
378 impl ClientProxy {
379 pub(crate) async fn new(base_proxy: Rc<BaseProxy>, setting_type: SettingType) -> Self {
380 Self { base: base_proxy, setting_type }
381 }
382
383 pub(crate) fn get_service_context(&self) -> Rc<ServiceContext> {
384 self.base.get_service_context()
385 }
386
387 pub(crate) async fn notify(&self, event: Event) {
388 self.base.notify(event).await;
389 }
390
391 pub(crate) async fn storage_write<T>(
392 &self,
393 store: &DeviceStorage,
394 info: T,
395 id: ftrace::Id,
396 ) -> Result<UpdateState, ControllerError>
397 where
398 T: Into<SettingInfo> + HasSettingType + DeviceStorageConvertible,
399 {
400 let setting_type = T::SETTING_TYPE;
401 let fst = format!("{setting_type:?}");
402 let guard = trace_guard!(
403 id,
404 c"write_setting send",
405 "setting_type" => fst.as_str()
406 );
407 let result = store.write::<T>(&info).await;
408 drop(guard);
409
410 trace!(
411 id,
412 c"write_setting receive",
413 "setting_type" => fst.as_str()
414 );
415 if let Ok(UpdateState::Updated) = result {
416 trace!(
417 id,
418 c"write_setting notify",
419 "setting_type" => fst.as_str()
420 );
421 self.notify(Event::Changed(info.into())).await;
422 }
423
424 result.map_err(|e| {
425 log::error!("Failed to write setting: {:?}", e);
426 ControllerError::WriteFailure(setting_type)
427 })
428 }
429 }
430
431 impl IntoHandlerResult for Result<UpdateState, ControllerError> {
432 fn into_handler_result(self) -> SettingHandlerResult {
433 self.map(|_| None)
434 }
435 }
436
437 pub(crate) struct Handler<C> {
438 _data: PhantomData<C>,
439 }
440
441 impl<'a, C, O> Handler<C>
442 where
443 C: controller::CreateWithAsync<Data = O> + super::controller::Handle + 'static,
444 O: Clone + 'static,
445 {
446 pub(crate) fn spawn_with_async(
447 context: Context,
448 data: O,
449 ) -> LocalBoxFuture<'static, ControllerGenerateResult> {
450 Box::pin(async move {
451 let setting_type = context.setting_type;
452
453 ClientImpl::create(
454 context,
455 Box::new({
456 let data = data.clone();
457 move |proxy| {
458 let data = data.clone();
459 Box::pin(async move {
460 let proxy = ClientProxy::new(proxy, setting_type).await;
461 let controller_result = C::create_with(proxy, data).await;
462
463 match controller_result {
464 Err(err) => Err(err),
465 Ok(controller) => Ok(Box::new(controller) as BoxedController),
466 }
467 })
468 }
469 }),
470 )
471 .await
472 })
473 }
474 }
475}
476
477pub(crate) fn reply(client: MessageClient, result: SettingHandlerResult) {
478 let _ = client.reply(Payload::Result(result).into());
479}