1use std::collections::HashSet;
6use std::rc::Rc;
7
8use fuchsia_async as fasync;
9use fuchsia_inspect::{self as inspect, component, Property, StringProperty};
10use fuchsia_inspect_derive::{Inspect, WithInspect};
11use futures::StreamExt;
12use settings_inspect_utils::managed_inspect_map::ManagedInspectMap;
13
14use crate::agent::{Context, Lifespan, Payload};
15use crate::base::{SettingInfo, SettingType};
16use crate::handler::base::{Payload as SettingPayload, Request};
17use crate::handler::setting_handler::{Event, Payload as HandlerPayload};
18use crate::message::base::{Audience, MessageEvent, MessengerType};
19use crate::service::message::Messenger;
20use crate::service::TryFromWithClient;
21use crate::{clock, service};
22
23const INSPECT_NODE_NAME: &str = "setting_values";
24const SETTING_TYPE_INSPECT_NODE_NAME: &str = "setting_types";
25
26pub(crate) struct SettingValuesInspectAgent {
29 messenger_client: Messenger,
30 setting_values: ManagedInspectMap<SettingValuesInspectInfo>,
31 setting_types: HashSet<SettingType>,
32 _setting_types_inspect_info: SettingTypesInspectInfo,
33}
34
35#[derive(Debug, Default, Inspect)]
40struct SettingTypesInspectInfo {
41 inspect_node: inspect::Node,
42 value: inspect::StringProperty,
43}
44
45impl SettingTypesInspectInfo {
46 fn new(value: String, node: &inspect::Node, key: &str) -> Self {
47 let info = Self::default()
48 .with_inspect(node, key)
49 .expect("Failed to create SettingTypesInspectInfo node");
50 info.value.set(&value);
51 info
52 }
53}
54
55#[derive(Default, Inspect)]
60struct SettingValuesInspectInfo {
61 inspect_node: inspect::Node,
63
64 value: StringProperty,
66
67 timestamp: StringProperty,
69}
70
71impl SettingValuesInspectAgent {
72 pub(crate) async fn create(context: Context) {
73 Self::create_with_node(
74 context,
75 component::inspector().root().create_child(INSPECT_NODE_NAME),
76 None,
77 )
78 .await;
79 }
80
81 async fn create_with_node(
86 context: Context,
87 inspect_node: inspect::Node,
88 custom_inspector: Option<&inspect::Inspector>,
89 ) {
90 let inspector = custom_inspector.unwrap_or_else(|| component::inspector());
91
92 let (messenger_client, receptor) = match context
93 .delegate
94 .create(MessengerType::Broker(Rc::new(move |message| {
95 matches!(
98 message.payload(),
99 service::Payload::Controller(HandlerPayload::Event(Event::Changed(_)))
100 )
101 })))
102 .await
103 {
104 Ok(messenger) => messenger,
105 Err(err) => {
106 log::error!("could not create inspect: {:?}", err);
107 return;
108 }
109 };
110
111 let mut setting_types_list: Vec<String> = context
113 .available_components
114 .clone()
115 .iter()
116 .map(|component| format!("{component:?}"))
117 .collect();
118 setting_types_list.sort();
119 let setting_types_value = format!("{setting_types_list:?}");
120 let setting_types_inspect_info = SettingTypesInspectInfo::new(
121 setting_types_value,
122 inspector.root(),
123 SETTING_TYPE_INSPECT_NODE_NAME,
124 );
125
126 let mut agent = Self {
127 messenger_client,
128 setting_values: ManagedInspectMap::<SettingValuesInspectInfo>::with_node(inspect_node),
129 setting_types: context.available_components.clone(),
130 _setting_types_inspect_info: setting_types_inspect_info,
131 };
132
133 fasync::Task::local(async move {
134 let _ = &context;
135 let event = receptor.fuse();
136 let agent_event = context.receptor.fuse();
137 futures::pin_mut!(agent_event, event);
138
139 loop {
140 futures::select! {
141 message_event = event.select_next_some() => {
142 agent.process_message_event(message_event).await;
143 },
144 agent_message = agent_event.select_next_some() => {
145 if let MessageEvent::Message(
146 service::Payload::Agent(Payload::Invocation(invocation)), client)
147 = agent_message {
148
149 if invocation.lifespan == Lifespan::Service {
150 agent.fetch_initial_values().await;
151 }
152
153 let _ = client.reply(Payload::Complete(Ok(())).into());
156 }
157 },
158 }
159 }
160 })
161 .detach();
162 }
163
164 async fn fetch_initial_values(&mut self) {
169 for setting_type in self.setting_types.clone() {
170 let mut receptor = self.messenger_client.message(
171 SettingPayload::Request(Request::Get).into(),
172 Audience::Address(service::Address::Handler(setting_type)),
173 );
174
175 if let Ok((SettingPayload::Response(Ok(Some(setting_info))), _)) =
176 receptor.next_of::<SettingPayload>().await
177 {
178 self.write_setting_to_inspect(setting_info).await;
179 } else {
180 log::error!("Could not fetch initial value for setting type:{:?}", setting_type);
181 }
182 }
183 }
184
185 async fn process_message_event(&mut self, event: service::message::MessageEvent) {
188 if let Ok((HandlerPayload::Event(Event::Changed(setting_info)), _)) =
189 HandlerPayload::try_from_with_client(event)
190 {
191 self.write_setting_to_inspect(setting_info).await;
192 }
193 }
194
195 async fn write_setting_to_inspect(&mut self, setting: SettingInfo) {
197 let (key, value) = setting.for_inspect();
198 let timestamp = clock::inspect_format_now();
199
200 let key_str = key.to_string();
201 let setting_values = self.setting_values.get_mut(&key_str);
202
203 if let Some(setting_values_info) = setting_values {
204 setting_values_info.timestamp.set(×tamp);
206 setting_values_info.value.set(&value);
207 } else {
208 let inspect_info =
210 self.setting_values.get_or_insert_with(key_str, SettingValuesInspectInfo::default);
211 inspect_info.timestamp.set(×tamp);
212 inspect_info.value.set(&value);
213 }
214 }
215}
216
217#[cfg(test)]
218mod tests {
219 use diagnostics_assertions::assert_data_tree;
220 use zx::MonotonicInstant;
221
222 use crate::agent::Invocation;
223 use crate::base::UnknownInfo;
224 use crate::message::base::Status;
225 use crate::service::MessageHub;
226 use crate::service_context::ServiceContext;
227
228 use super::*;
229
230 async fn create_context() -> Context {
231 let hub = MessageHub::create_hub();
232 Context::new(
233 hub.create(MessengerType::Unbound).await.expect("should be present").1,
234 hub,
235 vec![SettingType::Unknown].into_iter().collect(),
236 )
237 .await
238 }
239
240 #[fuchsia::test(allow_stalls = false)]
243 async fn test_write_inspect_on_service_lifespan() {
244 clock::mock::set(MonotonicInstant::from_nanos(0));
246
247 let inspector = inspect::Inspector::default();
248 let inspect_node = inspector.root().create_child(INSPECT_NODE_NAME);
249 let context = create_context().await;
250 let delegate = context.delegate.clone();
251 let agent_signature = context.receptor.get_signature();
252
253 let (_, mut setting_proxy_receptor) = context
254 .delegate
255 .create(MessengerType::Addressable(service::Address::Handler(SettingType::Unknown)))
256 .await
257 .expect("should create proxy");
258
259 SettingValuesInspectAgent::create_with_node(context, inspect_node, Some(&inspector)).await;
260
261 assert_data_tree!(inspector, root: {
263 setting_types: {
264 "value": "[\"Unknown\"]",
265 },
266 setting_values: {
267 }
268 });
269
270 let _ = delegate
272 .create(MessengerType::Unbound)
273 .await
274 .expect("should create messenger")
275 .0
276 .message(
277 Payload::Invocation(Invocation {
278 lifespan: Lifespan::Service,
279 service_context: Rc::new(ServiceContext::new(None, None)),
280 })
281 .into(),
282 Audience::Messenger(agent_signature),
283 );
284
285 let (_, reply_client) =
287 setting_proxy_receptor.next_payload().await.expect("payload should be present");
288
289 let mut reply_receptor =
290 reply_client.reply(SettingPayload::Response(Ok(Some(UnknownInfo(true).into()))).into());
291
292 while let Some(message_event) = reply_receptor.next().await {
293 if matches!(message_event, service::message::MessageEvent::Status(Status::Acknowledged))
294 {
295 break;
296 }
297 }
298
299 assert_data_tree!(inspector, root: {
301 setting_types: {
302 "value": "[\"Unknown\"]",
303 },
304 setting_values: {
305 "Unknown": {
306 value: "UnknownInfo(true)",
307 timestamp: "0.000000000",
308 }
309 }
310 });
311 }
312
313 #[fuchsia::test(allow_stalls = false)]
316 async fn test_write_inspect_on_changed() {
317 clock::mock::set(MonotonicInstant::from_nanos(0));
319
320 let inspector = inspect::Inspector::default();
321 let inspect_node = inspector.root().create_child(INSPECT_NODE_NAME);
322 let context = create_context().await;
323 let delegate = context.delegate.clone();
324
325 let mut proxy_receptor =
326 delegate.create(MessengerType::Unbound).await.expect("should create proxy messenger").1;
327
328 SettingValuesInspectAgent::create_with_node(context, inspect_node, Some(&inspector)).await;
329
330 assert_data_tree!(inspector, root: {
332 setting_types: {
333 "value": "[\"Unknown\"]",
334 },
335 setting_values: {
336 }
337 });
338
339 let _ = delegate
341 .create(MessengerType::Unbound)
342 .await
343 .expect("setting handler should be created")
344 .0
345 .message(
346 HandlerPayload::Event(Event::Changed(SettingInfo::Unknown(UnknownInfo(false))))
347 .into(),
348 Audience::Messenger(proxy_receptor.get_signature()),
349 );
350
351 let _payload = proxy_receptor.next_payload().await;
354
355 assert_data_tree!(inspector, root: {
357 setting_types: {
358 "value": "[\"Unknown\"]",
359 },
360 setting_values: {
361 "Unknown": {
362 value: "UnknownInfo(false)",
363 timestamp: "0.000000000",
364 }
365 }
366 });
367 }
368}