1# Copyright 2023 Google LLC 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# https://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14"""All functions relative to the bluetooth procedure.""" 15 16import logging 17import threading 18import traceback 19 20from floss.pandora.floss import adapter_client 21from floss.pandora.floss import advertising_client 22from floss.pandora.floss import floss_enums 23from floss.pandora.floss import gatt_client 24from floss.pandora.floss import gatt_server 25from floss.pandora.floss import manager_client 26from floss.pandora.floss import media_client 27from floss.pandora.floss import qa_client 28from floss.pandora.floss import scanner_client 29from floss.pandora.floss import socket_manager 30from floss.pandora.floss import telephony_client 31from floss.pandora.floss import utils 32from gi.repository import GLib 33import pydbus 34 35 36class Bluetooth(object): 37 """A bluetooth facade exposes all bluetooth functions.""" 38 39 # Default to this adapter during init. We will initialize to the correct 40 # default adapter after the manager client is initialized. 41 DEFAULT_ADAPTER = 0 42 43 # Time to sleep between polls 44 ADAPTER_CLIENT_POLL_INTERVAL = 0.1 45 46 # How long we wait for the adapter to come up after we start it. 47 ADAPTER_DAEMON_TIMEOUT_SEC = 20 48 49 # How long we wait for the manager daemon to come up after we start it. 50 DAEMON_TIMEOUT_SEC = 5 51 52 # Default scanner settings 53 SCANNER_INTERVAL = 0 54 SCANNER_WINDOW = 0 55 SCANNER_SCAN_TYPE = 0 56 57 FAKE_GATT_APP_ID = '12345678123456781234567812345678' 58 59 def __init__(self): 60 self.setup_mainloop() 61 62 # self state 63 self.is_clean = False 64 65 # DBUS clients 66 self.manager_client = manager_client.FlossManagerClient(self.bus) 67 self.adapter_client = adapter_client.FlossAdapterClient(self.bus, self.DEFAULT_ADAPTER) 68 self.advertising_client = advertising_client.FlossAdvertisingClient(self.bus, self.DEFAULT_ADAPTER) 69 self.scanner_client = scanner_client.FlossScannerClient(self.bus, self.DEFAULT_ADAPTER) 70 self.qa_client = qa_client.FlossQAClient(self.bus, self.DEFAULT_ADAPTER) 71 self.media_client = media_client.FlossMediaClient(self.bus, self.DEFAULT_ADAPTER) 72 self.gatt_client = gatt_client.FlossGattClient(self.bus, self.DEFAULT_ADAPTER) 73 self.gatt_server = gatt_server.FlossGattServer(self.bus, self.DEFAULT_ADAPTER) 74 self.socket_manager = socket_manager.FlossSocketManagerClient(self.bus, self.DEFAULT_ADAPTER) 75 self.telephony_client = telephony_client.FlossTelephonyClient(self.bus, self.DEFAULT_ADAPTER) 76 77 def __del__(self): 78 if not self.is_clean: 79 self.cleanup() 80 81 def cleanup(self): 82 self.mainloop_quit.set() 83 self.mainloop.quit() 84 self.is_clean = True 85 86 def setup_mainloop(self): 87 """Start mainloop thread in background. 88 89 This will also initialize a few 90 other variables (self.bus, self.mainloop, self.event_context) that may 91 be necessary for proper operation. 92 93 Raises: 94 RuntimeError: if we timeout to wait for the mainloop ready. 95 """ 96 97 self.mainloop_quit = threading.Event() 98 self.mainloop_ready = threading.Event() 99 self.thread = threading.Thread(name=utils.GLIB_THREAD_NAME, target=Bluetooth.mainloop_thread, args=(self,)) 100 self.thread.start() 101 102 # Wait for mainloop to be ready 103 if not self.mainloop_ready.wait(timeout=5): 104 raise RuntimeError('Unable to initialize GLib mainloop') 105 106 def mainloop_thread(self): 107 # Set up mainloop. All subsequent buses and connections will use this 108 # mainloop. We also use a separate main context to avoid multithreading 109 # issues. 110 GLib.threads_init() 111 self.mainloop = GLib.MainLoop() 112 113 # Set up bus connection 114 self.bus = pydbus.SystemBus() 115 116 # Set thread ready 117 self.mainloop_ready.set() 118 119 while not self.mainloop_quit.is_set(): 120 self.mainloop.run() 121 122 def register_clients_callback(self): 123 """Registers callback for all interfaces. 124 125 Returns: 126 True on success, False otherwise. 127 """ 128 if not self.adapter_client.register_callbacks(): 129 logging.error('adapter_client: Failed to register callbacks') 130 return False 131 if not self.advertising_client.register_advertiser_callback(): 132 logging.error('advertising_client: Failed to register advertiser callbacks') 133 return False 134 if not self.scanner_client.register_scanner_callback(): 135 logging.error('scanner_client: Failed to register callbacks') 136 return False 137 if not self.qa_client.register_qa_callback(): 138 logging.error('qa_client: Failed to register callbacks') 139 return False 140 if not self.media_client.register_callback(): 141 logging.error('media_client: Failed to register callbacks') 142 return False 143 if not self.gatt_client.register_client(self.FAKE_GATT_APP_ID, False): 144 logging.error('gatt_client: Failed to register callbacks') 145 return False 146 if not self.gatt_server.register_server(self.FAKE_GATT_APP_ID, False): 147 logging.error('gatt_server: Failed to register callbacks') 148 return False 149 if not self.socket_manager.register_callbacks(): 150 logging.error('scanner_client: Failed to register callbacks') 151 return False 152 if not self.telephony_client.register_telephony_callback(): 153 logging.error('telephony_client: Failed to register callbacks') 154 return False 155 return True 156 157 def is_bluetoothd_proxy_valid(self): 158 """Checks whether the proxy objects for Floss are ok and registers client callbacks.""" 159 160 proxy_ready = all([ 161 self.manager_client.has_proxy(), 162 self.adapter_client.has_proxy(), 163 self.advertising_client.has_proxy(), 164 self.scanner_client.has_proxy(), 165 self.qa_client.has_proxy(), 166 self.media_client.has_proxy(), 167 self.gatt_client.has_proxy(), 168 self.gatt_server.has_proxy(), 169 self.socket_manager.has_proxy(), 170 self.telephony_client.has_proxy() 171 ]) 172 173 if not proxy_ready: 174 logging.info('Some proxy has not yet ready.') 175 return False 176 177 return self.register_clients_callback() 178 179 def set_powered(self, powered: bool): 180 """Set the power of bluetooth adapter and bluetooth clients. 181 182 Args: 183 powered: Power on or power off. 184 185 Returns: 186 bool: True if success, False otherwise. 187 """ 188 default_adapter = self.manager_client.get_default_adapter() 189 190 def _is_adapter_down(client): 191 return lambda: not client.has_proxy() 192 193 if powered: 194 # FIXME: Close rootcanal will cause manager_client failed call has_default_adapter. 195 # if not self.manager_client.has_default_adapter(): 196 # logging.warning('set_powered: Default adapter not available.') 197 # return False 198 self.manager_client.start(default_adapter) 199 200 self.adapter_client = adapter_client.FlossAdapterClient(self.bus, default_adapter) 201 self.advertising_client = advertising_client.FlossAdvertisingClient(self.bus, default_adapter) 202 self.scanner_client = scanner_client.FlossScannerClient(self.bus, default_adapter) 203 self.qa_client = qa_client.FlossQAClient(self.bus, default_adapter) 204 self.media_client = media_client.FlossMediaClient(self.bus, default_adapter) 205 self.gatt_client = gatt_client.FlossGattClient(self.bus, default_adapter) 206 self.gatt_server = gatt_server.FlossGattServer(self.bus, default_adapter) 207 self.socket_manager = socket_manager.FlossSocketManagerClient(self.bus, default_adapter) 208 self.telephony_client = telephony_client.FlossTelephonyClient(self.bus, default_adapter) 209 210 try: 211 utils.poll_for_condition( 212 condition=lambda: self.is_bluetoothd_proxy_valid() and self.adapter_client.get_address(), 213 desc='Wait for adapter start', 214 sleep_interval=self.ADAPTER_CLIENT_POLL_INTERVAL, 215 timeout=self.ADAPTER_DAEMON_TIMEOUT_SEC) 216 except TimeoutError as e: 217 logging.error('timeout: error starting adapter daemon: %s', e) 218 logging.error(traceback.format_exc()) 219 return False 220 else: 221 self.manager_client.stop(default_adapter) 222 try: 223 utils.poll_for_condition(condition=_is_adapter_down(self.adapter_client), 224 desc='Wait for adapter stop', 225 sleep_interval=self.ADAPTER_CLIENT_POLL_INTERVAL, 226 timeout=self.ADAPTER_DAEMON_TIMEOUT_SEC) 227 except TimeoutError as e: 228 logging.error('timeout: error stopping adapter daemon: %s', e) 229 logging.error(traceback.format_exc()) 230 return False 231 return True 232 233 def reset(self): 234 if not self.set_powered(False): 235 return False 236 237 if not self.set_powered(True): 238 return False 239 return True 240 241 def get_address(self): 242 return self.adapter_client.get_address() 243 244 def get_remote_type(self, address): 245 return self.adapter_client.get_remote_property(address, 'Type') 246 247 def is_connected(self, address): 248 return self.adapter_client.is_connected(address) 249 250 def is_bonded(self, address): 251 return self.adapter_client.is_bonded(address) 252 253 def is_discovering(self): 254 return self.adapter_client.is_discovering() 255 256 def set_discoverable(self, mode, duration=60): 257 return self.adapter_client.set_property('Discoverable', mode, duration) 258 259 def create_bond(self, address, transport): 260 return self.adapter_client.create_bond(address, transport) 261 262 def remove_bond(self, address): 263 return self.adapter_client.remove_bond(address) 264 265 def get_bonded_devices(self): 266 return self.adapter_client.get_bonded_devices() 267 268 def forget_device(self, address): 269 return self.adapter_client.forget_device(address) 270 271 def set_pin(self, address, accept, pin_code): 272 return self.adapter_client.set_pin(address, accept, pin_code) 273 274 def set_pairing_confirmation(self, address, accept): 275 return self.adapter_client.set_pairing_confirmation(address, accept) 276 277 def connect_device(self, address): 278 return self.adapter_client.connect_all_enabled_profiles(address) 279 280 def disconnect_device(self, address): 281 return self.adapter_client.disconnect_all_enabled_profiles(address) 282 283 def start_discovery(self): 284 if self.adapter_client.is_discovering(): 285 logging.warning('Adapter is already discovering.') 286 return True 287 return self.adapter_client.start_discovery() 288 289 def stop_discovery(self): 290 if not self.adapter_client.is_discovering(): 291 logging.warning('Discovery is already stopped.') 292 return True 293 return self.adapter_client.stop_discovery() 294 295 def start_advertising_set(self, parameters, advertise_data, scan_response, periodic_parameters, periodic_data, 296 duration, max_ext_adv_events): 297 parameters = self.advertising_client.make_dbus_advertising_set_parameters(parameters) 298 advertise_data = self.advertising_client.make_dbus_advertise_data(advertise_data) 299 scan_response = utils.make_kv_optional_value(self.advertising_client.make_dbus_advertise_data(scan_response)) 300 periodic_parameters = utils.make_kv_optional_value( 301 self.advertising_client.make_dbus_periodic_advertising_parameters(periodic_parameters)) 302 periodic_data = utils.make_kv_optional_value(self.advertising_client.make_dbus_advertise_data(periodic_data)) 303 304 return self.advertising_client.start_advertising_set(parameters, advertise_data, scan_response, 305 periodic_parameters, periodic_data, duration, 306 max_ext_adv_events) 307 308 def stop_advertising_set(self, advertiser_id): 309 return self.advertising_client.stop_advertising_set(advertiser_id) 310 311 def register_scanner(self): 312 return self.scanner_client.register_scanner() 313 314 def start_scan(self, scanner_id, settings=None, scan_filter=None): 315 if settings is None: 316 settings = self.scanner_client.make_dbus_scan_settings(self.SCANNER_INTERVAL, self.SCANNER_WINDOW, 317 self.SCANNER_SCAN_TYPE) 318 return self.scanner_client.start_scan(scanner_id, settings, scan_filter) 319 320 def stop_scan(self, scanner_id): 321 if not self.scanner_client.remove_monitor(scanner_id): 322 logging.error('Failed to stop scanning.') 323 return False 324 return True 325 326 def set_hid_report(self, addr, report_type, report): 327 return self.qa_client.set_hid_report(addr, report_type, report) 328 329 def read_characteristic(self, address, handle, auth_re): 330 return self.gatt_client.read_characteristic(address, handle, auth_re) 331 332 def read_descriptor(self, address, handle, auth_req): 333 return self.gatt_client.read_descriptor(address, handle, auth_req) 334 335 def discover_services(self, address): 336 return self.gatt_client.discover_services(address) 337 338 def get_bond_state(self, address): 339 self.adapter_client.get_bond_state(address) 340 341 def fetch_remote(self, address): 342 return self.adapter_client.fetch_remote_uuids(address) 343 344 def get_remote_uuids(self, address): 345 return self.adapter_client.get_remote_property(address, 'Uuids') 346 347 def btif_gattc_discover_service_by_uuid(self, address, uuid): 348 return self.gatt_client.btif_gattc_discover_service_by_uuid(address, uuid) 349 350 def configure_mtu(self, address, mtu): 351 return self.gatt_client.configure_mtu(address, mtu) 352 353 def refresh_device(self, address): 354 return self.gatt_client.refresh_device(address) 355 356 def write_descriptor(self, address, handle, auth_req, value): 357 return self.gatt_client.write_descriptor(address, handle, auth_req, value) 358 359 def write_characteristic(self, address, handle, write_type, auth_req, value): 360 return self.gatt_client.write_characteristic(address, handle, write_type, auth_req, value) 361 362 def set_mps_qualification_enabled(self, enable): 363 return self.telephony_client.set_mps_qualification_enabled(enable) 364 365 def incoming_call(self, number): 366 return self.telephony_client.incoming_call(number) 367 368 def set_phone_ops_enabled(self, enable): 369 return self.telephony_client.set_phone_ops_enabled(enable) 370 371 def dial_call(self, number): 372 return self.telephony_client.dialing_call(number) 373 374 def answer_call(self): 375 return self.telephony_client.answer_call() 376 377 def swap_active_call(self): 378 return self.telephony_client.hold_active_accept_held() 379 380 def set_last_call(self, number=None): 381 return self.telephony_client.set_last_call(number) 382 383 def set_memory_call(self, number=None): 384 return self.telephony_client.set_memory_call(number) 385 386 def get_connected_audio_devices(self): 387 return self.media_client.devices 388 389 def audio_connect(self, address): 390 return self.telephony_client.audio_connect(address) 391 392 def audio_disconnect(self, address): 393 return self.telephony_client.audio_disconnect(address) 394 395 def hangup_call(self): 396 return self.telephony_client.hangup_call() 397 398 def set_battery_level(self, battery_level): 399 return self.telephony_client.set_battery_level(battery_level) 400 401 def gatt_connect(self, address, is_direct, transport): 402 return self.gatt_client.connect_client(address, is_direct, transport) 403 404 def set_connectable(self, mode): 405 return self.qa_client.set_connectable(mode) 406 407 def read_using_characteristic_uuid(self, address, uuid, start_handle, end_handle, auth_req): 408 return self.gatt_client.read_using_characteristic_uuid(address, uuid, start_handle, end_handle, auth_req) 409 410 def register_for_notification(self, address, handle, enable): 411 return self.gatt_client.register_for_notification(address, handle, enable) 412 413 def add_service(self, service): 414 service = self.gatt_server.make_dbus_service(service) 415 return self.gatt_server.add_service(service) 416 417 def is_encrypted(self, address): 418 connection_state = self.adapter_client.get_connection_state(address) 419 if connection_state is None: 420 logging.error('Failed to get connection state for address: %s', address) 421 return False 422 return connection_state > floss_enums.BtConnectionState.CONNECTED_ONLY 423 424 def listen_using_l2cap_channel(self): 425 return self.socket_manager.listen_using_l2cap_channel() 426 427 def listen_using_insecure_l2cap_channel(self): 428 return self.socket_manager.listen_using_insecure_l2cap_channel() 429 430 def create_l2cap_channel(self, address, psm): 431 name = self.adapter_client.get_remote_property(address, 'Name') 432 device = self.socket_manager.make_dbus_device(address, name) 433 return self.socket_manager.create_l2cap_channel(device, psm) 434 435 def create_insecure_l2cap_channel(self, address, psm): 436 name = self.adapter_client.get_remote_property(address, 'Name') 437 device = self.socket_manager.make_dbus_device(address, name) 438 return self.socket_manager.create_insecure_l2cap_channel(device, psm) 439 440 def accept_socket(self, socket_id, timeout_ms=None): 441 return self.socket_manager.accept(socket_id, timeout_ms) 442 443 def create_insecure_rfcomm_socket_to_service_record(self, address, uuid): 444 name = self.adapter_client.get_remote_property(address, 'Name') 445 device = self.socket_manager.make_dbus_device(address, name) 446 return self.socket_manager.create_insecure_rfcomm_socket_to_service_record(device, uuid) 447 448 def listen_using_insecure_rfcomm_with_service_record(self, name, uuid): 449 return self.socket_manager.listen_using_insecure_rfcomm_with_service_record(name, uuid) 450 451 def close_socket(self, socket_id): 452 return self.socket_manager.close(socket_id) 453 454 def get_connected_audio_devices(self): 455 return self.media_client.devices 456 457 def disconnect_media(self, address): 458 return self.media_client.disconnect(address) 459 460 def incoming_call(self, number): 461 return self.telephony_client.incoming_call(number) 462