settings/agent/inspect/
setting_values.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 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
26/// An agent that listens in on messages between the proxy and setting handlers to record the
27/// values of all settings to inspect.
28pub(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/// Information about the setting types available in the settings service.
36///
37/// Inspect nodes are not used, but need to be held as they're deleted from inspect once they go
38/// out of scope.
39#[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/// Information about a setting to be written to inspect.
56///
57/// Inspect nodes are not used, but need to be held as they're deleted from inspect once they go
58/// out of scope.
59#[derive(Default, Inspect)]
60struct SettingValuesInspectInfo {
61    /// Node of this info.
62    inspect_node: inspect::Node,
63
64    /// Debug string representation of the value of this setting.
65    value: StringProperty,
66
67    /// Milliseconds since Unix epoch that this setting's value was changed.
68    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    /// Create an agent to listen in on all messages between Proxy and setting
82    /// handlers. Agent starts immediately without calling invocation, but
83    /// acknowledges the invocation payload to let the Authority know the agent
84    /// starts properly.
85    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                // Only catch messages that were originally sent from the interfaces, and
96                // that contain a request for the specific setting type we're interested in.
97                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        // Add inspect node for the setting types.
112        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                            // Since the agent runs at creation, there is no
154                            // need to handle state here.
155                            let _ = client.reply(Payload::Complete(Ok(())).into());
156                        }
157                    },
158                }
159            }
160        })
161        .detach();
162    }
163
164    /// This function iterates over the available components, requesting the current value with a
165    /// Get Request. The returned values combined with updates returned through watching for changes
166    /// (which should be registered for observing beforehand) provide full coverage of current
167    /// setting values.
168    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    /// Identifies [`service::message::MessageEvent`] that contains a changing event from a setting
186    /// handler to the proxy and records the setting values.
187    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    /// Writes a setting value to inspect.
196    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            // Value already known, just update its fields.
205            setting_values_info.timestamp.set(&timestamp);
206            setting_values_info.value.set(&value);
207        } else {
208            // Setting value not recorded yet, create a new inspect node.
209            let inspect_info =
210                self.setting_values.get_or_insert_with(key_str, SettingValuesInspectInfo::default);
211            inspect_info.timestamp.set(&timestamp);
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    // Verifies that inspect agent intercepts a restore request and writes the setting value to
241    // inspect.
242    #[fuchsia::test(allow_stalls = false)]
243    async fn test_write_inspect_on_service_lifespan() {
244        // Set the clock so that timestamps will always be 0.
245        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        // Inspect agent should not report any setting values.
262        assert_data_tree!(inspector, root: {
263            setting_types: {
264                "value": "[\"Unknown\"]",
265            },
266            setting_values: {
267            }
268        });
269
270        // Message service lifespan to agent.
271        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        // Setting handler reply to get.
286        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        // Inspect agent writes value to inspect.
300        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    // Verifies that inspect agent intercepts setting change events and writes the setting value
314    // to inspect.
315    #[fuchsia::test(allow_stalls = false)]
316    async fn test_write_inspect_on_changed() {
317        // Set the clock so that timestamps will always be 0.
318        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        // Inspect agent should not report any setting values.
331        assert_data_tree!(inspector, root: {
332            setting_types: {
333                "value": "[\"Unknown\"]",
334            },
335            setting_values: {
336            }
337        });
338
339        // Setting handler notifies proxy of setting changed.
340        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        // Await for the event changed. The agent will intercept it in-between so capturing here
352        // will ensure the inspect operation has fully occurred.
353        let _payload = proxy_receptor.next_payload().await;
354
355        // Inspect agent writes value to inspect.
356        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}