1#!/usr/bin/env python 2 3# Copyright (c) 2013 The Chromium OS Authors. All rights reserved. 4# Use of this source code is governed by a BSD-style license that can be 5# found in the LICENSE file. 6 7import base64 8import json 9import logging 10import logging.handlers 11 12import common 13from autotest_lib.client.common_lib.cros.bluetooth import bluetooth_sdp_socket 14from autotest_lib.client.common_lib.cros.bluetooth import bluetooth_socket 15from autotest_lib.client.cros import constants 16from autotest_lib.client.cros import xmlrpc_server 17 18 19class BluetoothTesterXmlRpcDelegate(xmlrpc_server.XmlRpcDelegate): 20 """Exposes Tester methods called remotely during Bluetooth autotests. 21 22 All instance methods of this object without a preceding '_' are exposed via 23 an XML-RPC server. This is not a stateless handler object, which means that 24 if you store state inside the delegate, that state will remain around for 25 future calls. 26 """ 27 28 BR_EDR_LE_PROFILE = ( 29 bluetooth_socket.MGMT_SETTING_POWERED | 30 bluetooth_socket.MGMT_SETTING_CONNECTABLE | 31 bluetooth_socket.MGMT_SETTING_PAIRABLE | 32 bluetooth_socket.MGMT_SETTING_SSP | 33 bluetooth_socket.MGMT_SETTING_BREDR | 34 bluetooth_socket.MGMT_SETTING_LE) 35 36 LE_PROFILE = ( 37 bluetooth_socket.MGMT_SETTING_POWERED | 38 bluetooth_socket.MGMT_SETTING_CONNECTABLE | 39 bluetooth_socket.MGMT_SETTING_PAIRABLE | 40 bluetooth_socket.MGMT_SETTING_LE) 41 42 PROFILE_SETTINGS = { 43 'computer': BR_EDR_LE_PROFILE, 44 'peripheral': LE_PROFILE 45 } 46 47 PROFILE_CLASS = { 48 'computer': 0x000104, 49 'peripheral': None 50 } 51 52 PROFILE_NAMES = { 53 'computer': ('ChromeOS Bluetooth Tester', 'Tester'), 54 'peripheral': ('ChromeOS Bluetooth Tester', 'Tester') 55 } 56 57 58 def __init__(self): 59 super(BluetoothTesterXmlRpcDelegate, self).__init__() 60 61 # Open the Bluetooth Control socket to the kernel which provides us 62 # the needed raw management access to the Bluetooth Host Subsystem. 63 self._control = bluetooth_socket.BluetoothControlSocket() 64 # Open the Bluetooth SDP socket to the kernel which provides us the 65 # needed interface to use SDP commands. 66 self._sdp = bluetooth_sdp_socket.BluetoothSDPSocket() 67 # This is almost a constant, but it might not be forever. 68 self.index = 0 69 70 71 def setup(self, profile): 72 """Set up the tester with the given profile. 73 74 @param profile: Profile to use for this test, valid values are: 75 computer - a standard computer profile 76 77 @return True on success, False otherwise. 78 79 """ 80 profile_settings = self.PROFILE_SETTINGS[profile] 81 profile_class = self.PROFILE_CLASS[profile] 82 (profile_name, profile_short_name) = self.PROFILE_NAMES[profile] 83 84 # Make sure the controller actually exists. 85 if self.index not in self._control.read_index_list(): 86 logging.warning('Bluetooth Controller missing on tester') 87 return False 88 89 # Make sure all of the settings are supported by the controller. 90 ( address, bluetooth_version, manufacturer_id, 91 supported_settings, current_settings, class_of_device, 92 name, short_name ) = self._control.read_info(self.index) 93 if profile_settings & supported_settings != profile_settings: 94 logging.warning('Controller does not support requested settings') 95 logging.debug('Supported: %b; Requested: %b', supported_settings, 96 profile_settings) 97 return False 98 99 # The high 8 bits of class_of_device is part of Class of Service field 100 # which are not actually updated in kernel with 101 # self._control.set_device_class() below due to a bug in kernel 102 # mgmt.c: set_dev_class(). Hence, we should keep these bits in 103 # profile_class to make the test setup correctly. 104 # Refer to this link about Class of Device/Service bits. 105 # https://www.bluetooth.com/specifications/assigned-numbers/baseband 106 # Since the class of device is used as an indication only and is not 107 # practically useful in autotest, the service class bits are just 108 # copied from previous self._control.read_info() request. 109 # Refer to Bluetooth Spec. 4.2, "Vol 3, Part C, 3.2.4.4 Usage" about 110 # why it is not actually important. 111 # Refer to "Vol 2. Part E, 7.3.26 Write Class of Device Command" about 112 # the correct parameters to pass to set_device_class() which require 113 # 3 bytes instead of 2 bytes. 114 # Remove the following statement which modifies profile_class only 115 # when kernel is fixed and 3 bytes are passed in set_dev_class(). 116 # However, this is of very low priority. 117 profile_class = ((class_of_device & 0xFF0000) | 118 (profile_class & 0x00FFFF)) 119 120 # Before beginning, force the adapter power off, even if it's already 121 # off; this is enough to persuade an AP-mode Intel chip to accept 122 # settings. 123 if not self._control.set_powered(self.index, False): 124 logging.warning('Failed to power off adapter to accept settings') 125 return False 126 127 # Set the controller up as either BR/EDR only, LE only or Dual Mode. 128 # This is a bit tricky because it rejects commands outright unless 129 # it's in dual mode, so we actually have to figure out what changes 130 # we have to make, and we have to turn things on before we turn them 131 # off. 132 turn_on = (current_settings ^ profile_settings) & profile_settings 133 if turn_on & bluetooth_socket.MGMT_SETTING_BREDR: 134 if self._control.set_bredr(self.index, True) is None: 135 logging.warning('Failed to enable BR/EDR') 136 return False 137 if turn_on & bluetooth_socket.MGMT_SETTING_LE: 138 if self._control.set_le(self.index, True) is None: 139 logging.warning('Failed to enable LE') 140 return False 141 142 turn_off = (current_settings ^ profile_settings) & current_settings 143 if turn_off & bluetooth_socket.MGMT_SETTING_BREDR: 144 if self._control.set_bredr(self.index, False) is None: 145 logging.warning('Failed to disable BR/EDR') 146 return False 147 if turn_off & bluetooth_socket.MGMT_SETTING_LE: 148 if self._control.set_le(self.index, False) is None: 149 logging.warning('Failed to disable LE') 150 return False 151 if turn_off & bluetooth_socket.MGMT_SETTING_SECURE_CONNECTIONS: 152 if self._control.set_secure_connections(self.index, False) is None: 153 logging.warning('Failed to disable secure connections') 154 return False 155 156 # Adjust settings that are BR/EDR specific that we need to set before 157 # powering on the adapter, and would be rejected otherwise. 158 if profile_settings & bluetooth_socket.MGMT_SETTING_BREDR: 159 if (self._control.set_link_security( 160 self.index, 161 (profile_settings & 162 bluetooth_socket.MGMT_SETTING_LINK_SECURITY)) 163 is None): 164 logging.warning('Failed to set link security setting') 165 return False 166 if (self._control.set_ssp( 167 self.index, 168 profile_settings & bluetooth_socket.MGMT_SETTING_SSP) 169 is None): 170 logging.warning('Failed to set SSP setting') 171 return False 172 if (self._control.set_hs( 173 self.index, 174 profile_settings & bluetooth_socket.MGMT_SETTING_HS) 175 is None): 176 logging.warning('Failed to set High Speed setting') 177 return False 178 179 # Split our the major and minor class; it's listed as a kernel bug 180 # that we supply these to the kernel without shifting the bits over 181 # to take out the CoD format field, so this might have to change 182 # one day. 183 major_class = (profile_class & 0x00ff00) >> 8 184 minor_class = profile_class & 0x0000ff 185 if (self._control.set_device_class( 186 self.index, major_class, minor_class) 187 is None): 188 logging.warning('Failed to set device class') 189 return False 190 191 # Setup generic settings that apply to either BR/EDR, LE or dual-mode 192 # that still require the power to be off. 193 if (self._control.set_connectable( 194 self.index, 195 profile_settings & bluetooth_socket.MGMT_SETTING_CONNECTABLE) 196 is None): 197 logging.warning('Failed to set connectable setting') 198 return False 199 if (self._control.set_pairable( 200 self.index, 201 profile_settings & bluetooth_socket.MGMT_SETTING_PAIRABLE) 202 is None): 203 logging.warning('Failed to set pairable setting') 204 return False 205 206 if (self._control.set_local_name( 207 self.index, profile_name, profile_short_name) 208 is None): 209 logging.warning('Failed to set local name') 210 return False 211 212 # Check and set discoverable property 213 if ((profile_settings ^ current_settings) & 214 bluetooth_socket.MGMT_SETTING_DISCOVERABLE): 215 logging.debug('Set discoverable to %x ', 216 profile_settings & 217 bluetooth_socket.MGMT_SETTING_DISCOVERABLE) 218 if self._control.set_discoverable( 219 self.index, 220 profile_settings & 221 bluetooth_socket.MGMT_SETTING_DISCOVERABLE) is None: 222 logging.warning('Failed to set discoverable setting') 223 return False 224 225 # Now the settings have been set, power up the adapter. 226 if not self._control.set_powered( 227 self.index, 228 profile_settings & bluetooth_socket.MGMT_SETTING_POWERED): 229 logging.warning('Failed to set powered setting') 230 return False 231 232 # Fast connectable can only be set once the controller is powered, 233 # and only when BR/EDR is enabled. 234 if profile_settings & bluetooth_socket.MGMT_SETTING_BREDR: 235 # Wait for the device class set event, this happens after the 236 # power up "command complete" event when we've pre-set the class 237 # even though it's a side-effect of doing that. 238 self._control.wait_for_events( 239 self.index, 240 ( bluetooth_socket.MGMT_EV_CLASS_OF_DEV_CHANGED, )) 241 242 if (self._control.set_fast_connectable( 243 self.index, 244 profile_settings & 245 bluetooth_socket.MGMT_SETTING_FAST_CONNECTABLE) 246 is None): 247 logging.warning('Failed to set fast connectable setting') 248 return False 249 250 # Fetch the settings again and make sure they're all set correctly, 251 # including the BR/EDR flag. 252 ( address, bluetooth_version, manufacturer_id, 253 supported_settings, current_settings, class_of_device, 254 name, short_name ) = self._control.read_info(self.index) 255 256 # Check generic settings. 257 if profile_settings != current_settings: 258 logging.warning('Controller settings did not match those set: ' 259 '%x != %x', current_settings, profile_settings) 260 return False 261 if name != profile_name: 262 logging.warning('Local name did not match that set: "%s" != "%s"', 263 name, profile_name) 264 return False 265 elif short_name != profile_short_name: 266 logging.warning('Short name did not match that set: "%s" != "%s"', 267 short_name, profile_short_name) 268 return False 269 270 # Check BR/EDR specific settings. 271 if profile_settings & bluetooth_socket.MGMT_SETTING_BREDR: 272 if class_of_device != profile_class: 273 if class_of_device & 0x00ffff == profile_class & 0x00ffff: 274 logging.warning('Class of device matched that set, but ' 275 'Service Class field did not: %x != %x ' 276 'Reboot Tester? ', 277 class_of_device, profile_class) 278 else: 279 logging.warning('Class of device did not match that set: ' 280 '%x != %x', class_of_device, profile_class) 281 return False 282 283 return True 284 285 286 def set_discoverable(self, discoverable, timeout=0): 287 """Set the discoverable state of the controller. 288 289 @param discoverable: Whether controller should be discoverable. 290 @param timeout: Timeout in seconds before disabling discovery again, 291 ignored when discoverable is False, must not be zero when 292 discoverable is True. 293 294 @return True on success, False otherwise. 295 296 """ 297 settings = self._control.set_discoverable(self.index, 298 discoverable, timeout) 299 return settings & bluetooth_socket.MGMT_SETTING_DISCOVERABLE 300 301 302 def read_info(self): 303 """Read the adapter information from the Kernel. 304 305 @return the information as a JSON-encoded tuple of: 306 ( address, bluetooth_version, manufacturer_id, 307 supported_settings, current_settings, class_of_device, 308 name, short_name ) 309 310 """ 311 return json.dumps(self._control.read_info(self.index)) 312 313 314 def set_advertising(self, advertising): 315 """Set the whether the controller is advertising via LE. 316 317 @param advertising: Whether controller should advertise via LE. 318 319 @return True on success, False otherwise. 320 321 """ 322 settings = self._control.set_advertising(self.index, advertising) 323 return settings & bluetooth_socket.MGMT_SETTING_ADVERTISING 324 325 326 def discover_devices(self, br_edr=True, le_public=True, le_random=True): 327 """Discover remote devices. 328 329 Activates device discovery and collects the set of devices found, 330 returning them as a list. 331 332 @param br_edr: Whether to detect BR/EDR devices. 333 @param le_public: Whether to detect LE Public Address devices. 334 @param le_random: Whether to detect LE Random Address devices. 335 336 @return List of devices found as JSON-encoded tuples with the format 337 (address, address_type, rssi, flags, base64-encoded eirdata), 338 or False if discovery could not be started. 339 340 """ 341 address_type = 0 342 if br_edr: 343 address_type |= 0x1 344 if le_public: 345 address_type |= 0x2 346 if le_random: 347 address_type |= 0x4 348 349 set_type = self._control.start_discovery(self.index, address_type) 350 if set_type != address_type: 351 logging.warning('Discovery address type did not match that set: ' 352 '%x != %x', set_type, address_type) 353 return False 354 355 devices = self._control.get_discovered_devices(self.index) 356 return json.dumps([ 357 (address, address_type, rssi, flags, 358 base64.encodestring(eirdata)) 359 for address, address_type, rssi, flags, eirdata in devices 360 ]) 361 362 363 def connect(self, address): 364 """Connect to device with the given address 365 366 @param address: Bluetooth address. 367 368 """ 369 self._sdp.connect(address) 370 return True 371 372 373 def service_search_request(self, uuids, max_rec_cnt, preferred_size=32, 374 forced_pdu_size=None, invalid_request=False): 375 """Send a Service Search Request 376 377 @param uuids: List of UUIDs (as integers) to look for. 378 @param max_rec_cnt: Maximum count of returned service records. 379 @param preferred_size: Preffered size of UUIDs in bits (16, 32, or 128). 380 @param forced_pdu_size: Use certain PDU size parameter instead of 381 calculating actual length of sequence. 382 @param invalid_request: Whether to send request with intentionally 383 invalid syntax for testing purposes (bool flag). 384 385 @return list of found services' service record handles or Error Code 386 387 """ 388 return json.dumps( 389 self._sdp.service_search_request( 390 uuids, max_rec_cnt, preferred_size, forced_pdu_size, 391 invalid_request) 392 ) 393 394 395 def service_attribute_request(self, handle, max_attr_byte_count, attr_ids, 396 forced_pdu_size=None, invalid_request=None): 397 """Send a Service Attribute Request 398 399 @param handle: service record from which attribute values are to be 400 retrieved. 401 @param max_attr_byte_count: maximum number of bytes of attribute data to 402 be returned in the response to this request. 403 @param attr_ids: a list, where each element is either an attribute ID 404 or a range of attribute IDs. 405 @param forced_pdu_size: Use certain PDU size parameter instead of 406 calculating actual length of sequence. 407 @param invalid_request: Whether to send request with intentionally 408 invalid syntax for testing purposes (string with raw request). 409 410 @return list of found attributes IDs and their values or Error Code 411 412 """ 413 return json.dumps( 414 self._sdp.service_attribute_request( 415 handle, max_attr_byte_count, attr_ids, forced_pdu_size, 416 invalid_request) 417 ) 418 419 420 def service_search_attribute_request(self, uuids, max_attr_byte_count, 421 attr_ids, preferred_size=32, 422 forced_pdu_size=None, 423 invalid_request=None): 424 """Send a Service Search Attribute Request 425 426 @param uuids: list of UUIDs (as integers) to look for. 427 @param max_attr_byte_count: maximum number of bytes of attribute data to 428 be returned in the response to this request. 429 @param attr_ids: a list, where each element is either an attribute ID 430 or a range of attribute IDs. 431 @param preferred_size: Preffered size of UUIDs in bits (16, 32, or 128). 432 @param forced_pdu_size: Use certain PDU size parameter instead of 433 calculating actual length of sequence. 434 @param invalid_request: Whether to send request with intentionally 435 invalid syntax for testing purposes (string to be prepended 436 to correct request). 437 438 @return list of found attributes IDs and their values or Error Code 439 440 """ 441 return json.dumps( 442 self._sdp.service_search_attribute_request( 443 uuids, max_attr_byte_count, attr_ids, preferred_size, 444 forced_pdu_size, invalid_request) 445 ) 446 447 448if __name__ == '__main__': 449 logging.basicConfig(level=logging.DEBUG) 450 handler = logging.handlers.SysLogHandler(address = '/dev/log') 451 formatter = logging.Formatter( 452 'bluetooth_tester_xmlrpc_server: [%(levelname)s] %(message)s') 453 handler.setFormatter(formatter) 454 logging.getLogger().addHandler(handler) 455 logging.debug('bluetooth_tester_xmlrpc_server main...') 456 server = xmlrpc_server.XmlRpcServer( 457 'localhost', 458 constants.BLUETOOTH_TESTER_XMLRPC_SERVER_PORT) 459 server.register_delegate(BluetoothTesterXmlRpcDelegate()) 460 server.run() 461