• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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