settings/agent/inspect/
usage_counts.rs1use crate::agent::{Context, Payload};
12use crate::base::SettingType;
13use crate::handler::base::{Payload as HandlerPayload, Request};
14use crate::message::base::{MessageEvent, MessengerType};
15use crate::service::TryFromWithClient;
16use crate::{service, trace};
17use fuchsia_async as fasync;
18use fuchsia_inspect::{self as inspect, component};
19use fuchsia_inspect_derive::Inspect;
20use futures::StreamExt;
21use inspect::NumericProperty;
22use settings_inspect_utils::managed_inspect_map::ManagedInspectMap;
23use std::collections::HashMap;
24use std::rc::Rc;
25
26struct SettingTypeUsageInspectInfo {
28 requests_by_type: ManagedInspectMap<UsageInfo>,
30}
31
32impl SettingTypeUsageInspectInfo {
33 fn new(parent: &inspect::Node, setting_type_str: &str) -> Self {
34 Self {
35 requests_by_type: ManagedInspectMap::<UsageInfo>::with_node(
36 parent.create_child(setting_type_str),
37 ),
38 }
39 }
40}
41
42#[derive(Default, Inspect)]
43struct UsageInfo {
44 inspect_node: inspect::Node,
46
47 count: inspect::IntProperty,
49}
50
51pub(crate) struct SettingTypeUsageInspectAgent {
54 inspect_node: inspect::Node,
56
57 api_call_counts: HashMap<String, SettingTypeUsageInspectInfo>,
59}
60
61impl SettingTypeUsageInspectAgent {
62 pub(crate) async fn create(context: Context) {
63 Self::create_with_node(
64 context,
65 component::inspector().root().create_child("api_usage_counts"),
66 )
67 .await;
68 }
69
70 async fn create_with_node(context: Context, node: inspect::Node) {
71 let (_, message_rx) = context
72 .delegate
73 .create(MessengerType::Broker(Rc::new(move |message| {
74 matches!(message.payload(), service::Payload::Setting(HandlerPayload::Request(_)))
76 })))
77 .await
78 .expect("should receive client");
79
80 let mut agent =
81 SettingTypeUsageInspectAgent { inspect_node: node, api_call_counts: HashMap::new() };
82
83 fasync::Task::local({
84 async move {
85 let _ = &context;
86 let id = fuchsia_trace::Id::new();
87 trace!(id, c"usage_counts_inspect_agent");
88 let event = message_rx.fuse();
89 let agent_event = context.receptor.fuse();
90 futures::pin_mut!(agent_event, event);
91
92 loop {
93 futures::select! {
94 message_event = event.select_next_some() => {
95 trace!(
96 id,
97 c"message_event"
98 );
99 agent.process_message_event(message_event);
100 },
101 agent_message = agent_event.select_next_some() => {
102 trace!(
103 id,
104 c"agent_event"
105 );
106 if let MessageEvent::Message(
107 service::Payload::Agent(Payload::Invocation(_invocation)), client)
108 = agent_message {
109 let _ = client.reply(Payload::Complete(Ok(())).into());
112 }
113 },
114 }
115 }
116 }})
117 .detach();
118 }
119
120 fn process_message_event(&mut self, event: service::message::MessageEvent) {
123 if let Ok((HandlerPayload::Request(request), client)) =
124 HandlerPayload::try_from_with_client(event)
125 {
126 if let service::message::Audience::Address(service::Address::Handler(setting_type)) =
127 client.get_audience()
128 {
129 self.record_usage(setting_type, &request);
130 }
131 }
132 }
133
134 fn record_usage(&mut self, setting_type: SettingType, request: &Request) {
136 let inspect_node = &self.inspect_node;
137 let setting_type_str = format!("{setting_type:?}");
138 let setting_type_info = self
139 .api_call_counts
140 .entry(setting_type_str.clone())
141 .or_insert_with(|| SettingTypeUsageInspectInfo::new(inspect_node, &setting_type_str));
142
143 let mut key = request.for_inspect();
144 if key.starts_with("Set") {
145 key = "Set";
147 }
148 let usage = setting_type_info
149 .requests_by_type
150 .get_or_insert_with(key.to_string(), UsageInfo::default);
151 let _ = usage.count.add(1);
152 }
153}
154
155#[cfg(test)]
156mod tests {
157 use super::*;
158 use crate::display::types::SetDisplayInfo;
159 use crate::intl::types::{IntlInfo, LocaleId, TemperatureUnit};
160
161 use diagnostics_assertions::assert_data_tree;
162 use std::collections::HashSet;
163
164 struct RequestProcessor {
168 delegate: service::message::Delegate,
169 }
170
171 impl RequestProcessor {
172 fn new(delegate: service::message::Delegate) -> Self {
173 RequestProcessor { delegate }
174 }
175
176 async fn send_and_receive(&self, setting_type: SettingType, setting_request: Request) {
177 let (messenger, _) =
178 self.delegate.create(MessengerType::Unbound).await.expect("should be created");
179
180 let (_, mut receptor) = self
181 .delegate
182 .create(MessengerType::Addressable(service::Address::Handler(setting_type)))
183 .await
184 .expect("should be created");
185
186 let _ = messenger.message(
187 HandlerPayload::Request(setting_request).into(),
188 service::message::Audience::Address(service::Address::Handler(setting_type)),
189 );
190
191 let _ = receptor.next_payload().await;
192 }
193 }
194
195 async fn create_context() -> Context {
196 Context::new(
197 service::MessageHub::create_hub()
198 .create(MessengerType::Unbound)
199 .await
200 .expect("should be present")
201 .1,
202 service::MessageHub::create_hub(),
203 HashSet::new(),
204 )
205 .await
206 }
207
208 #[fuchsia::test(allow_stalls = false)]
209 async fn test_inspect() {
210 let inspector = inspect::Inspector::default();
211 let inspect_node = inspector.root().create_child("api_usage_counts");
212 let context = create_context().await;
213
214 let request_processor = RequestProcessor::new(context.delegate.clone());
215
216 SettingTypeUsageInspectAgent::create_with_node(context, inspect_node).await;
217
218 let turn_off_auto_brightness = Request::SetDisplayInfo(SetDisplayInfo {
220 auto_brightness: Some(false),
221 ..SetDisplayInfo::default()
222 });
223 request_processor
224 .send_and_receive(SettingType::Display, turn_off_auto_brightness.clone())
225 .await;
226
227 request_processor.send_and_receive(SettingType::Display, turn_off_auto_brightness).await;
228
229 for _ in 0..100 {
230 request_processor
231 .send_and_receive(
232 SettingType::Intl,
233 Request::SetIntlInfo(IntlInfo {
234 locales: Some(vec![LocaleId { id: "en-US".to_string() }]),
235 temperature_unit: Some(TemperatureUnit::Celsius),
236 time_zone_id: Some("UTC".to_string()),
237 hour_cycle: None,
238 }),
239 )
240 .await;
241 }
242
243 assert_data_tree!(inspector, root: {
244 api_usage_counts: {
245 "Display": {
246 "Set": {
247 count: 2i64,
248 },
249 },
250 "Intl": {
251 "Set": {
252 count: 100i64
253 },
254 }
255 },
256 });
257 }
258
259 #[fuchsia::test(allow_stalls = false)]
260 async fn test_inspect_mixed_request_types() {
261 let inspector = inspect::Inspector::default();
262 let inspect_node = inspector.root().create_child("api_usage_counts");
263 let context = create_context().await;
264
265 let request_processor = RequestProcessor::new(context.delegate.clone());
266
267 SettingTypeUsageInspectAgent::create_with_node(context, inspect_node).await;
268
269 request_processor
271 .send_and_receive(
272 SettingType::Display,
273 Request::SetDisplayInfo(SetDisplayInfo {
274 auto_brightness: Some(false),
275 ..SetDisplayInfo::default()
276 }),
277 )
278 .await;
279
280 request_processor.send_and_receive(SettingType::Display, Request::Get).await;
281
282 request_processor
283 .send_and_receive(
284 SettingType::Display,
285 Request::SetDisplayInfo(SetDisplayInfo {
286 auto_brightness: Some(true),
287 ..SetDisplayInfo::default()
288 }),
289 )
290 .await;
291
292 request_processor.send_and_receive(SettingType::Display, Request::Get).await;
293
294 assert_data_tree!(inspector, root: {
295 api_usage_counts: {
296 "Display": {
297 "Set": {
298 count: 2i64,
299 },
300 "Get": {
301 count: 2i64,
302 },
303 },
304 }
305 });
306 }
307}