1use std::cell::Cell;
6use std::collections::HashMap;
7use std::rc::Rc;
8
9use bluetooth_traits::{
10 BluetoothCharacteristicMsg, BluetoothDescriptorMsg, BluetoothRequest, BluetoothResponse,
11 BluetoothServiceMsg,
12};
13use dom_struct::dom_struct;
14use ipc_channel::ipc::IpcSender;
15use profile_traits::ipc;
16
17use crate::conversions::Convert;
18use crate::dom::bindings::cell::DomRefCell;
19use crate::dom::bindings::codegen::Bindings::BluetoothDeviceBinding::BluetoothDeviceMethods;
20use crate::dom::bindings::codegen::Bindings::BluetoothRemoteGATTServerBinding::BluetoothRemoteGATTServerMethods;
21use crate::dom::bindings::error::{Error, ErrorResult};
22use crate::dom::bindings::inheritance::Castable;
23use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object};
24use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
25use crate::dom::bindings::str::DOMString;
26use crate::dom::bluetooth::{AsyncBluetoothListener, Bluetooth, response_async};
27use crate::dom::bluetoothcharacteristicproperties::BluetoothCharacteristicProperties;
28use crate::dom::bluetoothremotegattcharacteristic::BluetoothRemoteGATTCharacteristic;
29use crate::dom::bluetoothremotegattdescriptor::BluetoothRemoteGATTDescriptor;
30use crate::dom::bluetoothremotegattserver::BluetoothRemoteGATTServer;
31use crate::dom::bluetoothremotegattservice::BluetoothRemoteGATTService;
32use crate::dom::eventtarget::EventTarget;
33use crate::dom::globalscope::GlobalScope;
34use crate::dom::promise::Promise;
35use crate::realms::InRealm;
36use crate::script_runtime::CanGc;
37
38#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
39#[derive(JSTraceable, MallocSizeOf)]
40struct AttributeInstanceMap {
41 service_map: DomRefCell<HashMap<String, Dom<BluetoothRemoteGATTService>>>,
42 characteristic_map: DomRefCell<HashMap<String, Dom<BluetoothRemoteGATTCharacteristic>>>,
43 descriptor_map: DomRefCell<HashMap<String, Dom<BluetoothRemoteGATTDescriptor>>>,
44}
45
46#[dom_struct]
48pub(crate) struct BluetoothDevice {
49 eventtarget: EventTarget,
50 id: DOMString,
51 name: Option<DOMString>,
52 gatt: MutNullableDom<BluetoothRemoteGATTServer>,
53 context: Dom<Bluetooth>,
54 attribute_instance_map: AttributeInstanceMap,
55 watching_advertisements: Cell<bool>,
56}
57
58impl BluetoothDevice {
59 pub(crate) fn new_inherited(
60 id: DOMString,
61 name: Option<DOMString>,
62 context: &Bluetooth,
63 ) -> BluetoothDevice {
64 BluetoothDevice {
65 eventtarget: EventTarget::new_inherited(),
66 id,
67 name,
68 gatt: Default::default(),
69 context: Dom::from_ref(context),
70 attribute_instance_map: AttributeInstanceMap {
71 service_map: DomRefCell::new(HashMap::new()),
72 characteristic_map: DomRefCell::new(HashMap::new()),
73 descriptor_map: DomRefCell::new(HashMap::new()),
74 },
75 watching_advertisements: Cell::new(false),
76 }
77 }
78
79 pub(crate) fn new(
80 global: &GlobalScope,
81 id: DOMString,
82 name: Option<DOMString>,
83 context: &Bluetooth,
84 can_gc: CanGc,
85 ) -> DomRoot<BluetoothDevice> {
86 reflect_dom_object(
87 Box::new(BluetoothDevice::new_inherited(id, name, context)),
88 global,
89 can_gc,
90 )
91 }
92
93 pub(crate) fn get_gatt(&self) -> DomRoot<BluetoothRemoteGATTServer> {
94 self.gatt
95 .or_init(|| BluetoothRemoteGATTServer::new(&self.global(), self, CanGc::note()))
96 }
97
98 fn get_context(&self) -> DomRoot<Bluetooth> {
99 DomRoot::from_ref(&self.context)
100 }
101
102 pub(crate) fn get_or_create_service(
103 &self,
104 service: &BluetoothServiceMsg,
105 server: &BluetoothRemoteGATTServer,
106 can_gc: CanGc,
107 ) -> DomRoot<BluetoothRemoteGATTService> {
108 let service_map_ref = &self.attribute_instance_map.service_map;
109 let mut service_map = service_map_ref.borrow_mut();
110 if let Some(existing_service) = service_map.get(&service.instance_id) {
111 return DomRoot::from_ref(existing_service);
112 }
113 let bt_service = BluetoothRemoteGATTService::new(
114 &server.global(),
115 &server.Device(),
116 DOMString::from(service.uuid.clone()),
117 service.is_primary,
118 service.instance_id.clone(),
119 can_gc,
120 );
121 service_map.insert(service.instance_id.clone(), Dom::from_ref(&bt_service));
122 bt_service
123 }
124
125 pub(crate) fn get_or_create_characteristic(
126 &self,
127 characteristic: &BluetoothCharacteristicMsg,
128 service: &BluetoothRemoteGATTService,
129 can_gc: CanGc,
130 ) -> DomRoot<BluetoothRemoteGATTCharacteristic> {
131 let characteristic_map_ref = &self.attribute_instance_map.characteristic_map;
132 let mut characteristic_map = characteristic_map_ref.borrow_mut();
133 if let Some(existing_characteristic) = characteristic_map.get(&characteristic.instance_id) {
134 return DomRoot::from_ref(existing_characteristic);
135 }
136 let properties = BluetoothCharacteristicProperties::new(
137 &service.global(),
138 characteristic.broadcast,
139 characteristic.read,
140 characteristic.write_without_response,
141 characteristic.write,
142 characteristic.notify,
143 characteristic.indicate,
144 characteristic.authenticated_signed_writes,
145 characteristic.reliable_write,
146 characteristic.writable_auxiliaries,
147 can_gc,
148 );
149 let bt_characteristic = BluetoothRemoteGATTCharacteristic::new(
150 &service.global(),
151 service,
152 DOMString::from(characteristic.uuid.clone()),
153 &properties,
154 characteristic.instance_id.clone(),
155 can_gc,
156 );
157 characteristic_map.insert(
158 characteristic.instance_id.clone(),
159 Dom::from_ref(&bt_characteristic),
160 );
161 bt_characteristic
162 }
163
164 pub(crate) fn is_represented_device_null(&self) -> bool {
165 let (sender, receiver) = ipc::channel(self.global().time_profiler_chan().clone()).unwrap();
166 self.get_bluetooth_thread()
167 .send(BluetoothRequest::IsRepresentedDeviceNull(
168 self.Id().to_string(),
169 sender,
170 ))
171 .unwrap();
172 receiver.recv().unwrap()
173 }
174
175 pub(crate) fn get_or_create_descriptor(
176 &self,
177 descriptor: &BluetoothDescriptorMsg,
178 characteristic: &BluetoothRemoteGATTCharacteristic,
179 can_gc: CanGc,
180 ) -> DomRoot<BluetoothRemoteGATTDescriptor> {
181 let descriptor_map_ref = &self.attribute_instance_map.descriptor_map;
182 let mut descriptor_map = descriptor_map_ref.borrow_mut();
183 if let Some(existing_descriptor) = descriptor_map.get(&descriptor.instance_id) {
184 return DomRoot::from_ref(existing_descriptor);
185 }
186 let bt_descriptor = BluetoothRemoteGATTDescriptor::new(
187 &characteristic.global(),
188 characteristic,
189 DOMString::from(descriptor.uuid.clone()),
190 descriptor.instance_id.clone(),
191 can_gc,
192 );
193 descriptor_map.insert(
194 descriptor.instance_id.clone(),
195 Dom::from_ref(&bt_descriptor),
196 );
197 bt_descriptor
198 }
199
200 fn get_bluetooth_thread(&self) -> IpcSender<BluetoothRequest> {
201 self.global().as_window().bluetooth_thread()
202 }
203
204 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
206 pub(crate) fn clean_up_disconnected_device(&self, can_gc: CanGc) {
207 self.get_gatt().set_connected(false);
209
210 let mut service_map = self.attribute_instance_map.service_map.borrow_mut();
217 let service_ids = service_map.drain().map(|(id, _)| id).collect();
218
219 let mut characteristic_map = self.attribute_instance_map.characteristic_map.borrow_mut();
220 let characteristic_ids = characteristic_map.drain().map(|(id, _)| id).collect();
221
222 let mut descriptor_map = self.attribute_instance_map.descriptor_map.borrow_mut();
223 let descriptor_ids = descriptor_map.drain().map(|(id, _)| id).collect();
224
225 let _ = self
228 .get_bluetooth_thread()
229 .send(BluetoothRequest::SetRepresentedToNull(
230 service_ids,
231 characteristic_ids,
232 descriptor_ids,
233 ));
234
235 self.upcast::<EventTarget>()
237 .fire_bubbling_event(atom!("gattserverdisconnected"), can_gc);
238 }
239
240 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
242 pub(crate) fn garbage_collect_the_connection(&self) -> ErrorResult {
243 let context = self.get_context();
247 for (id, device) in context.get_device_map().borrow().iter() {
248 if id == &self.Id().to_string() && device.get_gatt().Connected() {
250 return Ok(());
251 }
252 }
253
254 let (sender, receiver) = ipc::channel(self.global().time_profiler_chan().clone()).unwrap();
256 self.get_bluetooth_thread()
257 .send(BluetoothRequest::GATTServerDisconnect(
258 String::from(self.Id()),
259 sender,
260 ))
261 .unwrap();
262 receiver.recv().unwrap().map_err(Convert::convert)
263 }
264}
265
266impl BluetoothDeviceMethods<crate::DomTypeHolder> for BluetoothDevice {
267 fn Id(&self) -> DOMString {
269 self.id.clone()
270 }
271
272 fn GetName(&self) -> Option<DOMString> {
274 self.name.clone()
275 }
276
277 fn GetGatt(&self) -> Option<DomRoot<BluetoothRemoteGATTServer>> {
279 if self
281 .global()
282 .as_window()
283 .bluetooth_extra_permission_data()
284 .allowed_devices_contains_id(self.id.clone()) &&
285 !self.is_represented_device_null()
286 {
287 return Some(self.get_gatt());
288 }
289 None
291 }
292
293 fn WatchAdvertisements(&self, comp: InRealm, can_gc: CanGc) -> Rc<Promise> {
295 let p = Promise::new_in_current_realm(comp, can_gc);
296 let sender = response_async(&p, self);
297 self.get_bluetooth_thread()
301 .send(BluetoothRequest::WatchAdvertisements(
302 String::from(self.Id()),
303 sender,
304 ))
305 .unwrap();
306 p
307 }
308
309 fn UnwatchAdvertisements(&self) {
311 self.watching_advertisements.set(false)
313 }
315
316 fn WatchingAdvertisements(&self) -> bool {
318 self.watching_advertisements.get()
319 }
320
321 event_handler!(
323 gattserverdisconnected,
324 GetOngattserverdisconnected,
325 SetOngattserverdisconnected
326 );
327}
328
329impl AsyncBluetoothListener for BluetoothDevice {
330 fn handle_response(&self, response: BluetoothResponse, promise: &Rc<Promise>, can_gc: CanGc) {
331 match response {
332 BluetoothResponse::WatchAdvertisements(_result) => {
334 self.watching_advertisements.set(true);
336 promise.resolve_native(&(), can_gc);
338 },
339 _ => promise.reject_error(Error::Type("Something went wrong...".to_owned()), can_gc),
340 }
341 }
342}