1use std::cell::Cell;
6use std::collections::HashMap;
7use std::rc::Rc;
8
9use dom_struct::dom_struct;
10use js::context::JSContext;
11use js::realm::CurrentRealm;
12use profile_traits::generic_channel;
13use script_bindings::cell::DomRefCell;
14use script_bindings::reflector::reflect_dom_object_with_cx;
15use servo_base::generic_channel::GenericSender;
16use servo_bluetooth_traits::{
17 BluetoothCharacteristicMsg, BluetoothDescriptorMsg, BluetoothRequest, BluetoothResponse,
18 BluetoothServiceMsg,
19};
20
21use crate::conversions::Convert;
22use crate::dom::bindings::codegen::Bindings::BluetoothDeviceBinding::BluetoothDeviceMethods;
23use crate::dom::bindings::codegen::Bindings::BluetoothRemoteGATTServerBinding::BluetoothRemoteGATTServerMethods;
24use crate::dom::bindings::error::{Error, ErrorResult};
25use crate::dom::bindings::inheritance::Castable;
26use crate::dom::bindings::reflector::DomGlobal;
27use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
28use crate::dom::bindings::str::DOMString;
29use crate::dom::bluetooth::{AsyncBluetoothListener, Bluetooth, response_async};
30use crate::dom::bluetoothcharacteristicproperties::BluetoothCharacteristicProperties;
31use crate::dom::bluetoothremotegattcharacteristic::BluetoothRemoteGATTCharacteristic;
32use crate::dom::bluetoothremotegattdescriptor::BluetoothRemoteGATTDescriptor;
33use crate::dom::bluetoothremotegattserver::BluetoothRemoteGATTServer;
34use crate::dom::bluetoothremotegattservice::BluetoothRemoteGATTService;
35use crate::dom::eventtarget::EventTarget;
36use crate::dom::globalscope::GlobalScope;
37use crate::dom::promise::Promise;
38
39#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
40#[derive(JSTraceable, MallocSizeOf)]
41struct AttributeInstanceMap {
42 service_map: DomRefCell<HashMap<String, Dom<BluetoothRemoteGATTService>>>,
43 characteristic_map: DomRefCell<HashMap<String, Dom<BluetoothRemoteGATTCharacteristic>>>,
44 descriptor_map: DomRefCell<HashMap<String, Dom<BluetoothRemoteGATTDescriptor>>>,
45}
46
47#[dom_struct]
49pub(crate) struct BluetoothDevice {
50 eventtarget: EventTarget,
51 id: DOMString,
52 name: Option<DOMString>,
53 gatt: MutNullableDom<BluetoothRemoteGATTServer>,
54 context: Dom<Bluetooth>,
55 attribute_instance_map: AttributeInstanceMap,
56 watching_advertisements: Cell<bool>,
57}
58
59impl BluetoothDevice {
60 pub(crate) fn new_inherited(
61 id: DOMString,
62 name: Option<DOMString>,
63 context: &Bluetooth,
64 ) -> BluetoothDevice {
65 BluetoothDevice {
66 eventtarget: EventTarget::new_inherited(),
67 id,
68 name,
69 gatt: Default::default(),
70 context: Dom::from_ref(context),
71 attribute_instance_map: AttributeInstanceMap {
72 service_map: DomRefCell::new(HashMap::new()),
73 characteristic_map: DomRefCell::new(HashMap::new()),
74 descriptor_map: DomRefCell::new(HashMap::new()),
75 },
76 watching_advertisements: Cell::new(false),
77 }
78 }
79
80 pub(crate) fn new(
81 cx: &mut JSContext,
82 global: &GlobalScope,
83 id: DOMString,
84 name: Option<DOMString>,
85 context: &Bluetooth,
86 ) -> DomRoot<BluetoothDevice> {
87 reflect_dom_object_with_cx(
88 Box::new(BluetoothDevice::new_inherited(id, name, context)),
89 global,
90 cx,
91 )
92 }
93
94 pub(crate) fn get_gatt(&self, cx: &mut JSContext) -> DomRoot<BluetoothRemoteGATTServer> {
95 self.gatt
96 .or_init(|| BluetoothRemoteGATTServer::new(cx, &self.global(), self))
97 }
98
99 fn get_context(&self) -> DomRoot<Bluetooth> {
100 DomRoot::from_ref(&self.context)
101 }
102
103 pub(crate) fn get_or_create_service(
104 &self,
105 cx: &mut JSContext,
106 service: &BluetoothServiceMsg,
107 server: &BluetoothRemoteGATTServer,
108 ) -> DomRoot<BluetoothRemoteGATTService> {
109 let service_map_ref = &self.attribute_instance_map.service_map;
110 {
111 let service_map = service_map_ref.borrow();
112 if let Some(existing_service) = service_map.get(&service.instance_id) {
113 return DomRoot::from_ref(existing_service);
114 }
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_ref
125 .safe_borrow_mut(cx.no_gc())
126 .insert(service.instance_id.clone(), Dom::from_ref(&bt_service));
127 bt_service
128 }
129
130 pub(crate) fn get_or_create_characteristic(
131 &self,
132 cx: &mut JSContext,
133 characteristic: &BluetoothCharacteristicMsg,
134 service: &BluetoothRemoteGATTService,
135 ) -> DomRoot<BluetoothRemoteGATTCharacteristic> {
136 let characteristic_map_ref = &self.attribute_instance_map.characteristic_map;
137 {
138 let characteristic_map = characteristic_map_ref.borrow();
139 if let Some(existing_characteristic) =
140 characteristic_map.get(&characteristic.instance_id)
141 {
142 return DomRoot::from_ref(existing_characteristic);
143 }
144 }
145 let properties = BluetoothCharacteristicProperties::new(
146 cx,
147 &service.global(),
148 characteristic.broadcast,
149 characteristic.read,
150 characteristic.write_without_response,
151 characteristic.write,
152 characteristic.notify,
153 characteristic.indicate,
154 characteristic.authenticated_signed_writes,
155 characteristic.reliable_write,
156 characteristic.writable_auxiliaries,
157 );
158 let bt_characteristic = BluetoothRemoteGATTCharacteristic::new(
159 cx,
160 &service.global(),
161 service,
162 DOMString::from(characteristic.uuid.clone()),
163 &properties,
164 characteristic.instance_id.clone(),
165 );
166 characteristic_map_ref.safe_borrow_mut(cx.no_gc()).insert(
167 characteristic.instance_id.clone(),
168 Dom::from_ref(&bt_characteristic),
169 );
170 bt_characteristic
171 }
172
173 pub(crate) fn is_represented_device_null(&self) -> bool {
174 let (sender, receiver) =
175 generic_channel::channel(self.global().time_profiler_chan().clone()).unwrap();
176 self.get_bluetooth_thread()
177 .send(BluetoothRequest::IsRepresentedDeviceNull(
178 String::from(self.Id()),
179 sender,
180 ))
181 .unwrap();
182 receiver.recv().unwrap()
183 }
184
185 pub(crate) fn get_or_create_descriptor(
186 &self,
187 cx: &mut JSContext,
188 descriptor: &BluetoothDescriptorMsg,
189 characteristic: &BluetoothRemoteGATTCharacteristic,
190 ) -> DomRoot<BluetoothRemoteGATTDescriptor> {
191 let descriptor_map_ref = &self.attribute_instance_map.descriptor_map;
192 {
193 let descriptor_map = descriptor_map_ref.borrow();
194 if let Some(existing_descriptor) = descriptor_map.get(&descriptor.instance_id) {
195 return DomRoot::from_ref(existing_descriptor);
196 }
197 }
198 let bt_descriptor = BluetoothRemoteGATTDescriptor::new(
199 cx,
200 &characteristic.global(),
201 characteristic,
202 DOMString::from(descriptor.uuid.clone()),
203 descriptor.instance_id.clone(),
204 );
205 descriptor_map_ref.safe_borrow_mut(cx.no_gc()).insert(
206 descriptor.instance_id.clone(),
207 Dom::from_ref(&bt_descriptor),
208 );
209 bt_descriptor
210 }
211
212 fn get_bluetooth_thread(&self) -> GenericSender<BluetoothRequest> {
213 self.global().as_window().bluetooth_thread()
214 }
215
216 pub(crate) fn clean_up_disconnected_device(&self, cx: &mut JSContext) {
218 self.get_gatt(cx).set_connected(false);
220
221 let service_ids = {
228 let mut service_map = self
229 .attribute_instance_map
230 .service_map
231 .safe_borrow_mut(cx.no_gc());
232 service_map.drain().map(|(id, _)| id).collect()
233 };
234
235 let characteristic_ids = {
236 let mut characteristic_map = self
237 .attribute_instance_map
238 .characteristic_map
239 .safe_borrow_mut(cx.no_gc());
240 characteristic_map.drain().map(|(id, _)| id).collect()
241 };
242
243 let descriptor_ids = {
244 let mut descriptor_map = self
245 .attribute_instance_map
246 .descriptor_map
247 .safe_borrow_mut(cx.no_gc());
248 descriptor_map.drain().map(|(id, _)| id).collect()
249 };
250
251 let _ = self
254 .get_bluetooth_thread()
255 .send(BluetoothRequest::SetRepresentedToNull(
256 service_ids,
257 characteristic_ids,
258 descriptor_ids,
259 ));
260
261 self.upcast::<EventTarget>()
263 .fire_bubbling_event(cx, atom!("gattserverdisconnected"));
264 }
265
266 pub(crate) fn garbage_collect_the_connection(&self, cx: &mut JSContext) -> ErrorResult {
268 let context = self.get_context();
272 for (id, device) in context.get_device_map().borrow().iter() {
273 if id == &self.Id().str() as &str && device.get_gatt(cx).Connected() {
275 return Ok(());
276 }
277 }
278
279 let (sender, receiver) =
281 generic_channel::channel(self.global().time_profiler_chan().clone()).unwrap();
282 self.get_bluetooth_thread()
283 .send(BluetoothRequest::GATTServerDisconnect(
284 String::from(self.Id()),
285 sender,
286 ))
287 .unwrap();
288 receiver.recv().unwrap().map_err(Convert::convert)
289 }
290}
291
292impl BluetoothDeviceMethods<crate::DomTypeHolder> for BluetoothDevice {
293 fn Id(&self) -> DOMString {
295 self.id.clone()
296 }
297
298 fn GetName(&self) -> Option<DOMString> {
300 self.name.clone()
301 }
302
303 fn GetGatt(&self, cx: &mut JSContext) -> Option<DomRoot<BluetoothRemoteGATTServer>> {
305 if self
307 .global()
308 .as_window()
309 .bluetooth_extra_permission_data()
310 .allowed_devices_contains_id(self.id.clone()) &&
311 !self.is_represented_device_null()
312 {
313 return Some(self.get_gatt(cx));
314 }
315 None
317 }
318
319 fn WatchAdvertisements(&self, cx: &mut CurrentRealm) -> Rc<Promise> {
321 let p = Promise::new_in_realm(cx);
322 let sender = response_async(&p, self);
323 self.get_bluetooth_thread()
327 .send(BluetoothRequest::WatchAdvertisements(
328 String::from(self.Id()),
329 sender,
330 ))
331 .unwrap();
332 p
333 }
334
335 fn UnwatchAdvertisements(&self) {
337 self.watching_advertisements.set(false)
339 }
341
342 fn WatchingAdvertisements(&self) -> bool {
344 self.watching_advertisements.get()
345 }
346
347 event_handler!(
349 gattserverdisconnected,
350 GetOngattserverdisconnected,
351 SetOngattserverdisconnected
352 );
353}
354
355impl AsyncBluetoothListener for BluetoothDevice {
356 fn handle_response(
357 &self,
358 cx: &mut JSContext,
359 response: BluetoothResponse,
360 promise: &Rc<Promise>,
361 ) {
362 match response {
363 BluetoothResponse::WatchAdvertisements(_result) => {
365 self.watching_advertisements.set(true);
367 promise.resolve_native(cx, &());
369 },
370 _ => promise.reject_error(cx, Error::Type(c"Something went wrong...".to_owned())),
371 }
372 }
373}