blob: 7570620ec25f9973475f55e97e1bf3af2c7c4d99 [file] [log] [blame]
Scott James Remnant4dcd73f2013-07-22 15:00:24 -07001# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
Scott James Remnant1ca2e0e2013-07-31 16:49:07 -07005import json
Scott James Remnant4dcd73f2013-07-22 15:00:24 -07006
Hsinyu Chaoe0b08e62015-08-11 10:50:37 +00007from autotest_lib.client.cros import constants
Scott James Remnant4dcd73f2013-07-22 15:00:24 -07008from autotest_lib.server import autotest
9
10
Scott James Remnant8d2cbf32013-11-12 11:00:25 -080011class BluetoothDevice(object):
12 """BluetoothDevice is a thin layer of logic over a remote DUT.
Scott James Remnant1c72d7a2013-07-29 15:00:04 -070013
14 The Autotest host object representing the remote DUT, passed to this
15 class on initialization, can be accessed from its host property.
16
17 """
Scott James Remnant4dcd73f2013-07-22 15:00:24 -070018
19 XMLRPC_BRINGUP_TIMEOUT_SECONDS = 60
Katherine Threlkeld88aa1432015-07-30 11:44:35 -070020 XMLRPC_LOG_PATH = '/var/log/bluetooth_xmlrpc_device.log'
Scott James Remnant4dcd73f2013-07-22 15:00:24 -070021
Scott James Remnant8d2cbf32013-11-12 11:00:25 -080022 def __init__(self, device_host):
23 """Construct a BluetoothDevice.
Scott James Remnant4dcd73f2013-07-22 15:00:24 -070024
Scott James Remnant8d2cbf32013-11-12 11:00:25 -080025 @param device_host: host object representing a remote host.
Scott James Remnant4dcd73f2013-07-22 15:00:24 -070026
27 """
Scott James Remnant8d2cbf32013-11-12 11:00:25 -080028 self.host = device_host
Scott James Remnant4dcd73f2013-07-22 15:00:24 -070029 # Make sure the client library is on the device so that the proxy code
30 # is there when we try to call it.
31 client_at = autotest.Autotest(self.host)
32 client_at.install()
33 # Start up the XML-RPC proxy on the client.
Roshan Pius58e5dd32015-10-16 15:16:42 -070034 self._proxy = self.host.rpc_server_tracker.xmlrpc_connect(
Scott James Remnant8d2cbf32013-11-12 11:00:25 -080035 constants.BLUETOOTH_DEVICE_XMLRPC_SERVER_COMMAND,
36 constants.BLUETOOTH_DEVICE_XMLRPC_SERVER_PORT,
Scott James Remnant4dcd73f2013-07-22 15:00:24 -070037 command_name=
Scott James Remnant8d2cbf32013-11-12 11:00:25 -080038 constants.BLUETOOTH_DEVICE_XMLRPC_SERVER_CLEANUP_PATTERN,
Scott James Remnant4dcd73f2013-07-22 15:00:24 -070039 ready_test_name=
Scott James Remnant8d2cbf32013-11-12 11:00:25 -080040 constants.BLUETOOTH_DEVICE_XMLRPC_SERVER_READY_METHOD,
Katherine Threlkeld88aa1432015-07-30 11:44:35 -070041 timeout_seconds=self.XMLRPC_BRINGUP_TIMEOUT_SECONDS,
42 logfile=self.XMLRPC_LOG_PATH)
Scott James Remnant4dcd73f2013-07-22 15:00:24 -070043
Joseph Hwang97ff05d2016-05-10 16:07:22 +080044 # Get some static information about the bluetooth adapter.
45 properties = self.get_adapter_properties()
46 self.bluez_version = properties.get('Name')
47 self.address = properties.get('Address')
48 self.bluetooth_class = properties.get('Class')
49 self.UUIDs = properties.get('UUIDs')
50
51
52 def start_bluetoothd(self):
53 """start bluetoothd.
54
55 @returns: True if bluetoothd is started correctly.
56 False otherwise.
57
58 """
59 return self._proxy.start_bluetoothd()
60
61
62 def stop_bluetoothd(self):
63 """stop bluetoothd.
64
65 @returns: True if bluetoothd is stopped correctly.
66 False otherwise.
67
68 """
69 return self._proxy.stop_bluetoothd()
70
71
72 def is_bluetoothd_running(self):
73 """Is bluetoothd running?
74
75 @returns: True if bluetoothd is running
76
77 """
78 return self._proxy.is_bluetoothd_running()
79
Scott James Remnant1c72d7a2013-07-29 15:00:04 -070080
Scott James Remnanta6442f52013-07-24 15:04:55 -070081 def reset_on(self):
82 """Reset the adapter and settings and power up the adapter.
83
84 @return True on success, False otherwise.
85
86 """
87 return self._proxy.reset_on()
88
Scott James Remnant1c72d7a2013-07-29 15:00:04 -070089
Scott James Remnanta6442f52013-07-24 15:04:55 -070090 def reset_off(self):
91 """Reset the adapter and settings, leave the adapter powered off.
92
93 @return True on success, False otherwise.
94
95 """
96 return self._proxy.reset_off()
97
Scott James Remnant1c72d7a2013-07-29 15:00:04 -070098
Scott James Remnant1ca2e0e2013-07-31 16:49:07 -070099 def has_adapter(self):
100 """@return True if an adapter is present, False if not."""
101 return self._proxy.has_adapter()
102
103
Scott James Remnanta6442f52013-07-24 15:04:55 -0700104 def set_powered(self, powered):
105 """Set the adapter power state.
106
Scott James Remnant1c72d7a2013-07-29 15:00:04 -0700107 @param powered: adapter power state to set (True or False).
Scott James Remnanta6442f52013-07-24 15:04:55 -0700108
109 @return True on success, False otherwise.
110
111 """
112 return self._proxy.set_powered(powered)
113
Scott James Remnant1c72d7a2013-07-29 15:00:04 -0700114
Joseph Hwang97ff05d2016-05-10 16:07:22 +0800115 def is_powered_on(self):
116 """Is the adapter powered on?
117
118 @returns: True if the adapter is powered on
119
120 """
121 properties = self.get_adapter_properties()
122 return bool(properties.get(u'Powered'))
123
124
125 def get_hci(self):
126 """Get hci of the adapter; normally, it is 'hci0'.
127
128 @returns: the hci name of the adapter.
129
130 """
131 dev_info = self.get_dev_info()
132 hci = (dev_info[1] if isinstance(dev_info, list) and
133 len(dev_info) > 1 else None)
134 return hci
135
136
137 def get_address(self):
138 """Get the bluetooth address of the adapter.
139
140 An example of the bluetooth address of the adapter: '6C:29:95:1A:D4:6F'
141
142 @returns: the bluetooth address of the adapter.
143
144 """
145 return self.address
146
147
148 def get_bluez_version(self):
149 """Get bluez version.
150
151 An exmaple of bluez version: 'BlueZ 5.39'
152
153 @returns: the bluez version
154
155 """
156 return self.bluez_version
157
158
159 def get_bluetooth_class(self):
160 """Get the bluetooth class of the adapter.
161
162 An example of the bluetooth class of a chromebook: 4718852
163
164 @returns: the bluetooth class.
165
166 """
167 return self.bluetooth_class
168
169
170 def get_UUIDs(self):
171 """Get the UUIDs.
172
173 An example of UUIDs:
174 [u'00001112-0000-1000-8000-00805f9b34fb',
175 u'00001801-0000-1000-8000-00805f9b34fb',
176 u'0000110a-0000-1000-8000-00805f9b34fb',
177 u'0000111f-0000-1000-8000-00805f9b34fb',
178 u'00001200-0000-1000-8000-00805f9b34fb',
179 u'00001800-0000-1000-8000-00805f9b34fb']
180
181 @returns: the list of the UUIDs.
182
183 """
184 return self.UUIDs
185
186
Scott James Remnanta6442f52013-07-24 15:04:55 -0700187 def set_discoverable(self, discoverable):
188 """Set the adapter discoverable state.
189
Scott James Remnant1c72d7a2013-07-29 15:00:04 -0700190 @param discoverable: adapter discoverable state to set (True or False).
Scott James Remnanta6442f52013-07-24 15:04:55 -0700191
192 @return True on success, False otherwise.
193
194 """
195 return self._proxy.set_discoverable(discoverable)
196
Scott James Remnant1c72d7a2013-07-29 15:00:04 -0700197
Joseph Hwang97ff05d2016-05-10 16:07:22 +0800198 def is_discoverable(self):
199 """Is the adapter in the discoverable state?
200
201 @return True if discoverable. False otherwise.
202
203 """
204 properties = self.get_adapter_properties()
205 return properties.get('Discoverable') == 1
206
207
Scott James Remnanta6442f52013-07-24 15:04:55 -0700208 def set_pairable(self, pairable):
209 """Set the adapter pairable state.
210
Scott James Remnant1c72d7a2013-07-29 15:00:04 -0700211 @param pairable: adapter pairable state to set (True or False).
Scott James Remnanta6442f52013-07-24 15:04:55 -0700212
213 @return True on success, False otherwise.
214
215 """
216 return self._proxy.set_pairable(pairable)
217
Scott James Remnant1c72d7a2013-07-29 15:00:04 -0700218
Joseph Hwang97ff05d2016-05-10 16:07:22 +0800219 def is_pairable(self):
220 """Is the adapter in the pairable state?
221
222 @return True if pairable. False otherwise.
223
224 """
225 properties = self.get_adapter_properties()
226 return properties.get('Pairable') == 1
227
228
Scott James Remnant1ca2e0e2013-07-31 16:49:07 -0700229 def get_adapter_properties(self):
230 """Read the adapter properties from the Bluetooth Daemon.
231
Joseph Hwang97ff05d2016-05-10 16:07:22 +0800232 An example of the adapter properties looks like
233 {u'Name': u'BlueZ 5.35',
234 u'Alias': u'Chromebook',
235 u'Modalias': u'bluetooth:v00E0p2436d0400',
236 u'Powered': 1,
237 u'DiscoverableTimeout': 180,
238 u'PairableTimeout': 0,
239 u'Discoverable': 0,
240 u'Address': u'6C:29:95:1A:D4:6F',
241 u'Discovering': 0,
242 u'Pairable': 1,
243 u'Class': 4718852,
244 u'UUIDs': [u'00001112-0000-1000-8000-00805f9b34fb',
245 u'00001801-0000-1000-8000-00805f9b34fb',
246 u'0000110a-0000-1000-8000-00805f9b34fb',
247 u'0000111f-0000-1000-8000-00805f9b34fb',
248 u'00001200-0000-1000-8000-00805f9b34fb',
249 u'00001800-0000-1000-8000-00805f9b34fb']}
250
Scott James Remnantaec4edd2013-08-26 18:47:11 -0700251 @return the properties as a dictionary on success,
Scott James Remnant1ca2e0e2013-07-31 16:49:07 -0700252 the value False otherwise.
253
254 """
255 return json.loads(self._proxy.get_adapter_properties())
256
257
Scott James Remnant50915ad2014-12-01 13:51:39 -0800258 def read_version(self):
259 """Read the version of the management interface from the Kernel.
260
Scott James Remnante59d5b92014-12-01 14:21:47 -0800261 @return the version as a tuple of:
Scott James Remnant50915ad2014-12-01 13:51:39 -0800262 ( version, revision )
263
264 """
265 return json.loads(self._proxy.read_version())
266
267
268 def read_supported_commands(self):
269 """Read the set of supported commands from the Kernel.
270
Scott James Remnante59d5b92014-12-01 14:21:47 -0800271 @return set of supported commands as arrays in a tuple of:
Scott James Remnant50915ad2014-12-01 13:51:39 -0800272 ( commands, events )
273
274 """
275 return json.loads(self._proxy.read_supported_commands())
276
277
278 def read_index_list(self):
279 """Read the list of currently known controllers from the Kernel.
280
Scott James Remnante59d5b92014-12-01 14:21:47 -0800281 @return array of controller indexes.
Scott James Remnant50915ad2014-12-01 13:51:39 -0800282
283 """
284 return json.loads(self._proxy.read_index_list())
285
286
Scott James Remnant1ca2e0e2013-07-31 16:49:07 -0700287 def read_info(self):
288 """Read the adapter information from the Kernel.
289
Joseph Hwang97ff05d2016-05-10 16:07:22 +0800290 An example of the adapter information looks like
291 [u'6C:29:95:1A:D4:6F', 6, 2, 65535, 2769, 4718852, u'Chromebook', u'']
292
Scott James Remnantaec4edd2013-08-26 18:47:11 -0700293 @return the information as a tuple of:
Scott James Remnant1ca2e0e2013-07-31 16:49:07 -0700294 ( address, bluetooth_version, manufacturer_id,
295 supported_settings, current_settings, class_of_device,
296 name, short_name )
297
298 """
299 return json.loads(self._proxy.read_info())
300
301
Scott James Remnantabea37c2014-12-01 14:22:23 -0800302 def add_device(self, address, address_type, action):
303 """Add a device to the Kernel action list.
304
305 @param address: Address of the device to add.
306 @param address_type: Type of device in @address.
307 @param action: Action to take.
308
309 @return tuple of ( address, address_type ) on success,
310 None on failure.
311
312 """
313 return json.loads(self._proxy.add_device(address, address_type, action))
314
315
316 def remove_device(self, address, address_type):
317 """Remove a device from the Kernel action list.
318
319 @param address: Address of the device to remove.
320 @param address_type: Type of device in @address.
321
322 @return tuple of ( address, address_type ) on success,
323 None on failure.
324
325 """
326 return json.loads(self._proxy.remove_device(address, address_type))
327
328
Scott James Remnantaec4edd2013-08-26 18:47:11 -0700329 def get_devices(self):
330 """Read information about remote devices known to the adapter.
331
Joseph Hwang97ff05d2016-05-10 16:07:22 +0800332 An example of the device information of RN-42 looks like
333 [{u'Name': u'RNBT-A96F',
334 u'Alias': u'RNBT-A96F',
335 u'Adapter': u'/org/bluez/hci0',
336 u'LegacyPairing': 0,
337 u'Paired': 1,
338 u'Connected': 0,
339 u'UUIDs': [u'00001124-0000-1000-8000-00805f9b34fb'],
340 u'Address': u'00:06:66:75:A9:6F',
341 u'Icon': u'input-mouse',
342 u'Class': 1408,
343 u'Trusted': 1,
344 u'Blocked': 0}]
345
Scott James Remnante59d5b92014-12-01 14:21:47 -0800346 @return the properties of each device as an array of
Scott James Remnantaec4edd2013-08-26 18:47:11 -0700347 dictionaries on success, the value False otherwise.
348
349 """
350 return json.loads(self._proxy.get_devices())
351
352
Joseph Hwangf064e0f2016-05-23 16:46:45 +0800353 def get_device_properties(self, address):
354 """Read information about remote devices known to the adapter.
355
356 An example of the device information of RN-42 looks like
357
358 @param address: Address of the device to pair.
359 @param pin: The pin code of the device to pair.
360 @param timeout: The timeout in seconds for pairing.
361
362 @returns: a dictionary of device properties of the device on success;
363 an empty dictionary otherwise.
364
365 """
366 return json.loads(self._proxy.get_device_by_address(address))
367
368 for device in self.get_devices():
369 if device.get['Address'] == address:
370 return device
371 return dict()
372
373
Scott James Remnantaec4edd2013-08-26 18:47:11 -0700374 def start_discovery(self):
375 """Start discovery of remote devices.
376
377 Obtain the discovered device information using get_devices(), called
378 stop_discovery() when done.
379
380 @return True on success, False otherwise.
381
382 """
383 return self._proxy.start_discovery()
384
385
386 def stop_discovery(self):
387 """Stop discovery of remote devices.
388
389 @return True on success, False otherwise.
390
391 """
392 return self._proxy.stop_discovery()
393
394
Joseph Hwang97ff05d2016-05-10 16:07:22 +0800395 def is_discovering(self):
396 """Is it discovering?
397
398 @return True if it is discovering. False otherwise.
399
400 """
401 return self.get_adapter_properties().get('Discovering') == 1
402
403
Scott James Remnantc7fd7a42014-12-01 16:43:38 -0800404 def get_dev_info(self):
405 """Read raw HCI device information.
406
Joseph Hwang97ff05d2016-05-10 16:07:22 +0800407 An example of the device information looks like:
408 [0, u'hci0', u'6C:29:95:1A:D4:6F', 13, 0, 1, 581900950526, 52472, 7,
409 32768, 1021, 5, 96, 6, 0, 0, 151, 151, 0, 0, 0, 0, 1968, 12507]
410
Scott James Remnantc7fd7a42014-12-01 16:43:38 -0800411 @return tuple of (index, name, address, flags, device_type, bus_type,
412 features, pkt_type, link_policy, link_mode,
413 acl_mtu, acl_pkts, sco_mtu, sco_pkts,
414 err_rx, err_tx, cmd_tx, evt_rx, acl_tx, acl_rx,
415 sco_tx, sco_rx, byte_rx, byte_tx) on success,
416 None on failure.
417
418 """
419 return json.loads(self._proxy.get_dev_info())
420
421
Artem Rakhovb144dce2014-02-20 21:02:09 -0800422 def register_profile(self, path, uuid, options):
423 """Register new profile (service).
424
425 @param path: Path to the profile object.
426 @param uuid: Service Class ID of the service as string.
427 @param options: Dictionary of options for the new service, compliant
428 with BlueZ D-Bus Profile API standard.
429
430 @return True on success, False otherwise.
431
432 """
433 return self._proxy.register_profile(path, uuid, options)
434
435
Cheng-Yi Chiangdc6442b2015-07-08 16:07:02 +0800436 def has_device(self, address):
437 """Checks if the device with a given address exists.
438
439 @param address: Address of the device.
440
441 @returns: True if there is a device with that address.
442 False otherwise.
443
444 """
445 return self._proxy.has_device(address)
446
447
Joseph Hwangf064e0f2016-05-23 16:46:45 +0800448 def device_is_paired(self, address):
449 """Checks if a device is paired.
450
451 @param address: address of the device.
452
453 @returns: True if device is paired. False otherwise.
454
455 """
456 return self._proxy.device_is_paired(address)
457
458
459 def set_trusted(self, address, trusted=True):
460 """Set the device trusted.
461
462 @param address: The bluetooth address of the device.
463 @param trusted: True or False indicating whether to set trusted or not.
464
465 @returns: True if successful. False otherwise.
466
467 """
468 return self._proxy.set_trusted(address, trusted)
469
470
471 def pair_legacy_device(self, address, pin, trusted, timeout):
Cheng-Yi Chiang212d4692015-05-25 22:08:14 +0800472 """Pairs a device with a given pin code.
473
474 Registers an agent who handles pin code request and
475 pairs a device with known pin code.
476
477 @param address: Address of the device to pair.
478 @param pin: The pin code of the device to pair.
Joseph Hwangf064e0f2016-05-23 16:46:45 +0800479 @param trusted: indicating whether to set the device trusted.
Cheng-Yi Chiang212d4692015-05-25 22:08:14 +0800480 @param timeout: The timeout in seconds for pairing.
481
482 @returns: True on success. False otherwise.
483
484 """
Joseph Hwangf064e0f2016-05-23 16:46:45 +0800485 return self._proxy.pair_legacy_device(address, pin, trusted, timeout)
486
487
488 def remove_device_object(self, address):
489 """Removes a device object and the pairing information.
490
491 Calls RemoveDevice method to remove remote device
492 object and the pairing information.
493
494 @param address: address of the device to unpair.
495
496 @returns: True on success. False otherwise.
497
498 """
499 return self._proxy.remove_device_object(address)
Cheng-Yi Chiang212d4692015-05-25 22:08:14 +0800500
501
502 def connect_device(self, address):
503 """Connects a device.
504
505 Connects a device if it is not connected.
506
507 @param address: Address of the device to connect.
508
509 @returns: True on success. False otherwise.
510
511 """
512 return self._proxy.connect_device(address)
513
514
Cheng-Yi Chianga5328522015-10-21 14:46:41 +0800515 def device_is_connected(self, address):
516 """Checks if a device is connected.
517
518 @param address: Address of the device to check if it is connected.
519
520 @returns: True if device is connected. False otherwise.
521
522 """
523 return self._proxy.device_is_connected(address)
524
525
Cheng-Yi Chiang212d4692015-05-25 22:08:14 +0800526 def disconnect_device(self, address):
527 """Disconnects a device.
528
529 Disconnects a device if it is connected.
530
531 @param address: Address of the device to disconnect.
532
533 @returns: True on success. False otherwise.
534
535 """
536 return self._proxy.disconnect_device(address)
537
538
Joseph Hwangefaf0352016-09-29 14:53:33 +0800539 def btmon_start(self):
540 """Start btmon monitoring."""
541 self._proxy.btmon_start()
542
543
544 def btmon_stop(self):
545 """Stop btmon monitoring."""
546 self._proxy.btmon_stop()
547
548
Joseph Hwang5c692b82016-11-21 15:56:57 +0800549 def btmon_get(self, search_str='', start_str=''):
Joseph Hwangefaf0352016-09-29 14:53:33 +0800550 """Get btmon output contents.
551
Joseph Hwang5c692b82016-11-21 15:56:57 +0800552 @param search_str: only lines with search_str would be kept.
553 @param start_str: all lines before the occurrence of start_str would be
554 filtered.
555
Joseph Hwangefaf0352016-09-29 14:53:33 +0800556 @returns: the recorded btmon output.
557
558 """
Joseph Hwang5c692b82016-11-21 15:56:57 +0800559 return self._proxy.btmon_get(search_str, start_str)
Joseph Hwangefaf0352016-09-29 14:53:33 +0800560
561
562 def btmon_find(self, pattern_str):
563 """Find if a pattern string exists in btmon output.
564
565 @param pattern_str: the pattern string to find.
566
567 @returns: True on success. False otherwise.
568
569 """
570 return self._proxy.btmon_find(pattern_str)
571
572
573 def register_advertisement(self, advertisement_data):
574 """Register an advertisement.
575
576 Note that rpc supports only conformable types. Hence, a
577 dict about the advertisement is passed as a parameter such
578 that the advertisement object could be contructed on the host.
579
580 @param advertisement_data: a dict of the advertisement for
581 the adapter to register.
582
583 @returns: True on success. False otherwise.
584
585 """
586 return self._proxy.register_advertisement(advertisement_data)
587
588
589 def set_advertising_intervals(self, min_adv_interval_ms,
590 max_adv_interval_ms):
591 """Set advertising intervals.
592
593 @param min_adv_interval_ms: the min advertising interval in ms.
594 @param max_adv_interval_ms: the max advertising interval in ms.
595
596 @returns: True on success. False otherwise.
597
598 """
599 return self._proxy.set_advertising_intervals(min_adv_interval_ms,
600 max_adv_interval_ms)
601
602
603 def reset_advertising(self):
604 """Reset advertising.
605
606 This includes unregister all advertisements, reset advertising
607 intervals, and disable advertising.
608
609 @returns: True on success. False otherwise.
610
611 """
612 return self._proxy.reset_advertising()
613
614
Katherine Threlkeld88aa1432015-07-30 11:44:35 -0700615 def copy_logs(self, destination):
616 """Copy the logs generated by this device to a given location.
617
618 @param destination: destination directory for the logs.
619
620 """
621 self.host.collect_logs(self.XMLRPC_LOG_PATH, destination)
622
623
Scott James Remnant4dcd73f2013-07-22 15:00:24 -0700624 def close(self):
625 """Tear down state associated with the client."""
Scott James Remnantda9f43c2013-08-07 17:53:14 -0700626 # Turn off the discoverable flag since it may affect future tests.
627 self._proxy.set_discoverable(False)
Scott James Remnanta6442f52013-07-24 15:04:55 -0700628 # Leave the adapter powered off, but don't do a full reset.
629 self._proxy.set_powered(False)
Scott James Remnant4dcd73f2013-07-22 15:00:24 -0700630 # This kills the RPC server.
Scott James Remnant1c72d7a2013-07-29 15:00:04 -0700631 self.host.close()