1use std::cell::Cell;
6use std::collections::HashMap;
7use std::rc::Rc;
8
9use base::generic_channel::GenericSender;
10use bluetooth_traits::{
11 BluetoothCharacteristicMsg, BluetoothDescriptorMsg, BluetoothRequest, BluetoothResponse,
12 BluetoothServiceMsg,
13};
14use dom_struct::dom_struct;
15use js::realm::CurrentRealm;
16use profile_traits::generic_channel;
17
18use crate::conversions::Convert;
19use crate::dom::bindings::cell::DomRefCell;
20use crate::dom::bindings::codegen::Bindings::BluetoothDeviceBinding::BluetoothDeviceMethods;
21use crate::dom::bindings::codegen::Bindings::BluetoothRemoteGATTServerBinding::BluetoothRemoteGATTServerMethods;
22use crate::dom::bindings::error::{Error, ErrorResult};
23use crate::dom::bindings::inheritance::Castable;
24use crate::dom::bindings::reflector::{DomGlobal, reflect_dom_object_with_cx};
25use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
26use crate::dom::bindings::str::DOMString;
27use crate::dom::bluetooth::{AsyncBluetoothListener, Bluetooth, response_async};
28use crate::dom::bluetoothcharacteristicproperties::BluetoothCharacteristicProperties;
29use crate::dom::bluetoothremotegattcharacteristic::BluetoothRemoteGATTCharacteristic;
30use crate::dom::bluetoothremotegattdescriptor::BluetoothRemoteGATTDescriptor;
31use crate::dom::bluetoothremotegattserver::BluetoothRemoteGATTServer;
32use crate::dom::bluetoothremotegattservice::BluetoothRemoteGATTService;
33use crate::dom::eventtarget::EventTarget;
34use crate::dom::globalscope::GlobalScope;
35use crate::dom::promise::Promise;
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 cx: &mut js::context::JSContext,
81 global: &GlobalScope,
82 id: DOMString,
83 name: Option<DOMString>,
84 context: &Bluetooth,
85 ) -> DomRoot<BluetoothDevice> {
86 reflect_dom_object_with_cx(
87 Box::new(BluetoothDevice::new_inherited(id, name, context)),
88 global,
89 cx,
90 )
91 }
92
93 pub(crate) fn get_gatt(
94 &self,
95 cx: &mut js::context::JSContext,
96 ) -> DomRoot<BluetoothRemoteGATTServer> {
97 self.gatt
98 .or_init(|| BluetoothRemoteGATTServer::new(cx, &self.global(), self))
99 }
100
101 fn get_context(&self) -> DomRoot<Bluetooth> {
102 DomRoot::from_ref(&self.context)
103 }
104
105 pub(crate) fn get_or_create_service(
106 &self,
107 cx: &mut js::context::JSContext,
108 service: &BluetoothServiceMsg,
109 server: &BluetoothRemoteGATTServer,
110 ) -> DomRoot<BluetoothRemoteGATTService> {
111 let service_map_ref = &self.attribute_instance_map.service_map;
112 let mut service_map = service_map_ref.borrow_mut();
113 if let Some(existing_service) = service_map.get(&service.instance_id) {
114 return DomRoot::from_ref(existing_service);
115 }
116 let bt_service = BluetoothRemoteGATTService::new(
117 cx,
118 &server.global(),
119 &server.Device(),
120 DOMString::from(service.uuid.clone()),
121 service.is_primary,
122 service.instance_id.clone(),
123 );
124 service_map.insert(service.instance_id.clone(), Dom::from_ref(&bt_service));
125 bt_service
126 }
127
128 pub(crate) fn get_or_create_characteristic(
129 &self,
130 cx: &mut js::context::JSContext,
131 characteristic: &BluetoothCharacteristicMsg,
132 service: &BluetoothRemoteGATTService,
133 ) -> DomRoot<BluetoothRemoteGATTCharacteristic> {
134 let characteristic_map_ref = &self.attribute_instance_map.characteristic_map;
135 let mut characteristic_map = characteristic_map_ref.borrow_mut();
136 if let Some(existing_characteristic) = characteristic_map.get(&characteristic.instance_id) {
137 return DomRoot::from_ref(existing_characteristic);
138 }
139 let properties = BluetoothCharacteristicProperties::new(
140 cx,
141 &service.global(),
142 characteristic.broadcast,
143 characteristic.read,
144 characteristic.write_without_response,
145 characteristic.write,
146 characteristic.notify,
147 characteristic.indicate,
148 characteristic.authenticated_signed_writes,
149 characteristic.reliable_write,
150 characteristic.writable_auxiliaries,
151 );
152 let bt_characteristic = BluetoothRemoteGATTCharacteristic::new(
153 cx,
154 &service.global(),
155 service,
156 DOMString::from(characteristic.uuid.clone()),
157 &properties,
158 characteristic.instance_id.clone(),
159 );
160 characteristic_map.insert(
161 characteristic.instance_id.clone(),
162 Dom::from_ref(&bt_characteristic),
163 );
164 bt_characteristic
165 }
166
167 pub(crate) fn is_represented_device_null(&self) -> bool {
168 let (sender, receiver) =
169 generic_channel::channel(self.global().time_profiler_chan().clone()).unwrap();
170 self.get_bluetooth_thread()
171 .send(BluetoothRequest::IsRepresentedDeviceNull(
172 self.Id().to_string(),
173 sender,
174 ))
175 .unwrap();
176 receiver.recv().unwrap()
177 }
178
179 pub(crate) fn get_or_create_descriptor(
180 &self,
181 cx: &mut js::context::JSContext,
182 descriptor: &BluetoothDescriptorMsg,
183 characteristic: &BluetoothRemoteGATTCharacteristic,
184 ) -> DomRoot<BluetoothRemoteGATTDescriptor> {
185 let descriptor_map_ref = &self.attribute_instance_map.descriptor_map;
186 let mut descriptor_map = descriptor_map_ref.borrow_mut();
187 if let Some(existing_descriptor) = descriptor_map.get(&descriptor.instance_id) {
188 return DomRoot::from_ref(existing_descriptor);
189 }
190 let bt_descriptor = BluetoothRemoteGATTDescriptor::new(
191 cx,
192 &characteristic.global(),
193 characteristic,
194 DOMString::from(descriptor.uuid.clone()),
195 descriptor.instance_id.clone(),
196 );
197 descriptor_map.insert(
198 descriptor.instance_id.clone(),
199 Dom::from_ref(&bt_descriptor),
200 );
201 bt_descriptor
202 }
203
204 fn get_bluetooth_thread(&self) -> GenericSender<BluetoothRequest> {
205 self.global().as_window().bluetooth_thread()
206 }
207
208 pub(crate) fn clean_up_disconnected_device(&self, cx: &mut js::context::JSContext) {
210 self.get_gatt(cx).set_connected(false);
212
213 let mut service_map = self.attribute_instance_map.service_map.borrow_mut();
220 let service_ids = service_map.drain().map(|(id, _)| id).collect();
221
222 let mut characteristic_map = self.attribute_instance_map.characteristic_map.borrow_mut();
223 let characteristic_ids = characteristic_map.drain().map(|(id, _)| id).collect();
224
225 let mut descriptor_map = self.attribute_instance_map.descriptor_map.borrow_mut();
226 let descriptor_ids = descriptor_map.drain().map(|(id, _)| id).collect();
227
228 let _ = self
231 .get_bluetooth_thread()
232 .send(BluetoothRequest::SetRepresentedToNull(
233 service_ids,
234 characteristic_ids,
235 descriptor_ids,
236 ));
237
238 self.upcast::<EventTarget>()
240 .fire_bubbling_event(atom!("gattserverdisconnected"), CanGc::from_cx(cx));
241 }
242
243 pub(crate) fn garbage_collect_the_connection(
245 &self,
246 cx: &mut js::context::JSContext,
247 ) -> ErrorResult {
248 let context = self.get_context();
252 for (id, device) in context.get_device_map().borrow().iter() {
253 if id == &self.Id().to_string() && device.get_gatt(cx).Connected() {
255 return Ok(());
256 }
257 }
258
259 let (sender, receiver) =
261 generic_channel::channel(self.global().time_profiler_chan().clone()).unwrap();
262 self.get_bluetooth_thread()
263 .send(BluetoothRequest::GATTServerDisconnect(
264 String::from(self.Id()),
265 sender,
266 ))
267 .unwrap();
268 receiver.recv().unwrap().map_err(Convert::convert)
269 }
270}
271
272impl BluetoothDeviceMethods<crate::DomTypeHolder> for BluetoothDevice {
273 fn Id(&self) -> DOMString {
275 self.id.clone()
276 }
277
278 fn GetName(&self) -> Option<DOMString> {
280 self.name.clone()
281 }
282
283 fn GetGatt(
285 &self,
286 cx: &mut js::context::JSContext,
287 ) -> Option<DomRoot<BluetoothRemoteGATTServer>> {
288 if self
290 .global()
291 .as_window()
292 .bluetooth_extra_permission_data()
293 .allowed_devices_contains_id(self.id.clone()) &&
294 !self.is_represented_device_null()
295 {
296 return Some(self.get_gatt(cx));
297 }
298 None
300 }
301
302 fn WatchAdvertisements(&self, cx: &mut CurrentRealm) -> Rc<Promise> {
304 let p = Promise::new_in_realm(cx);
305 let sender = response_async(&p, self);
306 self.get_bluetooth_thread()
310 .send(BluetoothRequest::WatchAdvertisements(
311 String::from(self.Id()),
312 sender,
313 ))
314 .unwrap();
315 p
316 }
317
318 fn UnwatchAdvertisements(&self) {
320 self.watching_advertisements.set(false)
322 }
324
325 fn WatchingAdvertisements(&self) -> bool {
327 self.watching_advertisements.get()
328 }
329
330 event_handler!(
332 gattserverdisconnected,
333 GetOngattserverdisconnected,
334 SetOngattserverdisconnected
335 );
336}
337
338impl AsyncBluetoothListener for BluetoothDevice {
339 fn handle_response(
340 &self,
341 cx: &mut js::context::JSContext,
342 response: BluetoothResponse,
343 promise: &Rc<Promise>,
344 ) {
345 match response {
346 BluetoothResponse::WatchAdvertisements(_result) => {
348 self.watching_advertisements.set(true);
350 promise.resolve_native(&(), CanGc::from_cx(cx));
352 },
353 _ => promise.reject_error(
354 Error::Type(c"Something went wrong...".to_owned()),
355 CanGc::from_cx(cx),
356 ),
357 }
358 }
359}