• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 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
5import base64
6import json
7import logging
8from datetime import datetime
9
10from autotest_lib.client.bin import utils
11from autotest_lib.client.cros import constants
12from autotest_lib.server import autotest
13
14
15class BluetoothDevice(object):
16    """BluetoothDevice is a thin layer of logic over a remote DUT.
17
18    The Autotest host object representing the remote DUT, passed to this
19    class on initialization, can be accessed from its host property.
20
21    """
22
23    XMLRPC_BRINGUP_TIMEOUT_SECONDS = 60
24    XMLRPC_LOG_PATH = '/var/log/bluetooth_xmlrpc_device.log'
25
26    def __init__(self, device_host):
27        """Construct a BluetoothDevice.
28
29        @param device_host: host object representing a remote host.
30
31        """
32        self.host = device_host
33        # Make sure the client library is on the device so that the proxy code
34        # is there when we try to call it.
35        client_at = autotest.Autotest(self.host)
36        client_at.install()
37        # Start up the XML-RPC proxy on the client.
38        self._proxy = self.host.rpc_server_tracker.xmlrpc_connect(
39                constants.BLUETOOTH_DEVICE_XMLRPC_SERVER_COMMAND,
40                constants.BLUETOOTH_DEVICE_XMLRPC_SERVER_PORT,
41                command_name=
42                  constants.BLUETOOTH_DEVICE_XMLRPC_SERVER_CLEANUP_PATTERN,
43                ready_test_name=
44                  constants.BLUETOOTH_DEVICE_XMLRPC_SERVER_READY_METHOD,
45                timeout_seconds=self.XMLRPC_BRINGUP_TIMEOUT_SECONDS,
46                logfile=self.XMLRPC_LOG_PATH)
47
48        # Get some static information about the bluetooth adapter.
49        properties = self.get_adapter_properties()
50        self.bluez_version = properties.get('Name')
51        self.address = properties.get('Address')
52        self.bluetooth_class = properties.get('Class')
53        self.UUIDs = properties.get('UUIDs')
54
55
56    def set_debug_log_levels(self, dispatcher_vb, newblue_vb, bluez_vb,
57                             kernel_vb):
58        """Enable or disable the debug logs of bluetooth
59
60        @param dispatcher_vb: verbosity of btdispatcher debug log, either 0 or 1
61        @param newblue_vb: verbosity of newblued debug log, either 0 or 1
62        @param bluez_vb: verbosity of bluez debug log, either 0 or 1
63        @param kernel_vb: verbosity of kernel debug log, either 0 or 1
64
65        """
66        return self._proxy.set_debug_log_levels(dispatcher_vb, newblue_vb,
67                                                bluez_vb, kernel_vb)
68
69    def log_message(self, msg, dut=True, peer=True):
70        """ Log a message in DUT log and peer logs with timestamp.
71
72        @param msg: message to be logged.
73        @param dut: log message on DUT
74        @param peer: log message on peer devices
75        """
76        try:
77            # TODO(b/146671469) Implement logging to tester
78
79            date =  datetime.strftime(datetime.now(),"%Y:%m:%d %H:%M:%S:%f")
80            msg = "bluetooth autotest --- %s : %s ---" % (date, msg)
81            logging.debug("Broadcasting '%s'",msg)
82
83            if dut:
84                self._proxy.log_message(msg)
85
86            if peer:
87                for chameleon in self.host.chameleon_list:
88                    chameleon.log_message(msg)
89        except Exception as e:
90            logging.error("Exception '%s' in log_message '%s'", str(e), msg)
91
92
93
94    def start_bluetoothd(self):
95        """start bluetoothd.
96
97        @returns: True if bluetoothd is started correctly.
98                  False otherwise.
99
100        """
101        return self._proxy.start_bluetoothd()
102
103
104    def stop_bluetoothd(self):
105        """stop bluetoothd.
106
107        @returns: True if bluetoothd is stopped correctly.
108                  False otherwise.
109
110        """
111        return self._proxy.stop_bluetoothd()
112
113
114    def is_bluetoothd_running(self):
115        """Is bluetoothd running?
116
117        @returns: True if bluetoothd is running
118
119        """
120        return self._proxy.is_bluetoothd_running()
121
122
123    def reset_on(self):
124        """Reset the adapter and settings and power up the adapter.
125
126        @return True on success, False otherwise.
127
128        """
129        return self._proxy.reset_on()
130
131
132    def reset_off(self):
133        """Reset the adapter and settings, leave the adapter powered off.
134
135        @return True on success, False otherwise.
136
137        """
138        return self._proxy.reset_off()
139
140
141    def has_adapter(self):
142        """@return True if an adapter is present, False if not."""
143        return self._proxy.has_adapter()
144
145
146    def set_powered(self, powered):
147        """Set the adapter power state.
148
149        @param powered: adapter power state to set (True or False).
150
151        @return True on success, False otherwise.
152
153        """
154        return self._proxy.set_powered(powered)
155
156
157    def is_powered_on(self):
158        """Is the adapter powered on?
159
160        @returns: True if the adapter is powered on
161
162        """
163        properties = self.get_adapter_properties()
164        return bool(properties.get(u'Powered'))
165
166
167    def get_hci(self):
168        """Get hci of the adapter; normally, it is 'hci0'.
169
170        @returns: the hci name of the adapter.
171
172        """
173        dev_info = self.get_dev_info()
174        hci = (dev_info[1] if isinstance(dev_info, list) and
175               len(dev_info) > 1 else None)
176        return hci
177
178
179    def get_address(self):
180        """Get the bluetooth address of the adapter.
181
182        An example of the bluetooth address of the adapter: '6C:29:95:1A:D4:6F'
183
184        @returns: the bluetooth address of the adapter.
185
186        """
187        return self.address
188
189
190    def get_bluez_version(self):
191        """Get bluez version.
192
193        An exmaple of bluez version: 'BlueZ 5.39'
194
195        @returns: the bluez version
196
197        """
198        return self.bluez_version
199
200
201    def get_bluetooth_class(self):
202        """Get the bluetooth class of the adapter.
203
204        An example of the bluetooth class of a chromebook: 4718852
205
206        @returns: the bluetooth class.
207
208        """
209        return self.bluetooth_class
210
211
212    def get_UUIDs(self):
213        """Get the UUIDs.
214
215        An example of UUIDs:
216            [u'00001112-0000-1000-8000-00805f9b34fb',
217             u'00001801-0000-1000-8000-00805f9b34fb',
218             u'0000110a-0000-1000-8000-00805f9b34fb',
219             u'0000111f-0000-1000-8000-00805f9b34fb',
220             u'00001200-0000-1000-8000-00805f9b34fb',
221             u'00001800-0000-1000-8000-00805f9b34fb']
222
223        @returns: the list of the UUIDs.
224
225        """
226        return self.UUIDs
227
228
229    def set_discoverable(self, discoverable):
230        """Set the adapter discoverable state.
231
232        @param discoverable: adapter discoverable state to set (True or False).
233
234        @return True on success, False otherwise.
235
236        """
237        return self._proxy.set_discoverable(discoverable)
238
239
240    def is_discoverable(self):
241        """Is the adapter in the discoverable state?
242
243        @return True if discoverable. False otherwise.
244
245        """
246        properties = self.get_adapter_properties()
247        return properties.get('Discoverable') == 1
248
249
250    def set_discoverable_timeout(self, discoverable_timeout):
251        """Set the adapter DiscoverableTimeout.
252
253        @param discoverable_timeout: adapter DiscoverableTimeout
254                value to set in seconds (Integer).
255
256        @return True on success, False otherwise.
257
258        """
259        return self._proxy.set_discoverable_timeout(discoverable_timeout)
260
261    def get_discoverable_timeout(self):
262        """Get the adapter DiscoverableTimeout.
263
264        @return Value of property DiscoverableTimeout in seconds (Integer).
265
266        """
267        return self._proxy.get_discoverable_timeout()
268
269    def set_pairable_timeout(self, pairable_timeout):
270        """Set the adapter PairableTimeout.
271
272        @param pairable_timeout: adapter PairableTimeout
273                value to set in seconds (Integer).
274
275        @return True on success, False otherwise.
276
277        """
278        return self._proxy.set_pairable_timeout(pairable_timeout)
279
280    def get_pairable_timeout(self):
281        """Get the adapter PairableTimeout.
282
283        @return Value of property PairableTimeout in seconds (Integer).
284
285        """
286        return self._proxy.get_pairable_timeout()
287
288
289    def set_pairable(self, pairable):
290        """Set the adapter pairable state.
291
292        @param pairable: adapter pairable state to set (True or False).
293
294        @return True on success, False otherwise.
295
296        """
297        return self._proxy.set_pairable(pairable)
298
299
300    def is_pairable(self):
301        """Is the adapter in the pairable state?
302
303        @return True if pairable. False otherwise.
304
305        """
306        properties = self.get_adapter_properties()
307        return properties.get('Pairable') == 1
308
309
310    def get_adapter_properties(self):
311        """Read the adapter properties from the Bluetooth Daemon.
312
313        An example of the adapter properties looks like
314        {u'Name': u'BlueZ 5.35',
315         u'Alias': u'Chromebook',
316         u'Modalias': u'bluetooth:v00E0p2436d0400',
317         u'Powered': 1,
318         u'DiscoverableTimeout': 180,
319         u'PairableTimeout': 0,
320         u'Discoverable': 0,
321         u'Address': u'6C:29:95:1A:D4:6F',
322         u'Discovering': 0,
323         u'Pairable': 1,
324         u'Class': 4718852,
325         u'UUIDs': [u'00001112-0000-1000-8000-00805f9b34fb',
326                    u'00001801-0000-1000-8000-00805f9b34fb',
327                    u'0000110a-0000-1000-8000-00805f9b34fb',
328                    u'0000111f-0000-1000-8000-00805f9b34fb',
329                    u'00001200-0000-1000-8000-00805f9b34fb',
330                    u'00001800-0000-1000-8000-00805f9b34fb']}
331
332        @return the properties as a dictionary on success,
333            the value False otherwise.
334
335        """
336        return json.loads(self._proxy.get_adapter_properties())
337
338
339    def read_version(self):
340        """Read the version of the management interface from the Kernel.
341
342        @return the version as a tuple of:
343          ( version, revision )
344
345        """
346        return json.loads(self._proxy.read_version())
347
348
349    def read_supported_commands(self):
350        """Read the set of supported commands from the Kernel.
351
352        @return set of supported commands as arrays in a tuple of:
353          ( commands, events )
354
355        """
356        return json.loads(self._proxy.read_supported_commands())
357
358
359    def read_index_list(self):
360        """Read the list of currently known controllers from the Kernel.
361
362        @return array of controller indexes.
363
364        """
365        return json.loads(self._proxy.read_index_list())
366
367
368    def read_info(self):
369        """Read the adapter information from the Kernel.
370
371        An example of the adapter information looks like
372        [u'6C:29:95:1A:D4:6F', 6, 2, 65535, 2769, 4718852, u'Chromebook', u'']
373
374        @return the information as a tuple of:
375          ( address, bluetooth_version, manufacturer_id,
376            supported_settings, current_settings, class_of_device,
377            name, short_name )
378
379        """
380        return json.loads(self._proxy.read_info())
381
382
383    def add_device(self, address, address_type, action):
384        """Add a device to the Kernel action list.
385
386        @param address: Address of the device to add.
387        @param address_type: Type of device in @address.
388        @param action: Action to take.
389
390        @return tuple of ( address, address_type ) on success,
391          None on failure.
392
393        """
394        return json.loads(self._proxy.add_device(address, address_type, action))
395
396
397    def remove_device(self, address, address_type):
398        """Remove a device from the Kernel action list.
399
400        @param address: Address of the device to remove.
401        @param address_type: Type of device in @address.
402
403        @return tuple of ( address, address_type ) on success,
404          None on failure.
405
406        """
407        return json.loads(self._proxy.remove_device(address, address_type))
408
409    def _decode_json_base64(self, data):
410        """Load serialized JSON and then base64 decode it
411
412        Required to handle non-ascii data
413        @param data: data to be JSON and base64 decode
414
415        @return : JSON and base64 decoded date
416
417
418        """
419        logging.debug("_decode_json_base64 raw data is %s", data)
420        json_encoded = json.loads(data)
421        logging.debug("JSON encoded data is %s", json_encoded)
422        base64_decoded = utils.base64_recursive_decode(json_encoded)
423        logging.debug("base64 decoded data is %s", base64_decoded)
424        return base64_decoded
425
426    def get_devices(self):
427        """Read information about remote devices known to the adapter.
428
429        An example of the device information of RN-42 looks like
430        [{u'Name': u'RNBT-A96F',
431          u'Alias': u'RNBT-A96F',
432          u'Adapter': u'/org/bluez/hci0',
433          u'LegacyPairing': 0,
434          u'Paired': 1,
435          u'Connected': 0,
436          u'UUIDs': [u'00001124-0000-1000-8000-00805f9b34fb'],
437          u'Address': u'00:06:66:75:A9:6F',
438          u'Icon': u'input-mouse',
439          u'Class': 1408,
440          u'Trusted': 1,
441          u'Blocked': 0}]
442
443        @return the properties of each device as an array of
444            dictionaries on success, the value False otherwise.
445
446        """
447        encoded_devices = self._proxy.get_devices()
448        return self._decode_json_base64(encoded_devices)
449
450
451    def get_device_property(self, address, prop_name):
452        """Read a property of BT device by directly querying device dbus object
453
454        @param address: Address of the device to query
455        @param prop_name: Property to be queried
456
457        @return The property if device is found and has property, None otherwise
458        """
459
460        prop_val = self._proxy.get_device_property(address, prop_name)
461
462        # Handle dbus error case returned by xmlrpc_server.dbus_safe decorator
463        if prop_val is None:
464            return prop_val
465
466        # Decode and return property value
467        return self._decode_json_base64(prop_val)
468
469
470    def start_discovery(self):
471        """Start discovery of remote devices.
472
473        Obtain the discovered device information using get_devices(), called
474        stop_discovery() when done.
475
476        @return (True, None) on success, (False, <error>) otherwise.
477
478        """
479        return self._proxy.start_discovery()
480
481
482    def stop_discovery(self):
483        """Stop discovery of remote devices.
484
485        @return (True, None) on success, (False, <error>) otherwise.
486
487        """
488        return self._proxy.stop_discovery()
489
490    def pause_discovery(self, system_suspend_resume=False):
491        """ Pause discovery of remote devices
492
493        @params: boolean system_suspend_resume Is this request related to
494                 system suspend resume.
495
496        @return (True, None) on success (False, <error>) otherwise
497        """
498        return self._proxy.pause_discovery(system_suspend_resume)
499
500    def unpause_discovery(self, system_suspend_resume=False):
501        """ Unpause discovery of remote devices
502
503        @params: boolean system_suspend_resume Is this request related to
504                 system suspend resume.
505
506        @return (True, None) on success (False, <error>) otherwise
507        """
508        return self._proxy.unpause_discovery(system_suspend_resume)
509
510
511    def pause_discovery(self, system_suspend_resume=False):
512        """Pause discovery of remote devices.
513
514        This pauses all device discovery sessions.
515
516        @param system_suspend_resume: whether the
517               request is related to system suspend/resume.
518
519        @return True on success, False otherwise.
520
521        """
522        return self._proxy.pause_discovery(system_suspend_resume)
523
524
525    def unpause_discovery(self, system_suspend_resume=False):
526        """Unpause discovery of remote devices.
527
528        This unpauses all device discovery sessions.
529
530        @param system_suspend_resume: whether the
531               request is related to system suspend/resume.
532
533        @return True on success, False otherwise.
534
535        """
536        return self._proxy.unpause_discovery(system_suspend_resume)
537
538
539    def is_discovering(self):
540        """Is it discovering?
541
542        @return True if it is discovering. False otherwise.
543
544        """
545        return self.get_adapter_properties().get('Discovering') == 1
546
547
548    def get_dev_info(self):
549        """Read raw HCI device information.
550
551        An example of the device information looks like:
552        [0, u'hci0', u'6C:29:95:1A:D4:6F', 13, 0, 1, 581900950526, 52472, 7,
553         32768, 1021, 5, 96, 6, 0, 0, 151, 151, 0, 0, 0, 0, 1968, 12507]
554
555        @return tuple of (index, name, address, flags, device_type, bus_type,
556                       features, pkt_type, link_policy, link_mode,
557                       acl_mtu, acl_pkts, sco_mtu, sco_pkts,
558                       err_rx, err_tx, cmd_tx, evt_rx, acl_tx, acl_rx,
559                       sco_tx, sco_rx, byte_rx, byte_tx) on success,
560                None on failure.
561
562        """
563        return json.loads(self._proxy.get_dev_info())
564
565    def get_supported_capabilities(self):
566        """ Get the supported_capabilities of the adapter
567        @returns (capabilities,None) on success (None, <error>) on failure
568        """
569        return self._proxy.get_supported_capabilities()
570
571    def register_profile(self, path, uuid, options):
572        """Register new profile (service).
573
574        @param path: Path to the profile object.
575        @param uuid: Service Class ID of the service as string.
576        @param options: Dictionary of options for the new service, compliant
577                        with BlueZ D-Bus Profile API standard.
578
579        @return True on success, False otherwise.
580
581        """
582        return self._proxy.register_profile(path, uuid, options)
583
584
585    def has_device(self, address):
586        """Checks if the device with a given address exists.
587
588        @param address: Address of the device.
589
590        @returns: True if there is a device with that address.
591                  False otherwise.
592
593        """
594        return self._proxy.has_device(address)
595
596
597    def device_is_paired(self, address):
598        """Checks if a device is paired.
599
600        @param address: address of the device.
601
602        @returns: True if device is paired. False otherwise.
603
604        """
605        return self._proxy.device_is_paired(address)
606
607
608    def device_services_resolved(self, address):
609        """Checks if services are resolved for a device.
610
611        @param address: address of the device.
612
613        @returns: True if services are resolved. False otherwise.
614
615        """
616        return self._proxy.device_services_resolved(address)
617
618
619    def set_trusted(self, address, trusted=True):
620        """Set the device trusted.
621
622        @param address: The bluetooth address of the device.
623        @param trusted: True or False indicating whether to set trusted or not.
624
625        @returns: True if successful. False otherwise.
626
627        """
628        return self._proxy.set_trusted(address, trusted)
629
630
631    def pair_legacy_device(self, address, pin, trusted, timeout):
632        """Pairs a device with a given pin code.
633
634        Registers an agent who handles pin code request and
635        pairs a device with known pin code.
636
637        @param address: Address of the device to pair.
638        @param pin: The pin code of the device to pair.
639        @param trusted: indicating whether to set the device trusted.
640        @param timeout: The timeout in seconds for pairing.
641
642        @returns: True on success. False otherwise.
643
644        """
645        return self._proxy.pair_legacy_device(address, pin, trusted, timeout)
646
647
648    def remove_device_object(self, address):
649        """Removes a device object and the pairing information.
650
651        Calls RemoveDevice method to remove remote device
652        object and the pairing information.
653
654        @param address: address of the device to unpair.
655
656        @returns: True on success. False otherwise.
657
658        """
659        return self._proxy.remove_device_object(address)
660
661
662    def connect_device(self, address):
663        """Connects a device.
664
665        Connects a device if it is not connected.
666
667        @param address: Address of the device to connect.
668
669        @returns: True on success. False otherwise.
670
671        """
672        return self._proxy.connect_device(address)
673
674
675    def device_is_connected(self, address):
676        """Checks if a device is connected.
677
678        @param address: Address of the device to check if it is connected.
679
680        @returns: True if device is connected. False otherwise.
681
682        """
683        return self._proxy.device_is_connected(address)
684
685
686    def disconnect_device(self, address):
687        """Disconnects a device.
688
689        Disconnects a device if it is connected.
690
691        @param address: Address of the device to disconnect.
692
693        @returns: True on success. False otherwise.
694
695        """
696        return self._proxy.disconnect_device(address)
697
698
699    def btmon_start(self):
700        """Start btmon monitoring."""
701        self._proxy.btmon_start()
702
703
704    def btmon_stop(self):
705        """Stop btmon monitoring."""
706        self._proxy.btmon_stop()
707
708
709    def btmon_get(self, search_str='', start_str=''):
710        """Get btmon output contents.
711
712        @param search_str: only lines with search_str would be kept.
713        @param start_str: all lines before the occurrence of start_str would be
714                filtered.
715
716        @returns: the recorded btmon output.
717
718        """
719        return self._proxy.btmon_get(search_str, start_str)
720
721
722    def btmon_find(self, pattern_str):
723        """Find if a pattern string exists in btmon output.
724
725        @param pattern_str: the pattern string to find.
726
727        @returns: True on success. False otherwise.
728
729        """
730        return self._proxy.btmon_find(pattern_str)
731
732
733    def register_advertisement(self, advertisement_data):
734        """Register an advertisement.
735
736        Note that rpc supports only conformable types. Hence, a
737        dict about the advertisement is passed as a parameter such
738        that the advertisement object could be contructed on the host.
739
740        @param advertisement_data: a dict of the advertisement for
741                                   the adapter to register.
742
743        @returns: True on success. False otherwise.
744
745        """
746        return self._proxy.register_advertisement(advertisement_data)
747
748
749    def unregister_advertisement(self, advertisement_data):
750        """Unregister an advertisement.
751
752        @param advertisement_data: a dict of the advertisement to unregister.
753
754        @returns: True on success. False otherwise.
755
756        """
757        return self._proxy.unregister_advertisement(advertisement_data)
758
759
760    def set_advertising_intervals(self, min_adv_interval_ms,
761                                  max_adv_interval_ms):
762        """Set advertising intervals.
763
764        @param min_adv_interval_ms: the min advertising interval in ms.
765        @param max_adv_interval_ms: the max advertising interval in ms.
766
767        @returns: True on success. False otherwise.
768
769        """
770        return self._proxy.set_advertising_intervals(min_adv_interval_ms,
771                                                     max_adv_interval_ms)
772
773
774    def reset_advertising(self):
775        """Reset advertising.
776
777        This includes unregister all advertisements, reset advertising
778        intervals, and disable advertising.
779
780        @returns: True on success. False otherwise.
781
782        """
783        return self._proxy.reset_advertising()
784
785
786    def read_characteristic(self, uuid, address):
787        """Reads the value of a gatt characteristic.
788
789        Reads the current value of a gatt characteristic.
790
791        @param uuid: The uuid of the characteristic to read, as a string.
792        @param address: The MAC address of the remote device.
793
794        @returns: A byte array containing the value of the if the uuid/address
795                      was found in the object tree.
796                  None if the uuid/address was not found in the object tree, or
797                      if a DBus exception was raised by the read operation.
798
799        """
800        value = self._proxy.read_characteristic(uuid, address)
801        if value is None:
802            return None
803        return bytearray(base64.standard_b64decode(value))
804
805
806    def write_characteristic(self, uuid, address, bytes_to_write):
807        """Performs a write operation on a gatt characteristic.
808
809        Writes to a GATT characteristic on a remote device.
810
811        @param uuid: The uuid of the characteristic to write to, as a string.
812        @param address: The MAC address of the remote device, as a string.
813        @param bytes_to_write: A byte array containing the data to write.
814
815        @returns: True if the write operation does not raise an exception.
816                  None if the uuid/address was not found in the object tree, or
817                      if a DBus exception was raised by the write operation.
818
819        """
820        return self._proxy.write_characteristic(
821            uuid, address, base64.standard_b64encode(bytes_to_write))
822
823
824    def start_notify(self, address, uuid, cccd_value):
825        """Starts the notification session on the gatt characteristic.
826
827        @param address: The MAC address of the remote device.
828        @param uuid: The uuid of the characteristic.
829        @param cccd_value: Possible CCCD values include
830               0x00 - inferred from the remote characteristic's properties
831               0x01 - notification
832               0x02 - indication
833
834        @returns: True if the operation succeeds.
835                  False if the characteristic is not found, or
836                      if a DBus exception was raised by the operation.
837
838        """
839        return self._proxy.start_notify(address, uuid, cccd_value)
840
841
842    def stop_notify(self, address, uuid):
843        """Stops the notification session on the gatt characteristic.
844
845        @param address: The MAC address of the remote device.
846        @param uuid: The uuid of the characteristic.
847
848        @returns: True if the operation succeeds.
849                  False if the characteristic is not found, or
850                      if a DBus exception was raised by the operation.
851
852        """
853        return self._proxy.stop_notify(address, uuid)
854
855
856    def is_notifying(self, address, uuid):
857        """Is the GATT characteristic in a notifying session?
858
859        @param address: The MAC address of the remote device.
860        @param uuid: The uuid of the characteristic.
861
862        @return True if it is in a notification session. False otherwise.
863
864        """
865        return self._proxy.is_notifying(address, uuid)
866
867
868    def is_characteristic_path_resolved(self, uuid, address):
869        """Checks whether a characteristic is in the object tree.
870
871        Checks whether a characteristic is curently found in the object tree.
872
873        @param uuid: The uuid of the characteristic to search for.
874        @param address: The MAC address of the device on which to search for
875            the characteristic.
876
877        @returns: True if the characteristic is found, False otherwise.
878
879        """
880        return self._proxy.is_characteristic_path_resolved(uuid, address)
881
882
883    def get_gatt_attributes_map(self, address):
884        """Return a JSON formated string of the GATT attributes of a device,
885        keyed by UUID
886        @param address: a string of the MAC address of the device
887
888        @return: JSON formated string, stored the nested structure of the
889        attributes. Each attribute has 'path' and ['chrcs' | 'descs'], which
890        store their object path and children respectively.
891        """
892        return self._proxy.get_gatt_attributes_map(address)
893
894
895    def get_gatt_service_property(self, object_path, property_name):
896        """Get property from a service attribute
897        @param object_path: a string of the object path of the service
898        @param property_name: a string of a property, ex: 'Value', 'UUID'
899
900        @return: the property if success,
901                 None otherwise
902        """
903        return self._proxy.get_gatt_service_property(object_path, property_name)
904
905
906    def get_gatt_characteristic_property(self, object_path, property_name):
907        """Get property from a characteristic attribute
908        @param object_path: a string of the object path of the characteristic
909        @param property_name: a string of a property, ex: 'Value', 'UUID'
910
911        @return: the property if success,
912                 None otherwise
913        """
914        return self._proxy.get_gatt_characteristic_property(object_path,
915                                                            property_name)
916
917
918    def get_gatt_descriptor_property(self, object_path, property_name):
919        """Get property from a descriptor attribute
920        @param object_path: a string of the object path of the descriptor
921        @param property_name: a string of a property, ex: 'Value', 'UUID'
922
923        @return: the property if success,
924                 None otherwise
925        """
926        return self._proxy.get_gatt_descriptor_property(object_path,
927                                                        property_name)
928
929
930    def gatt_characteristic_read_value(self, uuid, object_path):
931        """Perform method ReadValue on a characteristic attribute
932        @param uuid: a string of uuid
933        @param object_path: a string of the object path of the characteristic
934
935        @return: base64 string of dbus bytearray
936        """
937        return self._proxy.gatt_characteristic_read_value(uuid, object_path)
938
939
940    def gatt_descriptor_read_value(self, uuid, object_path):
941        """Perform method ReadValue on a descriptor attribute
942        @param uuid: a string of uuid
943        @param object_path: a string of the object path of the descriptor
944
945        @return: base64 string of dbus bytearray
946        """
947        return self._proxy.gatt_descriptor_read_value(uuid, object_path)
948
949
950    def copy_logs(self, destination):
951        """Copy the logs generated by this device to a given location.
952
953        @param destination: destination directory for the logs.
954
955        """
956        self.host.collect_logs(self.XMLRPC_LOG_PATH, destination)
957
958
959    def get_connection_info(self, address):
960        """Get device connection info.
961
962        @param address: The MAC address of the device.
963
964        @returns: On success, a tuple of:
965                      ( RSSI, transmit_power, max_transmit_power )
966                  None otherwise.
967
968        """
969        return self._proxy.get_connection_info(address)
970
971
972    def set_discovery_filter(self, filter):
973        """Set the discovery filter.
974
975        @param filter: The discovery filter to set.
976
977        @return True on success, False otherwise.
978
979        """
980        return self._proxy.set_discovery_filter(filter)
981
982
983    def set_le_connection_parameters(self, address, parameters):
984        """Set the LE connection parameters.
985
986        @param address: The MAC address of the device.
987        @param parameters: The LE connection parameters to set.
988
989        @return: True on success. False otherwise.
990
991        """
992        return self._proxy.set_le_connection_parameters(address, parameters)
993
994
995    def close(self, close_host=True):
996        """Tear down state associated with the client.
997
998        @param close_host: If True, shut down the xml rpc server by closing the
999            underlying host object (which also shuts down all other xml rpc
1000            servers running on the DUT). Otherwise, only shut down the
1001            bluetooth device xml rpc server, which can be desirable if the host
1002            object and/or other xml rpc servers need to be used afterwards.
1003        """
1004        # Turn off the discoverable flag since it may affect future tests.
1005        self._proxy.set_discoverable(False)
1006        # Reset the adapter and leave it on.
1007        self._proxy.reset_on()
1008        # This kills the RPC server.
1009        if close_host:
1010          self.host.close()
1011        else:
1012          self.host.rpc_server_tracker.disconnect(
1013              constants.BLUETOOTH_DEVICE_XMLRPC_SERVER_PORT)
1014