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
329#[allow(dead_code)]
331pub(crate) trait IntoHandlerResult {
332 #[allow(clippy::result_large_err)] fn into_handler_result(self) -> SettingHandlerResult;
335}
336
337#[allow(dead_code)]
338impl IntoHandlerResult for SettingInfo {
339 fn into_handler_result(self) -> SettingHandlerResult {
340 Ok(Some(self))
341 }
342}
343
344pub mod persist {
345 use super::{ClientImpl as BaseProxy, *};
346 use crate::trace;
347 use fuchsia_trace as ftrace;
348 use settings_storage::UpdateState;
349 use settings_storage::device_storage::DeviceStorageConvertible;
350
351 pub trait Storage: DeviceStorageConvertible + Into<SettingInfo> {}
352 impl<T: DeviceStorageConvertible + Into<SettingInfo>> Storage for T {}
353
354 pub(crate) mod controller {
355 use super::*;
356
357 #[async_trait(?Send)]
358 pub(crate) trait CreateWithAsync: Sized {
359 type Data;
360
361 async fn create_with(
363 handler: ClientProxy,
364 data: Self::Data,
365 ) -> Result<Self, ControllerError>;
366 }
367 }
368
369 pub struct ClientProxy {
370 base: Rc<BaseProxy>,
371 setting_type: SettingType,
372 }
373
374 impl Clone for ClientProxy {
375 fn clone(&self) -> Self {
376 Self { base: Rc::clone(&self.base), setting_type: self.setting_type }
377 }
378 }
379
380 impl ClientProxy {
381 pub(crate) async fn new(base_proxy: Rc<BaseProxy>, setting_type: SettingType) -> Self {
382 Self { base: base_proxy, setting_type }
383 }
384
385 pub(crate) fn get_service_context(&self) -> Rc<ServiceContext> {
386 self.base.get_service_context()
387 }
388
389 pub(crate) async fn notify(&self, event: Event) {
390 self.base.notify(event).await;
391 }
392
393 pub(crate) async fn storage_write<T>(
394 &self,
395 store: &DeviceStorage,
396 info: T,
397 id: ftrace::Id,
398 ) -> Result<UpdateState, ControllerError>
399 where
400 T: Into<SettingInfo> + HasSettingType + DeviceStorageConvertible,
401 {
402 let setting_type = T::SETTING_TYPE;
403 let fst = format!("{setting_type:?}");
404 let guard = trace_guard!(
405 id,
406 c"write_setting send",
407 "setting_type" => fst.as_str()
408 );
409 let result = store.write::<T>(&info).await;
410 drop(guard);
411
412 trace!(
413 id,
414 c"write_setting receive",
415 "setting_type" => fst.as_str()
416 );
417 if let Ok(UpdateState::Updated) = result {
418 trace!(
419 id,
420 c"write_setting notify",
421 "setting_type" => fst.as_str()
422 );
423 self.notify(Event::Changed(info.into())).await;
424 }
425
426 result.map_err(|e| {
427 log::error!("Failed to write setting: {:?}", e);
428 ControllerError::WriteFailure(setting_type)
429 })
430 }
431 }
432
433 impl IntoHandlerResult for Result<UpdateState, ControllerError> {
434 fn into_handler_result(self) -> SettingHandlerResult {
435 self.map(|_| None)
436 }
437 }
438
439 pub(crate) struct Handler<C> {
440 _data: PhantomData<C>,
441 }
442
443 impl<'a, C, O> Handler<C>
444 where
445 C: controller::CreateWithAsync<Data = O> + super::controller::Handle + 'static,
446 O: Clone + 'static,
447 {
448 pub(crate) fn spawn_with_async(
449 context: Context,
450 data: O,
451 ) -> LocalBoxFuture<'static, ControllerGenerateResult> {
452 Box::pin(async move {
453 let setting_type = context.setting_type;
454
455 ClientImpl::create(
456 context,
457 Box::new({
458 let data = data.clone();
459 move |proxy| {
460 let data = data.clone();
461 Box::pin(async move {
462 let proxy = ClientProxy::new(proxy, setting_type).await;
463 let controller_result = C::create_with(proxy, data).await;
464
465 match controller_result {
466 Err(err) => Err(err),
467 Ok(controller) => Ok(Box::new(controller) as BoxedController),
468 }
469 })
470 }
471 }),
472 )
473 .await
474 })
475 }
476 }
477}
478
479pub(crate) fn reply(client: MessageClient, result: SettingHandlerResult) {
480 let _ = client.reply(Payload::Result(result).into());
481}