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