xref: /aosp_15_r20/tools/netsim/rust/daemon/src/captures/capture.rs (revision cf78ab8cffb8fc9207af348f23af247fb04370a6)
1*cf78ab8cSAndroid Build Coastguard Worker // Copyright 2023 Google LLC
2*cf78ab8cSAndroid Build Coastguard Worker //
3*cf78ab8cSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License");
4*cf78ab8cSAndroid Build Coastguard Worker // you may not use this file except in compliance with the License.
5*cf78ab8cSAndroid Build Coastguard Worker // You may obtain a copy of the License at
6*cf78ab8cSAndroid Build Coastguard Worker //
7*cf78ab8cSAndroid Build Coastguard Worker //     https://www.apache.org/licenses/LICENSE-2.0
8*cf78ab8cSAndroid Build Coastguard Worker //
9*cf78ab8cSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*cf78ab8cSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS,
11*cf78ab8cSAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*cf78ab8cSAndroid Build Coastguard Worker // See the License for the specific language governing permissions and
13*cf78ab8cSAndroid Build Coastguard Worker // limitations under the License.
14*cf78ab8cSAndroid Build Coastguard Worker 
15*cf78ab8cSAndroid Build Coastguard Worker //! The internal structure of CaptureInfo and CaptureMaps
16*cf78ab8cSAndroid Build Coastguard Worker //!
17*cf78ab8cSAndroid Build Coastguard Worker //! CaptureInfo is the internal structure of any Capture that includes
18*cf78ab8cSAndroid Build Coastguard Worker //! the protobuf structure. CaptureMaps contains mappings of
19*cf78ab8cSAndroid Build Coastguard Worker //! ChipIdentifier and <FacadeIdentifier, Kind> to CaptureInfo.
20*cf78ab8cSAndroid Build Coastguard Worker 
21*cf78ab8cSAndroid Build Coastguard Worker use bytes::Bytes;
22*cf78ab8cSAndroid Build Coastguard Worker 
23*cf78ab8cSAndroid Build Coastguard Worker use std::collections::btree_map::{Iter, Values};
24*cf78ab8cSAndroid Build Coastguard Worker use std::collections::BTreeMap;
25*cf78ab8cSAndroid Build Coastguard Worker use std::fs::{File, OpenOptions};
26*cf78ab8cSAndroid Build Coastguard Worker use std::io::{Error, ErrorKind, Result};
27*cf78ab8cSAndroid Build Coastguard Worker use std::sync::mpsc::channel;
28*cf78ab8cSAndroid Build Coastguard Worker use std::sync::mpsc::{Receiver, Sender};
29*cf78ab8cSAndroid Build Coastguard Worker use std::sync::{Arc, Mutex};
30*cf78ab8cSAndroid Build Coastguard Worker use std::thread;
31*cf78ab8cSAndroid Build Coastguard Worker use std::time::{SystemTime, UNIX_EPOCH};
32*cf78ab8cSAndroid Build Coastguard Worker 
33*cf78ab8cSAndroid Build Coastguard Worker use super::pcap_util::{write_pcap_header, write_pcapng_header, LinkType};
34*cf78ab8cSAndroid Build Coastguard Worker use log::{info, warn};
35*cf78ab8cSAndroid Build Coastguard Worker 
36*cf78ab8cSAndroid Build Coastguard Worker use netsim_proto::{common::ChipKind, model::Capture as ProtoCapture};
37*cf78ab8cSAndroid Build Coastguard Worker use protobuf::well_known_types::timestamp::Timestamp;
38*cf78ab8cSAndroid Build Coastguard Worker 
39*cf78ab8cSAndroid Build Coastguard Worker use crate::events::{ChipAdded, ChipRemoved, Event};
40*cf78ab8cSAndroid Build Coastguard Worker use crate::resource::clone_captures;
41*cf78ab8cSAndroid Build Coastguard Worker 
42*cf78ab8cSAndroid Build Coastguard Worker use crate::captures::captures_handler::handle_packet;
43*cf78ab8cSAndroid Build Coastguard Worker use crate::captures::pcap_util::PacketDirection;
44*cf78ab8cSAndroid Build Coastguard Worker use crate::devices::chip::ChipIdentifier;
45*cf78ab8cSAndroid Build Coastguard Worker 
46*cf78ab8cSAndroid Build Coastguard Worker /// Internal Capture struct
47*cf78ab8cSAndroid Build Coastguard Worker pub struct CaptureInfo {
48*cf78ab8cSAndroid Build Coastguard Worker     /// Some(File) if the file is opened and capture is actively happening.
49*cf78ab8cSAndroid Build Coastguard Worker     /// None if the file is not opened.
50*cf78ab8cSAndroid Build Coastguard Worker     pub file: Option<File>,
51*cf78ab8cSAndroid Build Coastguard Worker     // Following items will be returned as ProtoCapture. (state: file.is_some())
52*cf78ab8cSAndroid Build Coastguard Worker     id: ChipIdentifier,
53*cf78ab8cSAndroid Build Coastguard Worker     /// ChipKind (BLUETOOTH, WIFI, or UWB)
54*cf78ab8cSAndroid Build Coastguard Worker     pub chip_kind: ChipKind,
55*cf78ab8cSAndroid Build Coastguard Worker     /// Device name
56*cf78ab8cSAndroid Build Coastguard Worker     pub device_name: String,
57*cf78ab8cSAndroid Build Coastguard Worker     /// Size of pcap file
58*cf78ab8cSAndroid Build Coastguard Worker     pub size: usize,
59*cf78ab8cSAndroid Build Coastguard Worker     /// Number of packet records
60*cf78ab8cSAndroid Build Coastguard Worker     pub records: i32,
61*cf78ab8cSAndroid Build Coastguard Worker     /// Timestamp as seconds
62*cf78ab8cSAndroid Build Coastguard Worker     pub seconds: i64,
63*cf78ab8cSAndroid Build Coastguard Worker     /// Timestamp as sub-nanoseconds
64*cf78ab8cSAndroid Build Coastguard Worker     pub nanos: i32,
65*cf78ab8cSAndroid Build Coastguard Worker     /// Boolean status of whether the device is connected to netsim
66*cf78ab8cSAndroid Build Coastguard Worker     pub valid: bool,
67*cf78ab8cSAndroid Build Coastguard Worker     /// Extension (pcap or pcapng)
68*cf78ab8cSAndroid Build Coastguard Worker     pub extension: String,
69*cf78ab8cSAndroid Build Coastguard Worker }
70*cf78ab8cSAndroid Build Coastguard Worker 
71*cf78ab8cSAndroid Build Coastguard Worker /// Captures contains a recent copy of all chips and their ChipKind, chip_id,
72*cf78ab8cSAndroid Build Coastguard Worker /// and owning device name.
73*cf78ab8cSAndroid Build Coastguard Worker ///
74*cf78ab8cSAndroid Build Coastguard Worker /// Information for any recent or ongoing captures is also stored in the ProtoCapture.
75*cf78ab8cSAndroid Build Coastguard Worker pub struct Captures {
76*cf78ab8cSAndroid Build Coastguard Worker     /// A mapping of chip id to CaptureInfo.
77*cf78ab8cSAndroid Build Coastguard Worker     ///
78*cf78ab8cSAndroid Build Coastguard Worker     /// BTreeMap is used for chip_id_to_capture, so that the CaptureInfo can always be
79*cf78ab8cSAndroid Build Coastguard Worker     /// ordered by ChipId. ListCaptureResponse will produce a ordered list of CaptureInfos.
80*cf78ab8cSAndroid Build Coastguard Worker     pub chip_id_to_capture: BTreeMap<ChipIdentifier, Arc<Mutex<CaptureInfo>>>,
81*cf78ab8cSAndroid Build Coastguard Worker     sender: Sender<CapturePacket>,
82*cf78ab8cSAndroid Build Coastguard Worker }
83*cf78ab8cSAndroid Build Coastguard Worker 
84*cf78ab8cSAndroid Build Coastguard Worker impl CaptureInfo {
85*cf78ab8cSAndroid Build Coastguard Worker     /// Create an instance of CaptureInfo
new(chip_kind: ChipKind, chip_id: ChipIdentifier, device_name: String) -> Self86*cf78ab8cSAndroid Build Coastguard Worker     pub fn new(chip_kind: ChipKind, chip_id: ChipIdentifier, device_name: String) -> Self {
87*cf78ab8cSAndroid Build Coastguard Worker         let extension = match chip_kind {
88*cf78ab8cSAndroid Build Coastguard Worker             ChipKind::UWB => "pcapng".to_string(),
89*cf78ab8cSAndroid Build Coastguard Worker             _ => "pcap".to_string(),
90*cf78ab8cSAndroid Build Coastguard Worker         };
91*cf78ab8cSAndroid Build Coastguard Worker         CaptureInfo {
92*cf78ab8cSAndroid Build Coastguard Worker             id: chip_id,
93*cf78ab8cSAndroid Build Coastguard Worker             chip_kind,
94*cf78ab8cSAndroid Build Coastguard Worker             device_name,
95*cf78ab8cSAndroid Build Coastguard Worker             size: 0,
96*cf78ab8cSAndroid Build Coastguard Worker             records: 0,
97*cf78ab8cSAndroid Build Coastguard Worker             seconds: 0,
98*cf78ab8cSAndroid Build Coastguard Worker             nanos: 0,
99*cf78ab8cSAndroid Build Coastguard Worker             valid: true,
100*cf78ab8cSAndroid Build Coastguard Worker             file: None,
101*cf78ab8cSAndroid Build Coastguard Worker             extension,
102*cf78ab8cSAndroid Build Coastguard Worker         }
103*cf78ab8cSAndroid Build Coastguard Worker     }
104*cf78ab8cSAndroid Build Coastguard Worker 
105*cf78ab8cSAndroid Build Coastguard Worker     /// Creates a pcap file with headers and store it under temp directory.
106*cf78ab8cSAndroid Build Coastguard Worker     ///
107*cf78ab8cSAndroid Build Coastguard Worker     /// The lifecycle of the file is NOT tied to the lifecycle of the struct
108*cf78ab8cSAndroid Build Coastguard Worker     /// Format: /tmp/netsimd/$USER/pcaps/netsim-{chip_id}-{device_name}-{chip_kind}.{extension}
start_capture(&mut self) -> Result<()>109*cf78ab8cSAndroid Build Coastguard Worker     pub fn start_capture(&mut self) -> Result<()> {
110*cf78ab8cSAndroid Build Coastguard Worker         if self.file.is_some() {
111*cf78ab8cSAndroid Build Coastguard Worker             return Ok(());
112*cf78ab8cSAndroid Build Coastguard Worker         }
113*cf78ab8cSAndroid Build Coastguard Worker         let mut filename = netsim_common::system::netsimd_temp_dir();
114*cf78ab8cSAndroid Build Coastguard Worker         filename.push("pcaps");
115*cf78ab8cSAndroid Build Coastguard Worker         std::fs::create_dir_all(&filename)?;
116*cf78ab8cSAndroid Build Coastguard Worker         filename.push(format!(
117*cf78ab8cSAndroid Build Coastguard Worker             "netsim-{:?}-{:}-{:?}.{}",
118*cf78ab8cSAndroid Build Coastguard Worker             self.id, self.device_name, self.chip_kind, self.extension
119*cf78ab8cSAndroid Build Coastguard Worker         ));
120*cf78ab8cSAndroid Build Coastguard Worker         let mut file = OpenOptions::new().write(true).truncate(true).create(true).open(filename)?;
121*cf78ab8cSAndroid Build Coastguard Worker         let link_type = match self.chip_kind {
122*cf78ab8cSAndroid Build Coastguard Worker             ChipKind::BLUETOOTH => LinkType::BluetoothHciH4WithPhdr,
123*cf78ab8cSAndroid Build Coastguard Worker             ChipKind::BLUETOOTH_BEACON => LinkType::BluetoothHciH4WithPhdr,
124*cf78ab8cSAndroid Build Coastguard Worker             ChipKind::WIFI => LinkType::Ieee80211RadioTap,
125*cf78ab8cSAndroid Build Coastguard Worker             ChipKind::UWB => LinkType::FiraUci,
126*cf78ab8cSAndroid Build Coastguard Worker             _ => return Err(Error::new(ErrorKind::Other, "Unsupported link type")),
127*cf78ab8cSAndroid Build Coastguard Worker         };
128*cf78ab8cSAndroid Build Coastguard Worker         let size = match self.extension.as_str() {
129*cf78ab8cSAndroid Build Coastguard Worker             "pcap" => write_pcap_header(link_type, &mut file)?,
130*cf78ab8cSAndroid Build Coastguard Worker             "pcapng" => write_pcapng_header(link_type, &mut file)?,
131*cf78ab8cSAndroid Build Coastguard Worker             _ => return Err(Error::new(ErrorKind::Other, "Incorrect Extension for file")),
132*cf78ab8cSAndroid Build Coastguard Worker         };
133*cf78ab8cSAndroid Build Coastguard Worker         let timestamp = SystemTime::now().duration_since(UNIX_EPOCH).expect("Time went backwards");
134*cf78ab8cSAndroid Build Coastguard Worker         self.size = size;
135*cf78ab8cSAndroid Build Coastguard Worker         self.records = 0;
136*cf78ab8cSAndroid Build Coastguard Worker         self.seconds = timestamp.as_secs() as i64;
137*cf78ab8cSAndroid Build Coastguard Worker         self.nanos = timestamp.subsec_nanos() as i32;
138*cf78ab8cSAndroid Build Coastguard Worker         self.file = Some(file);
139*cf78ab8cSAndroid Build Coastguard Worker         Ok(())
140*cf78ab8cSAndroid Build Coastguard Worker     }
141*cf78ab8cSAndroid Build Coastguard Worker 
142*cf78ab8cSAndroid Build Coastguard Worker     /// Closes file by removing ownership of self.file.
143*cf78ab8cSAndroid Build Coastguard Worker     ///
144*cf78ab8cSAndroid Build Coastguard Worker     /// Capture info will still retain the size and record count
145*cf78ab8cSAndroid Build Coastguard Worker     /// So it can be downloaded easily when GetCapture is invoked.
stop_capture(&mut self)146*cf78ab8cSAndroid Build Coastguard Worker     pub fn stop_capture(&mut self) {
147*cf78ab8cSAndroid Build Coastguard Worker         self.file = None;
148*cf78ab8cSAndroid Build Coastguard Worker     }
149*cf78ab8cSAndroid Build Coastguard Worker 
150*cf78ab8cSAndroid Build Coastguard Worker     /// Returns a Capture protobuf from CaptureInfo
get_capture_proto(&self) -> ProtoCapture151*cf78ab8cSAndroid Build Coastguard Worker     pub fn get_capture_proto(&self) -> ProtoCapture {
152*cf78ab8cSAndroid Build Coastguard Worker         let timestamp =
153*cf78ab8cSAndroid Build Coastguard Worker             Timestamp { seconds: self.seconds, nanos: self.nanos, ..Default::default() };
154*cf78ab8cSAndroid Build Coastguard Worker         ProtoCapture {
155*cf78ab8cSAndroid Build Coastguard Worker             id: self.id.0,
156*cf78ab8cSAndroid Build Coastguard Worker             chip_kind: self.chip_kind.into(),
157*cf78ab8cSAndroid Build Coastguard Worker             device_name: self.device_name.clone(),
158*cf78ab8cSAndroid Build Coastguard Worker             state: Some(self.file.is_some()),
159*cf78ab8cSAndroid Build Coastguard Worker             size: self.size as i32,
160*cf78ab8cSAndroid Build Coastguard Worker             records: self.records,
161*cf78ab8cSAndroid Build Coastguard Worker             timestamp: Some(timestamp).into(),
162*cf78ab8cSAndroid Build Coastguard Worker             valid: self.valid,
163*cf78ab8cSAndroid Build Coastguard Worker             ..Default::default()
164*cf78ab8cSAndroid Build Coastguard Worker         }
165*cf78ab8cSAndroid Build Coastguard Worker     }
166*cf78ab8cSAndroid Build Coastguard Worker }
167*cf78ab8cSAndroid Build Coastguard Worker 
168*cf78ab8cSAndroid Build Coastguard Worker struct CapturePacket {
169*cf78ab8cSAndroid Build Coastguard Worker     chip_id: ChipIdentifier,
170*cf78ab8cSAndroid Build Coastguard Worker     packet: Bytes,
171*cf78ab8cSAndroid Build Coastguard Worker     packet_type: u32,
172*cf78ab8cSAndroid Build Coastguard Worker     direction: PacketDirection,
173*cf78ab8cSAndroid Build Coastguard Worker }
174*cf78ab8cSAndroid Build Coastguard Worker 
175*cf78ab8cSAndroid Build Coastguard Worker impl Captures {
176*cf78ab8cSAndroid Build Coastguard Worker     /// Create an instance of Captures, which includes 2 empty hashmaps
new() -> Self177*cf78ab8cSAndroid Build Coastguard Worker     pub fn new() -> Self {
178*cf78ab8cSAndroid Build Coastguard Worker         let (sender, rx) = channel::<CapturePacket>();
179*cf78ab8cSAndroid Build Coastguard Worker         let _ =
180*cf78ab8cSAndroid Build Coastguard Worker             thread::Builder::new().name("capture_packet_handler".to_string()).spawn(move || {
181*cf78ab8cSAndroid Build Coastguard Worker                 while let Ok(CapturePacket { chip_id, packet, packet_type, direction }) = rx.recv()
182*cf78ab8cSAndroid Build Coastguard Worker                 {
183*cf78ab8cSAndroid Build Coastguard Worker                     handle_packet(chip_id, &packet, packet_type, direction);
184*cf78ab8cSAndroid Build Coastguard Worker                 }
185*cf78ab8cSAndroid Build Coastguard Worker             });
186*cf78ab8cSAndroid Build Coastguard Worker         Captures {
187*cf78ab8cSAndroid Build Coastguard Worker             chip_id_to_capture: BTreeMap::<ChipIdentifier, Arc<Mutex<CaptureInfo>>>::new(),
188*cf78ab8cSAndroid Build Coastguard Worker             sender,
189*cf78ab8cSAndroid Build Coastguard Worker         }
190*cf78ab8cSAndroid Build Coastguard Worker     }
191*cf78ab8cSAndroid Build Coastguard Worker 
192*cf78ab8cSAndroid Build Coastguard Worker     /// Sends a captured packet to the output processing thread.
send( &self, chip_id: ChipIdentifier, packet: &Bytes, packet_type: u32, direction: PacketDirection, )193*cf78ab8cSAndroid Build Coastguard Worker     pub fn send(
194*cf78ab8cSAndroid Build Coastguard Worker         &self,
195*cf78ab8cSAndroid Build Coastguard Worker         chip_id: ChipIdentifier,
196*cf78ab8cSAndroid Build Coastguard Worker         packet: &Bytes,
197*cf78ab8cSAndroid Build Coastguard Worker         packet_type: u32,
198*cf78ab8cSAndroid Build Coastguard Worker         direction: PacketDirection,
199*cf78ab8cSAndroid Build Coastguard Worker     ) {
200*cf78ab8cSAndroid Build Coastguard Worker         let _ = self.sender.send(CapturePacket {
201*cf78ab8cSAndroid Build Coastguard Worker             chip_id,
202*cf78ab8cSAndroid Build Coastguard Worker             packet: packet.clone(),
203*cf78ab8cSAndroid Build Coastguard Worker             packet_type,
204*cf78ab8cSAndroid Build Coastguard Worker             direction,
205*cf78ab8cSAndroid Build Coastguard Worker         });
206*cf78ab8cSAndroid Build Coastguard Worker     }
207*cf78ab8cSAndroid Build Coastguard Worker 
208*cf78ab8cSAndroid Build Coastguard Worker     /// Returns true if key exists in Captures.chip_id_to_capture
contains(&self, key: ChipIdentifier) -> bool209*cf78ab8cSAndroid Build Coastguard Worker     pub fn contains(&self, key: ChipIdentifier) -> bool {
210*cf78ab8cSAndroid Build Coastguard Worker         self.chip_id_to_capture.contains_key(&key)
211*cf78ab8cSAndroid Build Coastguard Worker     }
212*cf78ab8cSAndroid Build Coastguard Worker 
213*cf78ab8cSAndroid Build Coastguard Worker     /// Returns an Option of lockable and mutable CaptureInfo with given key
get(&mut self, key: ChipIdentifier) -> Option<&mut Arc<Mutex<CaptureInfo>>>214*cf78ab8cSAndroid Build Coastguard Worker     pub fn get(&mut self, key: ChipIdentifier) -> Option<&mut Arc<Mutex<CaptureInfo>>> {
215*cf78ab8cSAndroid Build Coastguard Worker         self.chip_id_to_capture.get_mut(&key)
216*cf78ab8cSAndroid Build Coastguard Worker     }
217*cf78ab8cSAndroid Build Coastguard Worker 
218*cf78ab8cSAndroid Build Coastguard Worker     /// Inserts the given CatpureInfo into Captures hashmaps
insert(&mut self, capture: CaptureInfo)219*cf78ab8cSAndroid Build Coastguard Worker     pub fn insert(&mut self, capture: CaptureInfo) {
220*cf78ab8cSAndroid Build Coastguard Worker         let chip_id = capture.id;
221*cf78ab8cSAndroid Build Coastguard Worker         let arc_capture = Arc::new(Mutex::new(capture));
222*cf78ab8cSAndroid Build Coastguard Worker         self.chip_id_to_capture.insert(chip_id, arc_capture.clone());
223*cf78ab8cSAndroid Build Coastguard Worker     }
224*cf78ab8cSAndroid Build Coastguard Worker 
225*cf78ab8cSAndroid Build Coastguard Worker     /// Returns true if chip_id_to_capture is empty
is_empty(&self) -> bool226*cf78ab8cSAndroid Build Coastguard Worker     pub fn is_empty(&self) -> bool {
227*cf78ab8cSAndroid Build Coastguard Worker         self.chip_id_to_capture.is_empty()
228*cf78ab8cSAndroid Build Coastguard Worker     }
229*cf78ab8cSAndroid Build Coastguard Worker 
230*cf78ab8cSAndroid Build Coastguard Worker     /// Returns an iterable object of chip_id_to_capture hashmap
iter(&self) -> Iter<ChipIdentifier, Arc<Mutex<CaptureInfo>>>231*cf78ab8cSAndroid Build Coastguard Worker     pub fn iter(&self) -> Iter<ChipIdentifier, Arc<Mutex<CaptureInfo>>> {
232*cf78ab8cSAndroid Build Coastguard Worker         self.chip_id_to_capture.iter()
233*cf78ab8cSAndroid Build Coastguard Worker     }
234*cf78ab8cSAndroid Build Coastguard Worker 
235*cf78ab8cSAndroid Build Coastguard Worker     /// Removes a CaptureInfo with given key from Captures
236*cf78ab8cSAndroid Build Coastguard Worker     ///
237*cf78ab8cSAndroid Build Coastguard Worker     /// When Capture is removed, remove from each map and also invoke closing of files.
remove(&mut self, key: &ChipIdentifier)238*cf78ab8cSAndroid Build Coastguard Worker     pub fn remove(&mut self, key: &ChipIdentifier) {
239*cf78ab8cSAndroid Build Coastguard Worker         if let Some(arc_capture) = self.chip_id_to_capture.get(key) {
240*cf78ab8cSAndroid Build Coastguard Worker             let mut capture = arc_capture.lock().expect("Failed to acquire lock on CaptureInfo");
241*cf78ab8cSAndroid Build Coastguard Worker             // Valid is marked false when chip is disconnected from netsim
242*cf78ab8cSAndroid Build Coastguard Worker             capture.valid = false;
243*cf78ab8cSAndroid Build Coastguard Worker             capture.stop_capture();
244*cf78ab8cSAndroid Build Coastguard Worker         } else {
245*cf78ab8cSAndroid Build Coastguard Worker             info!("key does not exist in Captures");
246*cf78ab8cSAndroid Build Coastguard Worker         }
247*cf78ab8cSAndroid Build Coastguard Worker         // CaptureInfo is not removed even after chip is removed
248*cf78ab8cSAndroid Build Coastguard Worker     }
249*cf78ab8cSAndroid Build Coastguard Worker 
250*cf78ab8cSAndroid Build Coastguard Worker     /// Returns Values of chip_id_to_capture hashmap values
values(&self) -> Values<ChipIdentifier, Arc<Mutex<CaptureInfo>>>251*cf78ab8cSAndroid Build Coastguard Worker     pub fn values(&self) -> Values<ChipIdentifier, Arc<Mutex<CaptureInfo>>> {
252*cf78ab8cSAndroid Build Coastguard Worker         self.chip_id_to_capture.values()
253*cf78ab8cSAndroid Build Coastguard Worker     }
254*cf78ab8cSAndroid Build Coastguard Worker }
255*cf78ab8cSAndroid Build Coastguard Worker 
256*cf78ab8cSAndroid Build Coastguard Worker impl Default for Captures {
default() -> Self257*cf78ab8cSAndroid Build Coastguard Worker     fn default() -> Self {
258*cf78ab8cSAndroid Build Coastguard Worker         Self::new()
259*cf78ab8cSAndroid Build Coastguard Worker     }
260*cf78ab8cSAndroid Build Coastguard Worker }
261*cf78ab8cSAndroid Build Coastguard Worker 
262*cf78ab8cSAndroid Build Coastguard Worker /// Create a thread to process events that matter to the Capture resource.
263*cf78ab8cSAndroid Build Coastguard Worker ///
264*cf78ab8cSAndroid Build Coastguard Worker /// We maintain a CaptureInfo for each chip that has been
265*cf78ab8cSAndroid Build Coastguard Worker /// connected to the simulation. This procedure monitors ChipAdded
266*cf78ab8cSAndroid Build Coastguard Worker /// and ChipRemoved events and updates the collection of CaptureInfo.
267*cf78ab8cSAndroid Build Coastguard Worker ///
spawn_capture_event_subscriber(event_rx: Receiver<Event>, capture: bool)268*cf78ab8cSAndroid Build Coastguard Worker pub fn spawn_capture_event_subscriber(event_rx: Receiver<Event>, capture: bool) {
269*cf78ab8cSAndroid Build Coastguard Worker     let _ =
270*cf78ab8cSAndroid Build Coastguard Worker         thread::Builder::new().name("capture_event_subscriber".to_string()).spawn(move || loop {
271*cf78ab8cSAndroid Build Coastguard Worker             match event_rx.recv() {
272*cf78ab8cSAndroid Build Coastguard Worker                 Ok(Event::ChipAdded(ChipAdded { chip_id, chip_kind, device_name, .. })) => {
273*cf78ab8cSAndroid Build Coastguard Worker                     let mut capture_info =
274*cf78ab8cSAndroid Build Coastguard Worker                         CaptureInfo::new(chip_kind, chip_id, device_name.clone());
275*cf78ab8cSAndroid Build Coastguard Worker                     if capture {
276*cf78ab8cSAndroid Build Coastguard Worker                         if let Err(err) = capture_info.start_capture() {
277*cf78ab8cSAndroid Build Coastguard Worker                             warn!("{err:?}");
278*cf78ab8cSAndroid Build Coastguard Worker                         }
279*cf78ab8cSAndroid Build Coastguard Worker                     }
280*cf78ab8cSAndroid Build Coastguard Worker                     clone_captures().write().unwrap().insert(capture_info);
281*cf78ab8cSAndroid Build Coastguard Worker                     info!("Capture event: ChipAdded chip_id: {chip_id} device_name: {device_name}");
282*cf78ab8cSAndroid Build Coastguard Worker                 }
283*cf78ab8cSAndroid Build Coastguard Worker                 Ok(Event::ChipRemoved(ChipRemoved { chip_id, .. })) => {
284*cf78ab8cSAndroid Build Coastguard Worker                     clone_captures().write().unwrap().remove(&chip_id);
285*cf78ab8cSAndroid Build Coastguard Worker                     info!("Capture event: ChipRemoved chip_id: {chip_id}");
286*cf78ab8cSAndroid Build Coastguard Worker                 }
287*cf78ab8cSAndroid Build Coastguard Worker                 _ => {}
288*cf78ab8cSAndroid Build Coastguard Worker             }
289*cf78ab8cSAndroid Build Coastguard Worker         });
290*cf78ab8cSAndroid Build Coastguard Worker }
291