1use std::sync::{Arc, OnceLock};
2
3use futures_core::Stream;
4use futures_lite::StreamExt;
5use java_spaghetti::Global;
6use log::info;
7use uuid::Uuid;
8
9use super::bindings::android::bluetooth::BluetoothDevice;
10use super::error::ErrorKind;
11use super::event_receiver::GlobalEvent;
12use super::gatt_tree::{CachedWeak, GattConnection, GattTree};
13use super::jni::Monitor;
14use super::service::Service;
15use super::util::{BoolExt, OptionExt};
16use super::vm_context::{android_api_level, jni_with_env};
17use super::{DeviceId, Result};
18
19#[derive(Clone)]
21pub struct Device {
22 pub(super) id: DeviceId,
23 pub(super) device: Global<BluetoothDevice>,
24 pub(super) connection: CachedWeak<GattConnection>,
25 pub(super) once_connected: Arc<OnceLock<()>>,
26}
27
28impl PartialEq for Device {
29 fn eq(&self, other: &Self) -> bool {
30 self.id() == other.id()
31 }
32}
33
34impl Eq for Device {}
35
36impl std::hash::Hash for Device {
37 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
38 self.id().hash(state);
39 }
40}
41
42impl std::fmt::Debug for Device {
43 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44 let mut f = f.debug_struct("Device");
45 f.field("name", &self.name().unwrap_or("(Unknown name)".into()));
46 f.field("id", &self.id());
47 f.finish()
48 }
49}
50
51impl std::fmt::Display for Device {
52 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
53 f.write_str(self.name().as_deref().unwrap_or("(Unknown name)"))
54 }
55}
56
57impl Device {
58 pub fn id(&self) -> DeviceId {
60 self.id.clone()
61 }
62
63 pub fn name(&self) -> Result<String> {
65 jni_with_env(|env| {
66 self.device
67 .as_ref(env)
68 .getName()
69 .map_err(|e| e.into())
70 .and_then(|s| s.non_null())
71 .map(|s| s.to_string_lossy())
72 })
73 }
74
75 pub async fn name_async(&self) -> Result<String> {
77 self.name()
78 }
79
80 pub async fn is_connected(&self) -> bool {
84 self.get_connection().is_ok()
85 }
86
87 pub async fn is_paired(&self) -> Result<bool> {
89 jni_with_env(|env| {
90 self.device
91 .as_ref(env)
92 .getBondState()
93 .map_err(|e| e.into())
94 .map(|i| i == BluetoothDevice::BOND_BONDED)
95 })
96 }
97
98 pub async fn pair(&self) -> Result<()> {
100 let conn = self.get_connection()?;
101 let mut receiver = self
102 .get_connection()?
103 .global_event_receiver
104 .subscribe()
105 .await?;
106
107 let bond_state = jni_with_env(|env| {
108 let device = self.device.as_ref(env);
109 device.getBondState().map_err(crate::Error::from)
110 })?;
111 match bond_state {
112 BluetoothDevice::BOND_BONDED => return Ok(()),
113 BluetoothDevice::BOND_BONDING => (),
114 _ => {
115 jni_with_env(|env| {
116 let device = self.device.as_ref(env);
117 let gatt = conn.gatt.as_ref(env);
118 let _lock = Monitor::new(&gatt);
119 device.createBond()?.non_false()?;
120 Ok::<_, crate::Error>(())
121 })?;
122 }
123 }
124 drop(conn);
125
126 while let Some(event) = receiver.next().await {
128 match event {
129 GlobalEvent::BondStateChanged(dev_id, prev_st, st) if dev_id == self.id => match st
130 {
131 BluetoothDevice::BOND_BONDED => return Ok(()),
132 BluetoothDevice::BOND_NONE => {
133 if prev_st == BluetoothDevice::BOND_BONDING {
134 return Err(crate::Error::new(
135 ErrorKind::NotAuthorized,
136 None,
137 "pairing process failed",
138 ));
139 } else if prev_st == BluetoothDevice::BOND_BONDED {
140 info!("deregistered connection with {dev_id} in Device::pair");
141 GattTree::deregister_connection(&dev_id);
142 return Err(ErrorKind::NotConnected.into());
143 }
144 }
145 _ => (),
146 },
147 _ => (),
148 }
149 }
150 Err(ErrorKind::NotConnected.into())
151 }
152
153 pub async fn discover_services(&self) -> Result<Vec<Service>> {
155 let conn = self.get_connection()?;
156 let disc_lock = conn.discover_services.lock().await;
157 jni_with_env(|env| {
158 let gatt = conn.gatt.as_ref(env);
159 let gatt = Monitor::new(&gatt);
160 gatt.discoverServices()?.non_false()?;
161 Ok::<_, crate::Error>(())
162 })?;
163 drop(conn);
164 disc_lock.wait_unlock().await.ok_or_check_conn(&self.id)??;
165 self.collect_discovered_services()
166 }
167
168 pub async fn discover_services_with_uuid(&self, uuid: Uuid) -> Result<Vec<Service>> {
170 Ok(self
171 .discover_services()
172 .await?
173 .into_iter()
174 .filter(|serv| serv.uuid() == uuid)
175 .collect())
176 }
177
178 pub async fn services(&self) -> Result<Vec<Service>> {
182 let conn = self.get_connection()?;
183 if conn.discover_services.last_value().is_some() {
184 self.collect_discovered_services()
185 } else {
186 self.discover_services().await
187 }
188 }
189
190 fn collect_discovered_services(&self) -> Result<Vec<Service>> {
191 Ok(self
192 .get_connection()?
193 .services
194 .lock()
195 .unwrap()
196 .keys()
197 .map(|&service_id| Service::new(self.id.clone(), service_id))
198 .collect())
199 }
200
201 pub async fn service_changed_indications(
205 &self,
206 ) -> Result<impl Stream<Item = Result<ServicesChanged>> + Send + Unpin + '_> {
207 if android_api_level() < 31 {
208 return Err(crate::Error::new(
209 ErrorKind::NotSupported,
210 None,
211 "this requires BluetoothGattCallback.onServiceChanged() introduced in API level 31",
212 ));
213 }
214 let receiver = self
215 .get_connection()?
216 .services_changes
217 .subscribe(|| Ok::<_, crate::Error>(()), || ())
218 .await?;
219 Ok(receiver.map(|_| {
220 Ok(ServicesChanged {
221 dev_id: self.id.clone(),
222 })
223 }))
224 }
225
226 pub async fn rssi(&self) -> Result<i16> {
228 let conn = self.get_connection()?;
229 let read_rssi_lock = conn.read_rssi.lock().await;
230 jni_with_env(|env| {
231 let gatt = conn.gatt.as_ref(env);
232 let gatt = Monitor::new(&gatt);
233 gatt.readRemoteRssi()?.non_false()?;
234 Ok::<_, crate::Error>(())
235 })?;
236 drop(conn);
237 read_rssi_lock
238 .wait_unlock()
239 .await
240 .ok_or_check_conn(&self.id)?
241 }
242
243 pub async fn open_l2cap_channel(
247 &self,
248 psm: u16,
249 secure: bool,
250 ) -> Result<super::l2cap_channel::L2capChannel> {
251 use log::warn;
252 if self.get_connection().is_ok() {
253 warn!("trying to open L2CAP channel while there is a GATT connection.");
254 }
255 let (reader, writer) =
256 super::l2cap_channel::open_l2cap_channel(self.device.clone(), psm, secure)?;
257 Ok(super::l2cap_channel::L2capChannel { reader, writer })
258 }
259
260 pub(crate) fn get_connection(&self) -> Result<Arc<GattConnection>, crate::Error> {
261 self.connection
262 .get_or_find(|| GattTree::check_connection(&self.id))
263 }
264}
265
266#[derive(Debug, Clone, PartialEq, Eq, Hash)]
268pub struct ServicesChanged {
269 dev_id: DeviceId, }
271
272impl ServicesChanged {
273 pub fn was_invalidated(&self, service: &Service) -> bool {
275 GattTree::find_service(&self.dev_id, service.uuid()).is_none()
276 }
277}