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