1use crate::agent::{AgentCreator, Context, CreationFunc, 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::channel::mpsc::UnboundedReceiver;
21use futures::StreamExt;
22use inspect::NumericProperty;
23use settings_common::inspect::event::{Direction, UsageEvent};
24use settings_inspect_utils::managed_inspect_map::ManagedInspectMap;
25use std::cell::RefCell;
26use std::collections::HashMap;
27use std::rc::Rc;
28
29pub(crate) fn create_registrar(rx: UnboundedReceiver<UsageEvent>) -> AgentCreator {
30 let rx = Rc::new(RefCell::new(rx));
31 AgentCreator {
32 debug_id: "UsageCountInspectAgent",
33 create: CreationFunc::Dynamic(Rc::new(move |context| {
34 let rx = Rc::clone(&rx);
35 Box::pin(async move {
36 SettingTypeUsageInspectAgent::create(context, rx).await;
37 })
38 })),
39 }
40}
41
42struct SettingTypeUsageInspectInfo {
44 requests_by_type: ManagedInspectMap<UsageInfo>,
46}
47
48impl SettingTypeUsageInspectInfo {
49 fn new(parent: &inspect::Node, setting_type_str: &str) -> Self {
50 Self {
51 requests_by_type: ManagedInspectMap::<UsageInfo>::with_node(
52 parent.create_child(setting_type_str),
53 ),
54 }
55 }
56}
57
58#[derive(Default, Inspect)]
59struct UsageInfo {
60 inspect_node: inspect::Node,
62
63 count: inspect::IntProperty,
65}
66
67pub(crate) struct SettingTypeUsageInspectAgent {
70 inspect_node: inspect::Node,
72
73 api_call_counts: HashMap<String, SettingTypeUsageInspectInfo>,
75}
76
77impl SettingTypeUsageInspectAgent {
78 pub(crate) async fn create(context: Context, rx: Rc<RefCell<UnboundedReceiver<UsageEvent>>>) {
79 Self::create_with_node(
80 context,
81 rx,
82 component::inspector().root().create_child("api_usage_counts"),
83 )
84 .await;
85 }
86
87 async fn create_with_node(
88 context: Context,
89 rx: Rc<RefCell<UnboundedReceiver<UsageEvent>>>,
90 node: inspect::Node,
91 ) {
92 let (_, message_rx) = context
93 .delegate
94 .create(MessengerType::Broker(Rc::new(move |message| {
95 matches!(message.payload(), service::Payload::Setting(HandlerPayload::Request(_)))
97 })))
98 .await
99 .expect("should receive client");
100
101 let mut agent =
102 SettingTypeUsageInspectAgent { inspect_node: node, api_call_counts: HashMap::new() };
103
104 fasync::Task::local({
105 async move {
106 let _ = &context;
107 let id = fuchsia_trace::Id::new();
108 trace!(id, c"usage_counts_inspect_agent");
109 let event = message_rx.fuse();
110 let agent_event = context.receptor.fuse();
111 let mut usage_event = rx.borrow_mut();
112 futures::pin_mut!(agent_event, event);
113
114 loop {
115 futures::select! {
116 message_event = event.select_next_some() => {
117 trace!(
118 id,
119 c"message_event"
120 );
121 agent.process_message_event(message_event);
122 },
123 usage_event = usage_event.select_next_some() => {
124 agent.process_usage_event(usage_event);
125 },
126 agent_message = agent_event.select_next_some() => {
127 trace!(
128 id,
129 c"agent_event"
130 );
131 if let MessageEvent::Message(
132 service::Payload::Agent(Payload::Invocation(_invocation)), client)
133 = agent_message {
134 let _ = client.reply(Payload::Complete(Ok(())).into());
137 }
138 },
139 }
140 }
141 }})
142 .detach();
143 }
144
145 fn process_usage_event(&mut self, event: UsageEvent) {
146 if let Direction::Response(..) = event.direction {
148 return;
149 }
150
151 let inspect_node = &self.inspect_node;
152 let setting_type_info = self
153 .api_call_counts
154 .entry(event.setting.to_string())
155 .or_insert_with(|| SettingTypeUsageInspectInfo::new(inspect_node, event.setting));
156
157 let key = event.request_type;
158 let usage = setting_type_info
159 .requests_by_type
160 .get_or_insert_with(format!("{key:?}"), UsageInfo::default);
161 let _ = usage.count.add(1);
162 }
163
164 fn process_message_event(&mut self, event: service::message::MessageEvent) {
167 if let Ok((HandlerPayload::Request(request), client)) =
168 HandlerPayload::try_from_with_client(event)
169 {
170 if let service::message::Audience::Address(service::Address::Handler(setting_type)) =
171 client.get_audience()
172 {
173 self.record_usage(setting_type, &request);
174 }
175 }
176 }
177
178 fn record_usage(&mut self, setting_type: SettingType, request: &Request) {
180 let inspect_node = &self.inspect_node;
181 let setting_type_str = format!("{setting_type:?}");
182 let setting_type_info = self
183 .api_call_counts
184 .entry(setting_type_str.clone())
185 .or_insert_with(|| SettingTypeUsageInspectInfo::new(inspect_node, &setting_type_str));
186
187 let mut key = request.for_inspect();
188 if key.starts_with("Set") {
189 key = "Set";
191 }
192 let usage = setting_type_info
193 .requests_by_type
194 .get_or_insert_with(key.to_string(), UsageInfo::default);
195 let _ = usage.count.add(1);
196 }
197}
198
199#[cfg(test)]
200mod tests {
201 use super::*;
202 use crate::display::types::SetDisplayInfo;
203 use crate::intl::types::{IntlInfo, LocaleId, TemperatureUnit};
204
205 use diagnostics_assertions::assert_data_tree;
206 use futures::channel::mpsc;
207 use std::collections::HashSet;
208
209 struct RequestProcessor {
213 delegate: service::message::Delegate,
214 }
215
216 impl RequestProcessor {
217 fn new(delegate: service::message::Delegate) -> Self {
218 RequestProcessor { delegate }
219 }
220
221 async fn send_and_receive(&self, setting_type: SettingType, setting_request: Request) {
222 let (messenger, _) =
223 self.delegate.create(MessengerType::Unbound).await.expect("should be created");
224
225 let (_, mut receptor) = self
226 .delegate
227 .create(MessengerType::Addressable(service::Address::Handler(setting_type)))
228 .await
229 .expect("should be created");
230
231 let _ = messenger.message(
232 HandlerPayload::Request(setting_request).into(),
233 service::message::Audience::Address(service::Address::Handler(setting_type)),
234 );
235
236 let _ = receptor.next_payload().await;
237 }
238 }
239
240 async fn create_context() -> Context {
241 Context::new(
242 service::MessageHub::create_hub()
243 .create(MessengerType::Unbound)
244 .await
245 .expect("should be present")
246 .1,
247 service::MessageHub::create_hub(),
248 HashSet::new(),
249 )
250 .await
251 }
252
253 #[fuchsia::test(allow_stalls = false)]
254 async fn test_inspect() {
255 let inspector = inspect::Inspector::default();
256 let inspect_node = inspector.root().create_child("api_usage_counts");
257 let context = create_context().await;
258
259 let request_processor = RequestProcessor::new(context.delegate.clone());
260
261 let (_tx, rx) = mpsc::unbounded();
262 SettingTypeUsageInspectAgent::create_with_node(
263 context,
264 Rc::new(RefCell::new(rx)),
265 inspect_node,
266 )
267 .await;
268
269 let turn_off_auto_brightness = Request::SetDisplayInfo(SetDisplayInfo {
271 auto_brightness: Some(false),
272 ..SetDisplayInfo::default()
273 });
274 request_processor
275 .send_and_receive(SettingType::Display, turn_off_auto_brightness.clone())
276 .await;
277
278 request_processor.send_and_receive(SettingType::Display, turn_off_auto_brightness).await;
279
280 for _ in 0..100 {
281 request_processor
282 .send_and_receive(
283 SettingType::Intl,
284 Request::SetIntlInfo(IntlInfo {
285 locales: Some(vec![LocaleId { id: "en-US".to_string() }]),
286 temperature_unit: Some(TemperatureUnit::Celsius),
287 time_zone_id: Some("UTC".to_string()),
288 hour_cycle: None,
289 }),
290 )
291 .await;
292 }
293
294 assert_data_tree!(inspector, root: {
295 api_usage_counts: {
296 "Display": {
297 "Set": {
298 count: 2i64,
299 },
300 },
301 "Intl": {
302 "Set": {
303 count: 100i64
304 },
305 }
306 },
307 });
308 }
309
310 #[fuchsia::test(allow_stalls = false)]
311 async fn test_inspect_mixed_request_types() {
312 let inspector = inspect::Inspector::default();
313 let inspect_node = inspector.root().create_child("api_usage_counts");
314 let context = create_context().await;
315
316 let request_processor = RequestProcessor::new(context.delegate.clone());
317
318 let (_tx, rx) = mpsc::unbounded();
319 SettingTypeUsageInspectAgent::create_with_node(
320 context,
321 Rc::new(RefCell::new(rx)),
322 inspect_node,
323 )
324 .await;
325
326 request_processor
328 .send_and_receive(
329 SettingType::Display,
330 Request::SetDisplayInfo(SetDisplayInfo {
331 auto_brightness: Some(false),
332 ..SetDisplayInfo::default()
333 }),
334 )
335 .await;
336
337 request_processor.send_and_receive(SettingType::Display, Request::Get).await;
338
339 request_processor
340 .send_and_receive(
341 SettingType::Display,
342 Request::SetDisplayInfo(SetDisplayInfo {
343 auto_brightness: Some(true),
344 ..SetDisplayInfo::default()
345 }),
346 )
347 .await;
348
349 request_processor.send_and_receive(SettingType::Display, Request::Get).await;
350
351 assert_data_tree!(inspector, root: {
352 api_usage_counts: {
353 "Display": {
354 "Set": {
355 count: 2i64,
356 },
357 "Get": {
358 count: 2i64,
359 },
360 },
361 }
362 });
363 }
364}