xref: /aosp_15_r20/tools/netsim/rust/daemon/src/wireless/bluetooth.rs (revision cf78ab8cffb8fc9207af348f23af247fb04370a6)
1 // Copyright 2023 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 use crate::devices::chip::ChipIdentifier;
16 use crate::ffi::ffi_bluetooth;
17 use crate::wireless::{WirelessAdaptor, WirelessAdaptorImpl};
18 
19 use bytes::Bytes;
20 use cxx::{let_cxx_string, CxxString, CxxVector};
21 use log::{error, info};
22 use netsim_proto::config::Bluetooth as BluetoothConfig;
23 use netsim_proto::configuration::Controller as RootcanalController;
24 use netsim_proto::model::chip::Bluetooth as ProtoBluetooth;
25 use netsim_proto::model::chip::Radio as ProtoRadio;
26 use netsim_proto::model::Chip as ProtoChip;
27 use netsim_proto::stats::invalid_packet::Reason as InvalidPacketReason;
28 use netsim_proto::stats::{netsim_radio_stats, InvalidPacket, NetsimRadioStats as ProtoRadioStats};
29 use protobuf::{Enum, Message, MessageField};
30 use std::collections::BTreeMap;
31 use std::sync::atomic::{AtomicBool, Ordering};
32 use std::sync::{Arc, Mutex, OnceLock};
33 
34 static WIRELESS_BT_MUTEX: Mutex<()> = Mutex::new(());
35 
36 pub type RootcanalIdentifier = u32;
37 
38 // BLUETOOTH_INVALID_PACKETS is a singleton that contains a hash map from
39 // RootcanalIdentifier to Vec<InvalidPacket>
40 // This singleton is only used when Rootcanal reports invalid
41 // packets by rootcanal_id and we add those to Bluetooth struct.
42 static BLUETOOTH_INVALID_PACKETS: OnceLock<
43     Arc<Mutex<BTreeMap<RootcanalIdentifier, Vec<InvalidPacket>>>>,
44 > = OnceLock::new();
45 
get_bluetooth_invalid_packets() -> Arc<Mutex<BTreeMap<RootcanalIdentifier, Vec<InvalidPacket>>>>46 fn get_bluetooth_invalid_packets() -> Arc<Mutex<BTreeMap<RootcanalIdentifier, Vec<InvalidPacket>>>>
47 {
48     BLUETOOTH_INVALID_PACKETS.get_or_init(|| Arc::new(Mutex::new(BTreeMap::new()))).clone()
49 }
50 
51 /// Parameters for creating Bluetooth chips
52 /// allow(dead_code) due to not being used in unit tests
53 #[allow(dead_code)]
54 pub struct CreateParams {
55     pub address: String,
56     pub bt_properties: Option<MessageField<RootcanalController>>,
57 }
58 
59 /// Bluetooth struct will keep track of rootcanal_id
60 pub struct Bluetooth {
61     rootcanal_id: RootcanalIdentifier,
62     low_energy_enabled: AtomicBool,
63     classic_enabled: AtomicBool,
64 }
65 
patch_state( enabled: &AtomicBool, state: Option<bool>, id: RootcanalIdentifier, is_low_energy: bool, )66 fn patch_state(
67     enabled: &AtomicBool,
68     state: Option<bool>,
69     id: RootcanalIdentifier,
70     is_low_energy: bool,
71 ) {
72     if let Some(state) = state {
73         let _guard = WIRELESS_BT_MUTEX.lock().expect("Failed to lock WIRELESS_BT_MUTEX");
74         let last_state: bool = enabled.swap(state, Ordering::SeqCst);
75         match (last_state, state) {
76             (false, true) => ffi_bluetooth::add_device_to_phy(id, is_low_energy),
77             (true, false) => ffi_bluetooth::remove_device_from_phy(id, is_low_energy),
78             _ => {}
79         }
80     }
81 }
82 
83 impl Drop for Bluetooth {
drop(&mut self)84     fn drop(&mut self) {
85         // Lock to protect id_to_chip_info_ table in C++
86         let _guard = WIRELESS_BT_MUTEX.lock().expect("Failed to acquire lock on WIRELESS_BT_MUTEX");
87         ffi_bluetooth::bluetooth_remove(self.rootcanal_id);
88         get_bluetooth_invalid_packets().lock().expect("invalid packets").remove(&self.rootcanal_id);
89     }
90 }
91 
92 impl WirelessAdaptor for Bluetooth {
handle_request(&self, packet: &Bytes)93     fn handle_request(&self, packet: &Bytes) {
94         // Lock to protect device_to_transport_ table in C++
95         let _guard = WIRELESS_BT_MUTEX.lock().expect("Failed to acquire lock on WIRELESS_BT_MUTEX");
96         ffi_bluetooth::handle_bt_request(self.rootcanal_id, packet[0], &packet[1..].to_vec())
97     }
98 
reset(&self)99     fn reset(&self) {
100         let _guard = WIRELESS_BT_MUTEX.lock().expect("Failed to acquire lock on WIRELESS_BT_MUTEX");
101         ffi_bluetooth::bluetooth_reset(self.rootcanal_id);
102         self.low_energy_enabled.store(true, Ordering::SeqCst);
103         self.classic_enabled.store(true, Ordering::SeqCst);
104     }
105 
get(&self) -> ProtoChip106     fn get(&self) -> ProtoChip {
107         let bluetooth_bytes = ffi_bluetooth::bluetooth_get_cxx(self.rootcanal_id);
108         let bt_proto = ProtoBluetooth::parse_from_bytes(&bluetooth_bytes).unwrap();
109         let mut chip_proto = ProtoChip::new();
110         chip_proto.set_bt(ProtoBluetooth {
111             low_energy: Some(ProtoRadio {
112                 state: self.low_energy_enabled.load(Ordering::SeqCst).into(),
113                 tx_count: bt_proto.low_energy.tx_count,
114                 rx_count: bt_proto.low_energy.rx_count,
115                 ..Default::default()
116             })
117             .into(),
118             classic: Some(ProtoRadio {
119                 state: self.classic_enabled.load(Ordering::SeqCst).into(),
120                 tx_count: bt_proto.classic.tx_count,
121                 rx_count: bt_proto.classic.rx_count,
122                 ..Default::default()
123             })
124             .into(),
125             address: bt_proto.address,
126             bt_properties: bt_proto.bt_properties,
127             ..Default::default()
128         });
129         chip_proto
130     }
131 
patch(&self, chip: &ProtoChip)132     fn patch(&self, chip: &ProtoChip) {
133         if !chip.has_bt() {
134             return;
135         }
136         let id = self.rootcanal_id;
137         patch_state(&self.low_energy_enabled, chip.bt().low_energy.state, id, true);
138         patch_state(&self.classic_enabled, chip.bt().classic.state, id, false);
139     }
140 
get_stats(&self, duration_secs: u64) -> Vec<ProtoRadioStats>141     fn get_stats(&self, duration_secs: u64) -> Vec<ProtoRadioStats> {
142         // Construct NetsimRadioStats for BLE and Classic.
143         let mut ble_stats_proto = ProtoRadioStats::new();
144         ble_stats_proto.set_duration_secs(duration_secs);
145         if let Some(v) = get_bluetooth_invalid_packets()
146             .lock()
147             .expect("Failed to acquire lock on BLUETOOTH_INVALID_PACKETS")
148             .get(&self.rootcanal_id)
149         {
150             for invalid_packet in v {
151                 ble_stats_proto.invalid_packets.push(invalid_packet.clone());
152             }
153         }
154         let mut classic_stats_proto = ble_stats_proto.clone();
155 
156         // Obtain the Chip Information with get()
157         let chip_proto = self.get();
158         if chip_proto.has_bt() {
159             // Setting values for BLE Radio Stats
160             ble_stats_proto.set_kind(netsim_radio_stats::Kind::BLUETOOTH_LOW_ENERGY);
161             ble_stats_proto.set_tx_count(chip_proto.bt().low_energy.tx_count);
162             ble_stats_proto.set_rx_count(chip_proto.bt().low_energy.rx_count);
163             // Setting values for Classic Radio Stats
164             classic_stats_proto.set_kind(netsim_radio_stats::Kind::BLUETOOTH_CLASSIC);
165             classic_stats_proto.set_tx_count(chip_proto.bt().classic.tx_count);
166             classic_stats_proto.set_rx_count(chip_proto.bt().classic.rx_count);
167         }
168         vec![ble_stats_proto, classic_stats_proto]
169     }
170 }
171 
172 /// Create a new Emulated Bluetooth Chip
173 /// allow(dead_code) due to not being used in unit tests
174 #[allow(dead_code)]
new(create_params: &CreateParams, chip_id: ChipIdentifier) -> WirelessAdaptorImpl175 pub fn new(create_params: &CreateParams, chip_id: ChipIdentifier) -> WirelessAdaptorImpl {
176     // Lock to protect id_to_chip_info_ table in C++
177     let _guard = WIRELESS_BT_MUTEX.lock().expect("Failed to acquire lock on WIRELESS_BT_MUTEX");
178     let_cxx_string!(cxx_address = create_params.address.clone());
179     let proto_bytes = match &create_params.bt_properties {
180         Some(properties) => properties.write_to_bytes().unwrap(),
181         None => Vec::new(),
182     };
183     let rootcanal_id = ffi_bluetooth::bluetooth_add(chip_id.0, &cxx_address, &proto_bytes);
184     info!("Bluetooth WirelessAdaptor created with rootcanal_id: {rootcanal_id} chip_id: {chip_id}");
185     let wireless_adaptor = Bluetooth {
186         rootcanal_id,
187         low_energy_enabled: AtomicBool::new(true),
188         classic_enabled: AtomicBool::new(true),
189     };
190     get_bluetooth_invalid_packets()
191         .lock()
192         .expect("invalid packets")
193         .insert(rootcanal_id, Vec::new());
194     Box::new(wireless_adaptor)
195 }
196 
197 /// Starts the Bluetooth service.
bluetooth_start(config: &MessageField<BluetoothConfig>, instance_num: u16)198 pub fn bluetooth_start(config: &MessageField<BluetoothConfig>, instance_num: u16) {
199     let proto_bytes = config.as_ref().unwrap_or_default().write_to_bytes().unwrap();
200     ffi_bluetooth::bluetooth_start(&proto_bytes, instance_num);
201 }
202 
203 /// Stops the Bluetooth service.
bluetooth_stop()204 pub fn bluetooth_stop() {
205     ffi_bluetooth::bluetooth_stop();
206 }
207 
208 /// Report Invalid Packet
report_invalid_packet( rootcanal_id: RootcanalIdentifier, reason: InvalidPacketReason, description: String, packet: Vec<u8>, )209 pub fn report_invalid_packet(
210     rootcanal_id: RootcanalIdentifier,
211     reason: InvalidPacketReason,
212     description: String,
213     packet: Vec<u8>,
214 ) {
215     // TODO(b/330726276): spawn task on tokio once context is provided from rust_main
216     let _ = std::thread::Builder::new().name("report_invalid_packet".to_string()).spawn(move || {
217         match get_bluetooth_invalid_packets().lock().unwrap().get_mut(&rootcanal_id) {
218             Some(v) => {
219                 // Remove the earliest reported packet if length greater than 5
220                 if v.len() >= 5 {
221                     v.remove(0);
222                 }
223                 // append error packet
224                 let mut invalid_packet = InvalidPacket::new();
225                 invalid_packet.set_reason(reason);
226                 invalid_packet.set_description(description.clone());
227                 invalid_packet.set_packet(packet.clone());
228                 v.push(invalid_packet);
229                 // Log the report
230                 info!("Invalid Packet for rootcanal_id: {rootcanal_id}, reason: {reason:?}, description: {description:?}, packet: {packet:?}");
231             }
232             None => error!("Bluetooth WirelessAdaptor not created for rootcanal_id: {rootcanal_id}"),
233         }
234     });
235 }
236 
237 /// (Called by C++) Report Invalid Packet
report_invalid_packet_cxx( rootcanal_id: RootcanalIdentifier, reason: i32, description: &CxxString, packet: &CxxVector<u8>, )238 pub fn report_invalid_packet_cxx(
239     rootcanal_id: RootcanalIdentifier,
240     reason: i32,
241     description: &CxxString,
242     packet: &CxxVector<u8>,
243 ) {
244     report_invalid_packet(
245         rootcanal_id,
246         InvalidPacketReason::from_i32(reason).unwrap_or(InvalidPacketReason::UNKNOWN),
247         description.to_string(),
248         packet.as_slice().to_vec(),
249     );
250 }
251