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 let mut service_map = service_map_ref.borrow_mut();
111 if let Some(existing_service) = service_map.get(&service.instance_id) {
112 return DomRoot::from_ref(existing_service);
113 }
114 let bt_service = BluetoothRemoteGATTService::new(
115 cx,
116 &server.global(),
117 &server.Device(),
118 DOMString::from(service.uuid.clone()),
119 service.is_primary,
120 service.instance_id.clone(),
121 );
122 service_map.insert(service.instance_id.clone(), Dom::from_ref(&bt_service));
123 bt_service
124 }
125
126 pub(crate) fn get_or_create_characteristic(
127 &self,
128 cx: &mut JSContext,
129 characteristic: &BluetoothCharacteristicMsg,
130 service: &BluetoothRemoteGATTService,
131 ) -> DomRoot<BluetoothRemoteGATTCharacteristic> {
132 let characteristic_map_ref = &self.attribute_instance_map.characteristic_map;
133 let mut characteristic_map = characteristic_map_ref.borrow_mut();
134 if let Some(existing_characteristic) = characteristic_map.get(&characteristic.instance_id) {
135 return DomRoot::from_ref(existing_characteristic);
136 }
137 let properties = BluetoothCharacteristicProperties::new(
138 cx,
139 &service.global(),
140 characteristic.broadcast,
141 characteristic.read,
142 characteristic.write_without_response,
143 characteristic.write,
144 characteristic.notify,
145 characteristic.indicate,
146 characteristic.authenticated_signed_writes,
147 characteristic.reliable_write,
148 characteristic.writable_auxiliaries,
149 );
150 let bt_characteristic = BluetoothRemoteGATTCharacteristic::new(
151 cx,
152 &service.global(),
153 service,
154 DOMString::from(characteristic.uuid.clone()),
155 &properties,
156 characteristic.instance_id.clone(),
157 );
158 characteristic_map.insert(
159 characteristic.instance_id.clone(),
160 Dom::from_ref(&bt_characteristic),
161 );
162 bt_characteristic
163 }
164
165 pub(crate) fn is_represented_device_null(&self) -> bool {
166 let (sender, receiver) =
167 generic_channel::channel(self.global().time_profiler_chan().clone()).unwrap();
168 self.get_bluetooth_thread()
169 .send(BluetoothRequest::IsRepresentedDeviceNull(
170 String::from(self.Id()),
171 sender,
172 ))
173 .unwrap();
174 receiver.recv().unwrap()
175 }
176
177 pub(crate) fn get_or_create_descriptor(
178 &self,
179 cx: &mut JSContext,
180 descriptor: &BluetoothDescriptorMsg,
181 characteristic: &BluetoothRemoteGATTCharacteristic,
182 ) -> DomRoot<BluetoothRemoteGATTDescriptor> {
183 let descriptor_map_ref = &self.attribute_instance_map.descriptor_map;
184 let mut descriptor_map = descriptor_map_ref.borrow_mut();
185 if let Some(existing_descriptor) = descriptor_map.get(&descriptor.instance_id) {
186 return DomRoot::from_ref(existing_descriptor);
187 }
188 let bt_descriptor = BluetoothRemoteGATTDescriptor::new(
189 cx,
190 &characteristic.global(),
191 characteristic,
192 DOMString::from(descriptor.uuid.clone()),
193 descriptor.instance_id.clone(),
194 );
195 descriptor_map.insert(
196 descriptor.instance_id.clone(),
197 Dom::from_ref(&bt_descriptor),
198 );
199 bt_descriptor
200 }
201
202 fn get_bluetooth_thread(&self) -> GenericSender<BluetoothRequest> {
203 self.global().as_window().bluetooth_thread()
204 }
205
206 pub(crate) fn clean_up_disconnected_device(&self, cx: &mut JSContext) {
208 self.get_gatt(cx).set_connected(false);
210
211 let mut service_map = self.attribute_instance_map.service_map.borrow_mut();
218 let service_ids = service_map.drain().map(|(id, _)| id).collect();
219
220 let mut characteristic_map = self.attribute_instance_map.characteristic_map.borrow_mut();
221 let characteristic_ids = characteristic_map.drain().map(|(id, _)| id).collect();
222
223 let mut descriptor_map = self.attribute_instance_map.descriptor_map.borrow_mut();
224 let descriptor_ids = descriptor_map.drain().map(|(id, _)| id).collect();
225
226 let _ = self
229 .get_bluetooth_thread()
230 .send(BluetoothRequest::SetRepresentedToNull(
231 service_ids,
232 characteristic_ids,
233 descriptor_ids,
234 ));
235
236 self.upcast::<EventTarget>()
238 .fire_bubbling_event(cx, atom!("gattserverdisconnected"));
239 }
240
241 pub(crate) fn garbage_collect_the_connection(&self, cx: &mut JSContext) -> ErrorResult {
243 let context = self.get_context();
247 for (id, device) in context.get_device_map().borrow().iter() {
248 if id == &self.Id().str() as &str && device.get_gatt(cx).Connected() {
250 return Ok(());
251 }
252 }
253
254 let (sender, receiver) =
256 generic_channel::channel(self.global().time_profiler_chan().clone()).unwrap();
257 self.get_bluetooth_thread()
258 .send(BluetoothRequest::GATTServerDisconnect(
259 String::from(self.Id()),
260 sender,
261 ))
262 .unwrap();
263 receiver.recv().unwrap().map_err(Convert::convert)
264 }
265}
266
267impl BluetoothDeviceMethods<crate::DomTypeHolder> for BluetoothDevice {
268 fn Id(&self) -> DOMString {
270 self.id.clone()
271 }
272
273 fn GetName(&self) -> Option<DOMString> {
275 self.name.clone()
276 }
277
278 fn GetGatt(&self, cx: &mut JSContext) -> Option<DomRoot<BluetoothRemoteGATTServer>> {
280 if self
282 .global()
283 .as_window()
284 .bluetooth_extra_permission_data()
285 .allowed_devices_contains_id(self.id.clone()) &&
286 !self.is_represented_device_null()
287 {
288 return Some(self.get_gatt(cx));
289 }
290 None
292 }
293
294 fn WatchAdvertisements(&self, cx: &mut CurrentRealm) -> Rc<Promise> {
296 let p = Promise::new_in_realm(cx);
297 let sender = response_async(&p, self);
298 self.get_bluetooth_thread()
302 .send(BluetoothRequest::WatchAdvertisements(
303 String::from(self.Id()),
304 sender,
305 ))
306 .unwrap();
307 p
308 }
309
310 fn UnwatchAdvertisements(&self) {
312 self.watching_advertisements.set(false)
314 }
316
317 fn WatchingAdvertisements(&self) -> bool {
319 self.watching_advertisements.get()
320 }
321
322 event_handler!(
324 gattserverdisconnected,
325 GetOngattserverdisconnected,
326 SetOngattserverdisconnected
327 );
328}
329
330impl AsyncBluetoothListener for BluetoothDevice {
331 fn handle_response(
332 &self,
333 cx: &mut JSContext,
334 response: BluetoothResponse,
335 promise: &Rc<Promise>,
336 ) {
337 match response {
338 BluetoothResponse::WatchAdvertisements(_result) => {
340 self.watching_advertisements.set(true);
342 promise.resolve_native_with_cx(cx, &());
344 },
345 _ => {
346 promise.reject_error_with_cx(cx, Error::Type(c"Something went wrong...".to_owned()))
347 },
348 }
349 }
350}