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