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