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::StreamExt;
21use futures::channel::mpsc::UnboundedReceiver;
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
208 struct RequestProcessor {
212 delegate: service::message::Delegate,
213 }
214
215 impl RequestProcessor {
216 fn new(delegate: service::message::Delegate) -> Self {
217 RequestProcessor { delegate }
218 }
219
220 async fn send_and_receive(&self, setting_type: SettingType, setting_request: Request) {
221 let (messenger, _) =
222 self.delegate.create(MessengerType::Unbound).await.expect("should be created");
223
224 let (_, mut receptor) = self
225 .delegate
226 .create(MessengerType::Addressable(service::Address::Handler(setting_type)))
227 .await
228 .expect("should be created");
229
230 let _ = messenger.message(
231 HandlerPayload::Request(setting_request).into(),
232 service::message::Audience::Address(service::Address::Handler(setting_type)),
233 );
234
235 let _ = receptor.next_payload().await;
236 }
237 }
238
239 async fn create_context() -> Context {
240 Context::new(
241 service::MessageHub::create_hub()
242 .create(MessengerType::Unbound)
243 .await
244 .expect("should be present")
245 .1,
246 service::MessageHub::create_hub(),
247 )
248 .await
249 }
250
251 #[fuchsia::test(allow_stalls = false)]
252 async fn test_inspect() {
253 let inspector = inspect::Inspector::default();
254 let inspect_node = inspector.root().create_child("api_usage_counts");
255 let context = create_context().await;
256
257 let request_processor = RequestProcessor::new(context.delegate.clone());
258
259 let (_tx, rx) = mpsc::unbounded();
260 SettingTypeUsageInspectAgent::create_with_node(
261 context,
262 Rc::new(RefCell::new(rx)),
263 inspect_node,
264 )
265 .await;
266
267 let turn_off_auto_brightness = Request::SetDisplayInfo(SetDisplayInfo {
269 auto_brightness: Some(false),
270 ..SetDisplayInfo::default()
271 });
272 request_processor
273 .send_and_receive(SettingType::Display, turn_off_auto_brightness.clone())
274 .await;
275
276 request_processor.send_and_receive(SettingType::Display, turn_off_auto_brightness).await;
277
278 for _ in 0..100 {
279 request_processor
280 .send_and_receive(
281 SettingType::Intl,
282 Request::SetIntlInfo(IntlInfo {
283 locales: Some(vec![LocaleId { id: "en-US".to_string() }]),
284 temperature_unit: Some(TemperatureUnit::Celsius),
285 time_zone_id: Some("UTC".to_string()),
286 hour_cycle: None,
287 }),
288 )
289 .await;
290 }
291
292 assert_data_tree!(inspector, root: {
293 api_usage_counts: {
294 "Display": {
295 "Set": {
296 count: 2i64,
297 },
298 },
299 "Intl": {
300 "Set": {
301 count: 100i64
302 },
303 }
304 },
305 });
306 }
307
308 #[fuchsia::test(allow_stalls = false)]
309 async fn test_inspect_mixed_request_types() {
310 let inspector = inspect::Inspector::default();
311 let inspect_node = inspector.root().create_child("api_usage_counts");
312 let context = create_context().await;
313
314 let request_processor = RequestProcessor::new(context.delegate.clone());
315
316 let (_tx, rx) = mpsc::unbounded();
317 SettingTypeUsageInspectAgent::create_with_node(
318 context,
319 Rc::new(RefCell::new(rx)),
320 inspect_node,
321 )
322 .await;
323
324 request_processor
326 .send_and_receive(
327 SettingType::Display,
328 Request::SetDisplayInfo(SetDisplayInfo {
329 auto_brightness: Some(false),
330 ..SetDisplayInfo::default()
331 }),
332 )
333 .await;
334
335 request_processor.send_and_receive(SettingType::Display, Request::Get).await;
336
337 request_processor
338 .send_and_receive(
339 SettingType::Display,
340 Request::SetDisplayInfo(SetDisplayInfo {
341 auto_brightness: Some(true),
342 ..SetDisplayInfo::default()
343 }),
344 )
345 .await;
346
347 request_processor.send_and_receive(SettingType::Display, Request::Get).await;
348
349 assert_data_tree!(inspector, root: {
350 api_usage_counts: {
351 "Display": {
352 "Set": {
353 count: 2i64,
354 },
355 "Get": {
356 count: 2i64,
357 },
358 },
359 }
360 });
361 }
362}