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 super::advertise_data::{AdvertiseData, AdvertiseDataBuilder};
16 use super::advertise_settings::{
17     AdvertiseMode, AdvertiseSettings, AdvertiseSettingsBuilder, TxPowerLevel,
18 };
19 use super::chip::{rust_bluetooth_add, RustBluetoothChipCallbacks};
20 use super::packets::link_layer::{
21     Address, AddressType, LeLegacyAdvertisingPduBuilder, LeScanResponseBuilder, PacketType,
22 };
23 use crate::devices::chip::{ChipIdentifier, FacadeIdentifier};
24 use crate::devices::device::{AddChipResult, DeviceIdentifier};
25 use crate::devices::devices_handler::add_chip;
26 use crate::ffi::ffi_bluetooth;
27 use crate::wireless;
28 use cxx::{let_cxx_string, UniquePtr};
29 use lazy_static::lazy_static;
30 use log::{error, info, warn};
31 use netsim_proto::common::ChipKind;
32 use netsim_proto::model::chip::Bluetooth;
33 use netsim_proto::model::chip::{
34     ble_beacon::AdvertiseData as AdvertiseDataProto,
35     ble_beacon::AdvertiseSettings as AdvertiseSettingsProto, BleBeacon as BleBeaconProto,
36 };
37 use netsim_proto::model::chip_create::{
38     BleBeaconCreate as BleBeaconCreateProto, Chip as BuiltinProto,
39 };
40 use netsim_proto::model::{ChipCreate as ChipCreateProto, DeviceCreate as DeviceCreateProto};
41 use pdl_runtime::Packet;
42 use protobuf::{Message, MessageField};
43 use std::alloc::System;
44 use std::sync::{Mutex, RwLock};
45 use std::time::{Duration, Instant};
46 use std::{collections::HashMap, ptr::null};
47 
48 lazy_static! {
49     static ref EMPTY_ADDRESS: Address = Address::try_from(0u64).unwrap();
50     // A singleton that contains a hash map from chip id to RustBluetoothChip.
51     // It's used by `BeaconChip` to access `RustBluetoothChip` to call send_link_layer_packet().
52     static ref BT_CHIPS: RwLock<HashMap<ChipIdentifier, Mutex<UniquePtr<ffi_bluetooth::RustBluetoothChip>>>> =
53         RwLock::new(HashMap::new());
54     // Used to find beacon chip based on it's id from static methods.
55     pub(crate) static ref BEACON_CHIPS: RwLock<HashMap<ChipIdentifier, Mutex<BeaconChip>>> =
56         RwLock::new(HashMap::new());
57 }
58 
59 /// BeaconChip class.
60 pub struct BeaconChip {
61     device_name: String,
62     chip_id: ChipIdentifier,
63     address: Address,
64     advertise_settings: AdvertiseSettings,
65     advertise_data: AdvertiseData,
66     scan_response_data: AdvertiseData,
67     advertise_last: Option<Instant>,
68     advertise_start: Option<Instant>,
69 }
70 
71 impl BeaconChip {
new( device_name: String, chip_id: ChipIdentifier, address: String, ) -> Result<Self, String>72     pub fn new(
73         device_name: String,
74         chip_id: ChipIdentifier,
75         address: String,
76     ) -> Result<Self, String> {
77         Ok(BeaconChip {
78             chip_id,
79             device_name: device_name.clone(),
80             address: str_to_addr(&address)?,
81             advertise_settings: AdvertiseSettings::builder().build(),
82             advertise_data: AdvertiseData::builder(device_name.clone(), TxPowerLevel::default())
83                 .build()
84                 .unwrap(),
85             scan_response_data: AdvertiseData::builder(device_name, TxPowerLevel::default())
86                 .build()
87                 .unwrap(),
88             advertise_last: None,
89             advertise_start: None,
90         })
91     }
92 
from_proto( device_name: String, chip_id: ChipIdentifier, beacon_proto: &BleBeaconCreateProto, ) -> Result<Self, String>93     pub fn from_proto(
94         device_name: String,
95         chip_id: ChipIdentifier,
96         beacon_proto: &BleBeaconCreateProto,
97     ) -> Result<Self, String> {
98         let advertise_settings = AdvertiseSettings::from_proto(&beacon_proto.settings)?;
99         let advertise_data = AdvertiseData::from_proto(
100             device_name.clone(),
101             beacon_proto
102                 .settings
103                 .tx_power
104                 .as_ref()
105                 .map(TxPowerLevel::try_from)
106                 .transpose()?
107                 .unwrap_or_default(),
108             &beacon_proto.adv_data,
109         )?;
110         let scan_response_data = AdvertiseData::from_proto(
111             device_name.clone(),
112             advertise_settings.tx_power_level,
113             &beacon_proto.scan_response,
114         )?;
115 
116         let address = if beacon_proto.address == String::default() {
117             // Safe to unwrap here because chip_id is a u32 which is less than 6 bytes
118             u64::from(chip_id.0).try_into().unwrap()
119         } else {
120             str_to_addr(&beacon_proto.address)?
121         };
122 
123         Ok(BeaconChip {
124             device_name,
125             chip_id,
126             address,
127             advertise_settings,
128             advertise_data,
129             scan_response_data,
130             advertise_last: None,
131             advertise_start: None,
132         })
133     }
134 
send_link_layer_le_packet(&self, packet: &[u8], tx_power: i8)135     pub fn send_link_layer_le_packet(&self, packet: &[u8], tx_power: i8) {
136         let binding = BT_CHIPS.read().unwrap();
137         if let Some(rust_bluetooth_chip) = binding.get(&self.chip_id) {
138             rust_bluetooth_chip
139                 .lock()
140                 .expect("Failed to acquire lock on RustBluetoothChip")
141                 .pin_mut()
142                 .send_link_layer_le_packet(packet, tx_power);
143         } else {
144             warn!("Failed to get RustBluetoothChip for unknown chip id: {}", self.chip_id);
145         };
146     }
147 }
148 
149 // BEACON_CHIPS has ownership of all the BeaconChips, so we need a separate class to hold the callbacks.
150 // This class will be owned by rootcanal.
151 pub struct BeaconChipCallbacks {
152     chip_id: ChipIdentifier,
153 }
154 
155 impl RustBluetoothChipCallbacks for BeaconChipCallbacks {
tick(&mut self)156     fn tick(&mut self) {
157         let guard = BEACON_CHIPS.read().unwrap();
158         let mut beacon = guard.get(&self.chip_id);
159         if beacon.is_none() {
160             error!("could not find bluetooth beacon with chip id {}", self.chip_id);
161             return;
162         }
163         let mut beacon = beacon.unwrap().lock().expect("Failed to acquire lock on BeaconChip");
164 
165         if let (Some(start), Some(timeout)) =
166             (beacon.advertise_start, beacon.advertise_settings.timeout)
167         {
168             if start.elapsed() > timeout {
169                 return;
170             }
171         }
172 
173         if let Some(last) = beacon.advertise_last {
174             if last.elapsed() <= beacon.advertise_settings.mode.interval {
175                 return;
176             }
177         } else {
178             beacon.advertise_start = Some(Instant::now())
179         }
180 
181         beacon.advertise_last = Some(Instant::now());
182 
183         let packet = LeLegacyAdvertisingPduBuilder {
184             advertising_type: beacon.advertise_settings.get_packet_type(),
185             advertising_data: beacon.advertise_data.to_bytes(),
186             advertising_address_type: AddressType::Public,
187             target_address_type: AddressType::Public,
188             source_address: beacon.address,
189             destination_address: *EMPTY_ADDRESS,
190         }
191         .build()
192         .encode_to_vec()
193         .unwrap();
194 
195         beacon.send_link_layer_le_packet(&packet, beacon.advertise_settings.tx_power_level.dbm);
196     }
197 
receive_link_layer_packet( &mut self, source_address: String, destination_address: String, packet_type: u8, packet: &[u8], )198     fn receive_link_layer_packet(
199         &mut self,
200         source_address: String,
201         destination_address: String,
202         packet_type: u8,
203         packet: &[u8],
204     ) {
205         let guard = BEACON_CHIPS.read().unwrap();
206         let beacon = guard.get(&self.chip_id);
207         if beacon.is_none() {
208             error!("could not find bluetooth beacon with chip id {}", self.chip_id);
209             return;
210         }
211         let beacon = beacon.unwrap().lock().expect("Failed to acquire lock on BeaconChip");
212 
213         if beacon.advertise_settings.scannable
214             && destination_address == addr_to_str(beacon.address)
215             && packet_type == u8::from(PacketType::LeScan)
216         {
217             let packet = LeScanResponseBuilder {
218                 advertising_address_type: AddressType::Public,
219                 source_address: beacon.address,
220                 destination_address: beacon.address,
221                 scan_response_data: beacon.scan_response_data.to_bytes(),
222             }
223             .build()
224             .encode_to_vec()
225             .unwrap();
226 
227             beacon.send_link_layer_le_packet(&packet, beacon.advertise_settings.tx_power_level.dbm);
228         }
229     }
230 }
231 
232 /// Add a beacon device in rootcanal.
233 ///
234 /// Called by `devices/chip.rs`.
235 ///
236 /// Similar to `bluetooth_add()`.
237 #[cfg(not(test))]
ble_beacon_add( device_name: String, chip_id: ChipIdentifier, chip_proto: &ChipCreateProto, ) -> Result<FacadeIdentifier, String>238 pub fn ble_beacon_add(
239     device_name: String,
240     chip_id: ChipIdentifier,
241     chip_proto: &ChipCreateProto,
242 ) -> Result<FacadeIdentifier, String> {
243     let beacon_proto = match &chip_proto.chip {
244         Some(BuiltinProto::BleBeacon(beacon_proto)) => beacon_proto,
245         _ => return Err(String::from("failed to create ble beacon: unexpected chip type")),
246     };
247 
248     let beacon_chip = BeaconChip::from_proto(device_name, chip_id, beacon_proto)?;
249     if BEACON_CHIPS.write().unwrap().insert(chip_id, Mutex::new(beacon_chip)).is_some() {
250         return Err(format!(
251             "failed to create a bluetooth beacon chip with id {chip_id}: chip id already exists.",
252         ));
253     }
254 
255     let callbacks: Box<dyn RustBluetoothChipCallbacks> = Box::new(BeaconChipCallbacks { chip_id });
256     let add_rust_device_result = rust_bluetooth_add(
257         chip_id,
258         callbacks,
259         String::from("beacon"),
260         beacon_proto.address.clone(),
261     );
262     let rust_chip = add_rust_device_result.rust_chip;
263     let facade_id = add_rust_device_result.facade_id;
264     info!("Creating HCI facade_id: {} for chip_id: {}", facade_id, chip_id);
265     BT_CHIPS.write().unwrap().insert(chip_id, Mutex::new(rust_chip));
266 
267     Ok(FacadeIdentifier(facade_id))
268 }
269 
270 #[cfg(not(test))]
ble_beacon_remove( chip_id: ChipIdentifier, facade_id: FacadeIdentifier, ) -> Result<(), String>271 pub fn ble_beacon_remove(
272     chip_id: ChipIdentifier,
273     facade_id: FacadeIdentifier,
274 ) -> Result<(), String> {
275     let removed_beacon = BEACON_CHIPS.write().unwrap().remove(&chip_id);
276     let removed_radio = BT_CHIPS.write().unwrap().remove(&chip_id);
277     if removed_beacon.is_none() || removed_radio.is_none() {
278         Err(format!("failed to delete ble beacon chip: chip with id {chip_id} does not exist"))
279     } else {
280         ffi_bluetooth::bluetooth_remove_rust_device(facade_id.0);
281         Ok(())
282     }
283 }
284 
ble_beacon_patch( facade_id: FacadeIdentifier, chip_id: ChipIdentifier, patch: &BleBeaconProto, ) -> Result<(), String>285 pub fn ble_beacon_patch(
286     facade_id: FacadeIdentifier,
287     chip_id: ChipIdentifier,
288     patch: &BleBeaconProto,
289 ) -> Result<(), String> {
290     let mut guard = BEACON_CHIPS.write().unwrap();
291     let mut beacon = guard
292         .get_mut(&chip_id)
293         .ok_or(format!("could not find bluetooth beacon with chip id {chip_id} for patching"))?
294         .get_mut()
295         .unwrap();
296 
297     if patch.address != String::default() {
298         beacon.address = str_to_addr(&patch.address)?;
299         #[cfg(not(test))]
300         ffi_bluetooth::bluetooth_set_rust_device_address(
301             facade_id.0,
302             u64::from(beacon.address).to_le_bytes()[..6].try_into().unwrap(),
303         );
304     }
305 
306     if let Some(patch_settings) = patch.settings.as_ref() {
307         if let Some(interval) = patch_settings.interval.as_ref() {
308             beacon.advertise_settings.mode = interval.into();
309         }
310 
311         if let Some(tx_power) = patch_settings.tx_power.as_ref() {
312             beacon.advertise_settings.tx_power_level = tx_power.try_into()?
313         }
314 
315         beacon.advertise_settings.scannable =
316             patch_settings.scannable || beacon.advertise_settings.scannable;
317 
318         if patch_settings.timeout != u64::default() {
319             beacon.advertise_settings.timeout = Some(Duration::from_millis(patch_settings.timeout));
320         }
321     }
322 
323     if let Some(patch_adv_data) = patch.adv_data.as_ref() {
324         let mut builder = AdvertiseData::builder(
325             beacon.device_name.clone(),
326             beacon.advertise_settings.tx_power_level,
327         );
328 
329         if patch_adv_data.include_device_name || beacon.advertise_data.include_device_name {
330             builder.include_device_name();
331         }
332 
333         if patch_adv_data.include_tx_power_level || beacon.advertise_data.include_tx_power_level {
334             builder.include_tx_power_level();
335         }
336 
337         if !patch_adv_data.manufacturer_data.is_empty() {
338             builder.manufacturer_data(patch_adv_data.manufacturer_data.clone());
339         } else if let Some(manufacturer_data) = beacon.advertise_data.manufacturer_data.as_ref() {
340             builder.manufacturer_data(manufacturer_data.clone());
341         }
342 
343         beacon.advertise_data = builder.build()?;
344     }
345 
346     Ok(())
347 }
348 
ble_beacon_get( chip_id: ChipIdentifier, _facade_id: FacadeIdentifier, ) -> Result<BleBeaconProto, String>349 pub fn ble_beacon_get(
350     chip_id: ChipIdentifier,
351     _facade_id: FacadeIdentifier,
352 ) -> Result<BleBeaconProto, String> {
353     let guard = BEACON_CHIPS.read().unwrap();
354     let beacon = guard
355         .get(&chip_id)
356         .ok_or(format!("could not get bluetooth beacon with chip id {chip_id}"))?
357         .lock()
358         .expect("Failed to acquire lock on BeaconChip");
359     #[cfg(not(test))]
360     let bt = {
361         let bluetooth_bytes = ffi_bluetooth::bluetooth_get_cxx(_facade_id.0);
362         Some(Bluetooth::parse_from_bytes(&bluetooth_bytes).unwrap())
363     };
364     #[cfg(test)]
365     let bt = Some(netsim_proto::model::chip::Bluetooth::new());
366     Ok(BleBeaconProto {
367         bt: bt.into(),
368         address: addr_to_str(beacon.address),
369         settings: MessageField::some((&beacon.advertise_settings).try_into()?),
370         adv_data: MessageField::some((&beacon.advertise_data).into()),
371         ..Default::default()
372     })
373 }
374 
addr_to_str(addr: Address) -> String375 fn addr_to_str(addr: Address) -> String {
376     let bytes = u64::from(addr).to_le_bytes();
377     bytes[..5]
378         .iter()
379         .rfold(format!("{:02x}", bytes[5]), |addr, byte| addr + &format!(":{:02x}", byte))
380 }
381 
str_to_addr(addr: &str) -> Result<Address, String>382 fn str_to_addr(addr: &str) -> Result<Address, String> {
383     if addr == String::default() {
384         Ok(*EMPTY_ADDRESS)
385     } else {
386         if addr.len() != 17 {
387             return Err(String::from("failed to parse address: address was not the right length"));
388         }
389         let addr = addr.replace(':', "");
390         u64::from_str_radix(&addr, 16)
391             .map_err(|_| String::from("failed to parse address: invalid hex"))?
392             .try_into()
393             .map_err(|_| {
394                 String::from("failed to parse address: address must be smaller than 6 bytes")
395             })
396     }
397 }
398 
399 #[cfg(test)]
400 pub mod tests {
401     use std::ops::Add;
402     use std::sync::atomic::{AtomicU32, Ordering};
403     use std::thread;
404 
405     use netsim_proto::model::chip::ble_beacon::{
406         advertise_settings::{AdvertiseTxPower as AdvertiseTxPowerProto, Tx_power as TxPowerProto},
407         AdvertiseData as AdvertiseDataProto,
408     };
409 
410     use super::*;
411     // using ble_beacon_add from mocked.rs
412     use crate::bluetooth::ble_beacon_add;
413 
414     lazy_static! {
415         static ref TEST_GUID_GENERATOR: AtomicU32 = AtomicU32::new(0);
416     }
417 
next_id() -> ChipIdentifier418     fn next_id() -> ChipIdentifier {
419         ChipIdentifier(TEST_GUID_GENERATOR.fetch_add(1, Ordering::SeqCst))
420     }
421 
new_test_beacon_with_settings(settings: AdvertiseSettingsProto) -> ChipIdentifier422     fn new_test_beacon_with_settings(settings: AdvertiseSettingsProto) -> ChipIdentifier {
423         let id = next_id();
424 
425         let add_result = ble_beacon_add(
426             format!("test-device-{:?}", thread::current().id()),
427             id,
428             &ChipCreateProto {
429                 name: format!("test-beacon-chip-{:?}", thread::current().id()),
430                 chip: Some(BuiltinProto::BleBeacon(BleBeaconCreateProto {
431                     address: String::from("00:00:00:00:00:00"),
432                     settings: MessageField::some(settings),
433                     ..Default::default()
434                 })),
435                 ..Default::default()
436             },
437         );
438         assert!(add_result.is_ok(), "{}", add_result.unwrap_err());
439 
440         id
441     }
442 
cleanup_beacon(chip_id: ChipIdentifier)443     fn cleanup_beacon(chip_id: ChipIdentifier) {
444         BEACON_CHIPS.write().unwrap().remove(&chip_id);
445     }
446 
447     #[test]
test_beacon_get()448     fn test_beacon_get() {
449         let interval = Duration::from_millis(9999);
450         let settings = AdvertiseSettingsProto {
451             interval: Some(AdvertiseMode::new(interval).try_into().unwrap()),
452             ..Default::default()
453         };
454 
455         let id = new_test_beacon_with_settings(settings);
456 
457         let beacon = ble_beacon_get(id, FacadeIdentifier(0));
458         assert!(beacon.is_ok(), "{}", beacon.unwrap_err());
459         let beacon = beacon.unwrap();
460 
461         let interval_after_get =
462             beacon.settings.interval.as_ref().map(AdvertiseMode::from).unwrap().interval;
463 
464         assert_eq!(interval, interval_after_get);
465         cleanup_beacon(id);
466     }
467 
468     #[test]
test_beacon_patch()469     fn test_beacon_patch() {
470         let settings = AdvertiseSettingsProto {
471             interval: Some(AdvertiseMode::new(Duration::from_millis(0)).try_into().unwrap()),
472             ..Default::default()
473         };
474 
475         let id = new_test_beacon_with_settings(settings);
476 
477         let interval = Duration::from_millis(33);
478         let tx_power = TxPowerProto::TxPowerLevel(AdvertiseTxPowerProto::MEDIUM.into());
479         let scannable = true;
480         let patch_result = ble_beacon_patch(
481             FacadeIdentifier(0),
482             id,
483             &BleBeaconProto {
484                 settings: MessageField::some(AdvertiseSettingsProto {
485                     interval: Some(
486                         AdvertiseMode::new(Duration::from_millis(33)).try_into().unwrap(),
487                     ),
488                     scannable,
489                     tx_power: Some(tx_power.clone()),
490                     ..Default::default()
491                 }),
492                 ..Default::default()
493             },
494         );
495         assert!(patch_result.is_ok(), "{}", patch_result.unwrap_err());
496 
497         let beacon_proto = ble_beacon_get(id, FacadeIdentifier(0));
498         assert!(beacon_proto.is_ok(), "{}", beacon_proto.unwrap_err());
499         let beacon_proto = beacon_proto.unwrap();
500         let interval_after_patch =
501             beacon_proto.settings.interval.as_ref().map(AdvertiseMode::from).unwrap().interval;
502 
503         assert_eq!(interval, interval_after_patch);
504         assert_eq!(tx_power, *beacon_proto.settings.tx_power.as_ref().unwrap());
505         assert_eq!(scannable, beacon_proto.settings.scannable);
506         cleanup_beacon(id);
507     }
508 
509     #[test]
test_beacon_patch_default()510     fn test_beacon_patch_default() {
511         let settings =
512             AdvertiseSettingsProto { timeout: 1234, scannable: true, ..Default::default() };
513 
514         let id = new_test_beacon_with_settings(settings.clone());
515 
516         let patch_result = ble_beacon_patch(FacadeIdentifier(0), id, &BleBeaconProto::default());
517         assert!(patch_result.is_ok(), "{}", patch_result.unwrap_err());
518 
519         let beacon_proto = ble_beacon_get(id, FacadeIdentifier(0));
520         assert!(beacon_proto.is_ok(), "{}", beacon_proto.unwrap_err());
521         let beacon_proto = beacon_proto.unwrap();
522 
523         let settings_after_patch = beacon_proto.settings.unwrap();
524         assert_eq!(settings.timeout, settings_after_patch.timeout);
525         assert_eq!(settings.scannable, settings_after_patch.scannable);
526     }
527 
528     #[test]
test_str_to_addr_succeeds()529     fn test_str_to_addr_succeeds() {
530         let addr = str_to_addr("be:ac:12:34:00:0f");
531         assert_eq!(Address::try_from(0xbe_ac_12_34_00_0f).unwrap(), addr.unwrap());
532     }
533 
534     #[test]
test_empty_str_to_addr_succeeds()535     fn test_empty_str_to_addr_succeeds() {
536         let addr = str_to_addr("00:00:00:00:00:00");
537         assert_eq!(Address::try_from(0).unwrap(), addr.unwrap());
538     }
539 
540     #[test]
test_str_to_addr_fails()541     fn test_str_to_addr_fails() {
542         let addr = str_to_addr("hi mom!");
543         assert!(addr.is_err());
544     }
545 
546     #[test]
test_invalid_str_to_addr_fails()547     fn test_invalid_str_to_addr_fails() {
548         let addr = str_to_addr("56:78:9a:bc:de:fg");
549         assert!(addr.is_err());
550     }
551 
552     #[test]
test_long_str_to_addr_fails()553     fn test_long_str_to_addr_fails() {
554         let addr = str_to_addr("55:55:55:55:55:55:55:55");
555         assert!(addr.is_err());
556     }
557 
558     #[test]
test_short_str_to_addr_fails()559     fn test_short_str_to_addr_fails() {
560         let addr = str_to_addr("ab:cd");
561         assert!(addr.is_err());
562     }
563 
564     #[test]
test_addr_to_str_succeeds()565     fn test_addr_to_str_succeeds() {
566         let addr: u64 = 0xbe_ac_12_34_00_0f;
567         assert_eq!("be:ac:12:34:00:0f", addr_to_str(addr.try_into().unwrap()))
568     }
569 
570     #[test]
test_empty_addr_to_str_succeeds()571     fn test_empty_addr_to_str_succeeds() {
572         let addr: u64 = 0;
573         assert_eq!("00:00:00:00:00:00", addr_to_str(addr.try_into().unwrap()))
574     }
575 
576     #[test]
test_small_addr_to_str_succeeds()577     fn test_small_addr_to_str_succeeds() {
578         let addr: u64 = 123;
579         assert_eq!("00:00:00:00:00:7b", addr_to_str(addr.try_into().unwrap()))
580     }
581 }
582