• 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
5from __future__ import absolute_import
6
7import base64
8import functools
9import json
10import logging
11import threading
12from datetime import datetime
13
14import common
15from autotest_lib.client.bin import utils
16from autotest_lib.client.cros import constants
17from autotest_lib.server import autotest
18
19def proxy_thread_safe(method):
20    """A decorator enabling thread-safe XmlRpc calls"""
21
22    @functools.wraps(method)
23    def wrapper(self, *args, **kwargs):
24        """A wrapper of the decorated method"""
25        with self._proxy_lock:
26            return method(self, *args, **kwargs)
27
28    return wrapper
29
30
31class BluetoothDevice(object):
32    """BluetoothDevice is a thin layer of logic over a remote DUT.
33
34    The Autotest host object representing the remote DUT, passed to this
35    class on initialization, can be accessed from its host property.
36
37    """
38
39    XMLRPC_BRINGUP_TIMEOUT_SECONDS = 60
40    XMLRPC_LOG_PATH = '/var/log/bluetooth_xmlrpc_device.log'
41    XMLRPC_REQUEST_TIMEOUT_SECONDS = 180
42
43    # We currently get dates back in string format due to some inconsistencies
44    # between python2 and python3. This is the standard date format we use.
45    NATIVE_DATE_FORMAT = '%Y-%m-%d %H:%M:%S.%f'
46
47    def __init__(self, device_host, remote_facade_proxy=None):
48        """Construct a BluetoothDevice.
49
50        @param device_host: host object representing a remote host.
51
52        """
53        self.host = device_host
54        self._remote_proxy = remote_facade_proxy
55
56        # Make sure the client library is on the device so that the proxy code
57        # is there when we try to call it.
58        client_at = autotest.Autotest(self.host)
59        client_at.install()
60        self._proxy_lock = threading.Lock()
61
62        # If remote facade wasn't already created, connect directly here
63        if not self._remote_proxy:
64            self._connect_xmlrpc_directly()
65
66        # Get some static information about the bluetooth adapter.
67        properties = self.get_adapter_properties()
68        self.bluez_version = properties.get('Name')
69        self.address = properties.get('Address')
70        self.bluetooth_class = properties.get('Class')
71        self.UUIDs = properties.get('UUIDs')
72
73    def _connect_xmlrpc_directly(self):
74        """Connects to the bluetooth native facade directly via xmlrpc."""
75        proxy = self.host.rpc_server_tracker.xmlrpc_connect(
76                constants.BLUETOOTH_DEVICE_XMLRPC_SERVER_COMMAND,
77                constants.BLUETOOTH_DEVICE_XMLRPC_SERVER_PORT,
78                command_name=constants.
79                BLUETOOTH_DEVICE_XMLRPC_SERVER_CLEANUP_PATTERN,
80                ready_test_name=constants.
81                BLUETOOTH_DEVICE_XMLRPC_SERVER_READY_METHOD,
82                timeout_seconds=self.XMLRPC_BRINGUP_TIMEOUT_SECONDS,
83                logfile=self.XMLRPC_LOG_PATH,
84                request_timeout_seconds=self.XMLRPC_REQUEST_TIMEOUT_SECONDS)
85
86        self._bt_direct_proxy = proxy
87        return proxy
88
89    @property
90    def _proxy(self):
91        """Gets the proxy to the DUT bluetooth facade.
92
93        @return XML RPC proxy to DUT bluetooth facade.
94
95        """
96        # When the xmlrpc server is already created (using the
97        # RemoteFacadeFactory), we will use the BluetoothNativeFacade inside the
98        # remote proxy. Otherwise, we will use the xmlrpc server started from
99        # this class. Currently, there are a few users outside of the Bluetooth
100        # autotests that use this and this can be removed once those users
101        # migrate to using the RemoteFacadeFactory to generate the xmlrpc
102        # connection.
103        if self._remote_proxy:
104            return self._remote_proxy.bluetooth
105        else:
106            return self._bt_direct_proxy
107
108    @proxy_thread_safe
109    def set_debug_log_levels(self, dispatcher_vb, newblue_vb, bluez_vb,
110                             kernel_vb):
111        """Enable or disable the debug logs of bluetooth
112
113        @param dispatcher_vb: verbosity of btdispatcher debug log, either 0 or 1
114        @param newblue_vb: verbosity of newblued debug log, either 0 or 1
115        @param bluez_vb: verbosity of bluez debug log, either 0 or 1
116        @param kernel_vb: verbosity of kernel debug log, either 0 or 1
117
118        """
119        return self._proxy.set_debug_log_levels(dispatcher_vb, newblue_vb,
120                                                bluez_vb, kernel_vb)
121
122    @proxy_thread_safe
123    def log_message(self, msg, dut=True, peer=True):
124        """ Log a message in DUT log and peer logs with timestamp.
125
126        @param msg: message to be logged.
127        @param dut: log message on DUT
128        @param peer: log message on peer devices
129        """
130        try:
131            # TODO(b/146671469) Implement logging to tester
132
133            date =  datetime.strftime(datetime.now(),"%Y:%m:%d %H:%M:%S:%f")
134            msg = "bluetooth autotest --- %s : %s ---" % (date, msg)
135            logging.debug("Broadcasting '%s'",msg)
136
137            if dut:
138                self._proxy.log_message(msg)
139
140            if peer:
141                for btpeer in self.host.btpeer_list:
142                    btpeer.log_message(msg)
143        except Exception as e:
144            logging.error("Exception '%s' in log_message '%s'", str(e), msg)
145
146
147    @proxy_thread_safe
148    def is_wrt_supported(self):
149        """ Check if Bluetooth adapter support WRT logs.
150
151        Intel adapter support WRT (except of WP2 and StP2)
152
153        @returns: True if adapter support WRT logs
154        """
155        return self._proxy.is_wrt_supported()
156
157
158    @proxy_thread_safe
159    def enable_wrt_logs(self):
160        """Enable wrt logs on Intel adapters."""
161        return self._proxy.enable_wrt_logs()
162
163
164    @proxy_thread_safe
165    def collect_wrt_logs(self):
166        """Collect wrt logs on Intel adapters."""
167        return self._proxy.collect_wrt_logs()
168
169
170    @proxy_thread_safe
171    def start_bluetoothd(self):
172        """start bluetoothd.
173
174        @returns: True if bluetoothd is started correctly.
175                  False otherwise.
176
177        """
178        return self._proxy.start_bluetoothd()
179
180
181    @proxy_thread_safe
182    def stop_bluetoothd(self):
183        """stop bluetoothd.
184
185        @returns: True if bluetoothd is stopped correctly.
186                  False otherwise.
187
188        """
189        return self._proxy.stop_bluetoothd()
190
191
192    @proxy_thread_safe
193    def is_bluetoothd_running(self):
194        """Is bluetoothd running?
195
196        @returns: True if bluetoothd is running
197
198        """
199        return self._proxy.is_bluetoothd_running()
200
201
202    @proxy_thread_safe
203    def is_bluetoothd_valid(self):
204        """Checks whether the current bluetoothd session is ok.
205
206        Returns:
207            True if the current bluetoothd session is ok. False if bluetoothd is
208            not running or it is a new session.
209        """
210        return self._proxy.is_bluetoothd_proxy_valid()
211
212
213    @proxy_thread_safe
214    def reset_on(self):
215        """Reset the adapter and settings and power up the adapter.
216
217        @return True on success, False otherwise.
218
219        """
220        return self._proxy.reset_on()
221
222
223    @proxy_thread_safe
224    def reset_off(self):
225        """Reset the adapter and settings, leave the adapter powered off.
226
227        @return True on success, False otherwise.
228
229        """
230        return self._proxy.reset_off()
231
232
233    @proxy_thread_safe
234    def has_adapter(self):
235        """@return True if an adapter is present, False if not."""
236        return self._proxy.has_adapter()
237
238
239    @proxy_thread_safe
240    def is_wake_enabled(self):
241        """@return True if adapter is wake enabled, False if not."""
242        return self._proxy.is_wake_enabled()
243
244
245    @proxy_thread_safe
246    def set_wake_enabled(self, value):
247        """ Sets the power/wakeup value for the adapter.
248
249        Args:
250            value: Whether the adapter can wake from suspend
251
252        @return True if able to set it to value, False if not."""
253        return self._proxy.set_wake_enabled(value)
254
255
256    @proxy_thread_safe
257    def set_powered(self, powered):
258        """Set the adapter power state.
259
260        @param powered: adapter power state to set (True or False).
261
262        @return True on success, False otherwise.
263
264        """
265        return self._proxy.set_powered(powered)
266
267
268    def is_powered_on(self):
269        """Is the adapter powered on?
270
271        @returns: True if the adapter is powered on
272
273        """
274        properties = self.get_adapter_properties()
275        return bool(properties.get(u'Powered'))
276
277
278    def get_hci(self):
279        """Get hci of the adapter; normally, it is 'hci0'.
280
281        @returns: the hci name of the adapter.
282
283        """
284        dev_info = self.get_dev_info()
285        hci = (dev_info[1] if isinstance(dev_info, list) and
286               len(dev_info) > 1 else None)
287        return hci
288
289
290    def get_address(self):
291        """Get the bluetooth address of the adapter.
292
293        An example of the bluetooth address of the adapter: '6C:29:95:1A:D4:6F'
294
295        @returns: the bluetooth address of the adapter.
296
297        """
298        return self.address
299
300
301    def get_bluez_version(self):
302        """Get bluez version.
303
304        An exmaple of bluez version: 'BlueZ 5.39'
305
306        @returns: the bluez version
307
308        """
309        return self.bluez_version
310
311
312    def get_bluetooth_class(self):
313        """Get the bluetooth class of the adapter.
314
315        An example of the bluetooth class of a chromebook: 4718852
316
317        @returns: the bluetooth class.
318
319        """
320        return self.bluetooth_class
321
322
323    def get_UUIDs(self):
324        """Get the UUIDs.
325
326        An example of UUIDs:
327            [u'00001112-0000-1000-8000-00805f9b34fb',
328             u'00001801-0000-1000-8000-00805f9b34fb',
329             u'0000110a-0000-1000-8000-00805f9b34fb',
330             u'0000111f-0000-1000-8000-00805f9b34fb',
331             u'00001200-0000-1000-8000-00805f9b34fb',
332             u'00001800-0000-1000-8000-00805f9b34fb']
333
334        @returns: the list of the UUIDs.
335
336        """
337        return self.UUIDs
338
339
340    @proxy_thread_safe
341    def set_discoverable(self, discoverable):
342        """Set the adapter discoverable state.
343
344        @param discoverable: adapter discoverable state to set (True or False).
345
346        @return True on success, False otherwise.
347
348        """
349        return self._proxy.set_discoverable(discoverable)
350
351
352    def is_discoverable(self):
353        """Is the adapter in the discoverable state?
354
355        @return True if discoverable. False otherwise.
356
357        """
358        properties = self.get_adapter_properties()
359        return properties.get('Discoverable') == 1
360
361
362    @proxy_thread_safe
363    def set_discoverable_timeout(self, discoverable_timeout):
364        """Set the adapter DiscoverableTimeout.
365
366        @param discoverable_timeout: adapter DiscoverableTimeout
367                value to set in seconds (Integer).
368
369        @return True on success, False otherwise.
370
371        """
372        return self._proxy.set_discoverable_timeout(discoverable_timeout)
373
374
375    @proxy_thread_safe
376    def get_discoverable_timeout(self):
377        """Get the adapter DiscoverableTimeout.
378
379        @return Value of property DiscoverableTimeout in seconds (Integer).
380
381        """
382        return self._proxy.get_discoverable_timeout()
383
384
385    @proxy_thread_safe
386    def set_pairable_timeout(self, pairable_timeout):
387        """Set the adapter PairableTimeout.
388
389        @param pairable_timeout: adapter PairableTimeout
390                value to set in seconds (Integer).
391
392        @return True on success, False otherwise.
393
394        """
395        return self._proxy.set_pairable_timeout(pairable_timeout)
396
397
398    @proxy_thread_safe
399    def get_pairable_timeout(self):
400        """Get the adapter PairableTimeout.
401
402        @return Value of property PairableTimeout in seconds (Integer).
403
404        """
405        return self._proxy.get_pairable_timeout()
406
407
408    @proxy_thread_safe
409    def set_pairable(self, pairable):
410        """Set the adapter pairable state.
411
412        @param pairable: adapter pairable state to set (True or False).
413
414        @return True on success, False otherwise.
415
416        """
417        return self._proxy.set_pairable(pairable)
418
419
420    def is_pairable(self):
421        """Is the adapter in the pairable state?
422
423        @return True if pairable. False otherwise.
424
425        """
426        properties = self.get_adapter_properties()
427        return properties.get('Pairable') == 1
428
429    @proxy_thread_safe
430    def set_adapter_alias(self, alias):
431        """Set the adapter alias.
432
433        A note on Alias property - providing an empty string ('') will reset the
434        Alias property to the system default
435
436        @param alias: adapter alias to set with type String
437
438        @return True on success, False otherwise.
439        """
440
441        return self._proxy.set_adapter_alias(alias)
442
443    @proxy_thread_safe
444    def get_adapter_properties(self):
445        """Read the adapter properties from the Bluetooth Daemon.
446
447        An example of the adapter properties looks like
448        {u'Name': u'BlueZ 5.35',
449         u'Alias': u'Chromebook',
450         u'Modalias': u'bluetooth:v00E0p2436d0400',
451         u'Powered': 1,
452         u'DiscoverableTimeout': 180,
453         u'PairableTimeout': 0,
454         u'Discoverable': 0,
455         u'Address': u'6C:29:95:1A:D4:6F',
456         u'Discovering': 0,
457         u'Pairable': 1,
458         u'Class': 4718852,
459         u'UUIDs': [u'00001112-0000-1000-8000-00805f9b34fb',
460                    u'00001801-0000-1000-8000-00805f9b34fb',
461                    u'0000110a-0000-1000-8000-00805f9b34fb',
462                    u'0000111f-0000-1000-8000-00805f9b34fb',
463                    u'00001200-0000-1000-8000-00805f9b34fb',
464                    u'00001800-0000-1000-8000-00805f9b34fb']}
465
466        @return the properties as a dictionary on success,
467            the value False otherwise.
468
469        """
470        return json.loads(self._proxy.get_adapter_properties())
471
472
473    @proxy_thread_safe
474    def read_version(self):
475        """Read the version of the management interface from the Kernel.
476
477        @return the version as a tuple of:
478          ( version, revision )
479
480        """
481        return json.loads(self._proxy.read_version())
482
483
484    @proxy_thread_safe
485    def read_supported_commands(self):
486        """Read the set of supported commands from the Kernel.
487
488        @return set of supported commands as arrays in a tuple of:
489          ( commands, events )
490
491        """
492        return json.loads(self._proxy.read_supported_commands())
493
494
495    @proxy_thread_safe
496    def read_index_list(self):
497        """Read the list of currently known controllers from the Kernel.
498
499        @return array of controller indexes.
500
501        """
502        return json.loads(self._proxy.read_index_list())
503
504
505    @proxy_thread_safe
506    def read_info(self):
507        """Read the adapter information from the Kernel.
508
509        An example of the adapter information looks like
510        [u'6C:29:95:1A:D4:6F', 6, 2, 65535, 2769, 4718852, u'Chromebook', u'']
511
512        @return the information as a tuple of:
513          ( address, bluetooth_version, manufacturer_id,
514            supported_settings, current_settings, class_of_device,
515            name, short_name )
516
517        """
518        return json.loads(self._proxy.read_info())
519
520
521    @proxy_thread_safe
522    def add_device(self, address, address_type, action):
523        """Add a device to the Kernel action list.
524
525        @param address: Address of the device to add.
526        @param address_type: Type of device in @address.
527        @param action: Action to take.
528
529        @return tuple of ( address, address_type ) on success,
530          None on failure.
531
532        """
533        return json.loads(self._proxy.add_device(address, address_type, action))
534
535
536    @proxy_thread_safe
537    def remove_device(self, address, address_type):
538        """Remove a device from the Kernel action list.
539
540        @param address: Address of the device to remove.
541        @param address_type: Type of device in @address.
542
543        @return tuple of ( address, address_type ) on success,
544          None on failure.
545
546        """
547        return json.loads(self._proxy.remove_device(address, address_type))
548
549    def _decode_json_base64(self, data):
550        """Load serialized JSON and then base64 decode it
551
552        Required to handle non-ascii data
553        @param data: data to be JSON and base64 decode
554
555        @return : JSON and base64 decoded date
556
557
558        """
559        logging.debug("_decode_json_base64 raw data is %s", data)
560        json_encoded = json.loads(data)
561        logging.debug("JSON encoded data is %s", json_encoded)
562        base64_decoded = utils.base64_recursive_decode(json_encoded)
563        logging.debug("base64 decoded data is %s", base64_decoded)
564        return base64_decoded
565
566
567    @proxy_thread_safe
568    def get_devices(self):
569        """Read information about remote devices known to the adapter.
570
571        An example of the device information of RN-42 looks like
572        [{u'Name': u'RNBT-A96F',
573          u'Alias': u'RNBT-A96F',
574          u'Adapter': u'/org/bluez/hci0',
575          u'LegacyPairing': 0,
576          u'Paired': 1,
577          u'Connected': 0,
578          u'UUIDs': [u'00001124-0000-1000-8000-00805f9b34fb'],
579          u'Address': u'00:06:66:75:A9:6F',
580          u'Icon': u'input-mouse',
581          u'Class': 1408,
582          u'Trusted': 1,
583          u'Blocked': 0}]
584
585        @return the properties of each device as an array of
586            dictionaries on success, the value False otherwise.
587
588        """
589        encoded_devices = self._proxy.get_devices()
590        return self._decode_json_base64(encoded_devices)
591
592
593    @proxy_thread_safe
594    def get_device_property(self, address, prop_name):
595        """Read a property of BT device by directly querying device dbus object
596
597        @param address: Address of the device to query
598        @param prop_name: Property to be queried
599
600        @return The property if device is found and has property, None otherwise
601        """
602
603        prop_val = self._proxy.get_device_property(address, prop_name)
604
605        # Handle dbus error case returned by xmlrpc_server.dbus_safe decorator
606        if prop_val is None:
607            return prop_val
608
609        # Decode and return property value
610        return self._decode_json_base64(prop_val)
611
612
613    @proxy_thread_safe
614    def get_battery_property(self, address, prop_name):
615        """Read a property of battery by directly querying the dbus object
616
617        @param address: Address of the device to query
618        @param prop_name: Property to be queried
619
620        @return The property if battery is found and has property,
621          None otherwise
622        """
623
624        return self._proxy.get_battery_property(address, prop_name)
625
626    @proxy_thread_safe
627    def start_discovery(self):
628        """Start discovery of remote devices.
629
630        Obtain the discovered device information using get_devices(), called
631        stop_discovery() when done.
632
633        @return (True, None) on success, (False, <error>) otherwise.
634
635        """
636        return self._proxy.start_discovery()
637
638
639    @proxy_thread_safe
640    def stop_discovery(self):
641        """Stop discovery of remote devices.
642
643        @return (True, None) on success, (False, <error>) otherwise.
644
645        """
646        return self._proxy.stop_discovery()
647
648
649    def is_discovering(self):
650        """Is it discovering?
651
652        @return True if it is discovering. False otherwise.
653
654        """
655        return self.get_adapter_properties().get('Discovering') == 1
656
657
658    @proxy_thread_safe
659    def get_dev_info(self):
660        """Read raw HCI device information.
661
662        An example of the device information looks like:
663        [0, u'hci0', u'6C:29:95:1A:D4:6F', 13, 0, 1, 581900950526, 52472, 7,
664         32768, 1021, 5, 96, 6, 0, 0, 151, 151, 0, 0, 0, 0, 1968, 12507]
665
666        @return tuple of (index, name, address, flags, device_type, bus_type,
667                       features, pkt_type, link_policy, link_mode,
668                       acl_mtu, acl_pkts, sco_mtu, sco_pkts,
669                       err_rx, err_tx, cmd_tx, evt_rx, acl_tx, acl_rx,
670                       sco_tx, sco_rx, byte_rx, byte_tx) on success,
671                None on failure.
672
673        """
674        return json.loads(self._proxy.get_dev_info())
675
676
677    @proxy_thread_safe
678    def get_supported_capabilities(self):
679        """ Get the supported_capabilities of the adapter
680        @returns (capabilities,None) on success (None, <error>) on failure
681        """
682        capabilities, error = self._proxy.get_supported_capabilities()
683        return (json.loads(capabilities), error)
684
685
686    @proxy_thread_safe
687    def register_profile(self, path, uuid, options):
688        """Register new profile (service).
689
690        @param path: Path to the profile object.
691        @param uuid: Service Class ID of the service as string.
692        @param options: Dictionary of options for the new service, compliant
693                        with BlueZ D-Bus Profile API standard.
694
695        @return True on success, False otherwise.
696
697        """
698        return self._proxy.register_profile(path, uuid, options)
699
700
701    @proxy_thread_safe
702    def has_device(self, address):
703        """Checks if the device with a given address exists.
704
705        @param address: Address of the device.
706
707        @returns: True if there is a device with that address.
708                  False otherwise.
709
710        """
711        return self._proxy.has_device(address)
712
713
714    @proxy_thread_safe
715    def device_is_paired(self, address):
716        """Checks if a device is paired.
717
718        @param address: address of the device.
719
720        @returns: True if device is paired. False otherwise.
721
722        """
723        return self._proxy.device_is_paired(address)
724
725
726    @proxy_thread_safe
727    def device_services_resolved(self, address):
728        """Checks if services are resolved for a device.
729
730        @param address: address of the device.
731
732        @returns: True if services are resolved. False otherwise.
733
734        """
735        return self._proxy.device_services_resolved(address)
736
737
738    @proxy_thread_safe
739    def set_trusted(self, address, trusted=True):
740        """Set the device trusted.
741
742        @param address: The bluetooth address of the device.
743        @param trusted: True or False indicating whether to set trusted or not.
744
745        @returns: True if successful. False otherwise.
746
747        """
748        return self._proxy.set_trusted(address, trusted)
749
750
751    @proxy_thread_safe
752    def pair_legacy_device(self, address, pin, trusted, timeout):
753        """Pairs a device with a given pin code.
754
755        Registers an agent who handles pin code request and
756        pairs a device with known pin code.
757
758        @param address: Address of the device to pair.
759        @param pin: The pin code of the device to pair.
760        @param trusted: indicating whether to set the device trusted.
761        @param timeout: The timeout in seconds for pairing.
762
763        @returns: True on success. False otherwise.
764
765        """
766        return self._proxy.pair_legacy_device(address, pin, trusted, timeout)
767
768
769    @proxy_thread_safe
770    def remove_device_object(self, address):
771        """Removes a device object and the pairing information.
772
773        Calls RemoveDevice method to remove remote device
774        object and the pairing information.
775
776        @param address: address of the device to unpair.
777
778        @returns: True on success. False otherwise.
779
780        """
781        return self._proxy.remove_device_object(address)
782
783
784    @proxy_thread_safe
785    def connect_device(self, address):
786        """Connects a device.
787
788        Connects a device if it is not connected.
789
790        @param address: Address of the device to connect.
791
792        @returns: True on success. False otherwise.
793
794        """
795        return self._proxy.connect_device(address)
796
797
798    @proxy_thread_safe
799    def device_is_connected(self, address):
800        """Checks if a device is connected.
801
802        @param address: Address of the device to check if it is connected.
803
804        @returns: True if device is connected. False otherwise.
805
806        """
807        return self._proxy.device_is_connected(address)
808
809
810    @proxy_thread_safe
811    def disconnect_device(self, address):
812        """Disconnects a device.
813
814        Disconnects a device if it is connected.
815
816        @param address: Address of the device to disconnect.
817
818        @returns: True on success. False otherwise.
819
820        """
821        return self._proxy.disconnect_device(address)
822
823
824    @proxy_thread_safe
825    def btmon_start(self):
826        """Start btmon monitoring."""
827        self._proxy.btmon_start()
828
829
830    @proxy_thread_safe
831    def btmon_stop(self):
832        """Stop btmon monitoring."""
833        self._proxy.btmon_stop()
834
835
836    @proxy_thread_safe
837    def btmon_get(self, search_str='', start_str=''):
838        """Get btmon output contents.
839
840        @param search_str: only lines with search_str would be kept.
841        @param start_str: all lines before the occurrence of start_str would be
842                filtered.
843
844        @returns: the recorded btmon output.
845
846        """
847        return self._proxy.btmon_get(search_str, start_str)
848
849
850    @proxy_thread_safe
851    def btmon_find(self, pattern_str):
852        """Find if a pattern string exists in btmon output.
853
854        @param pattern_str: the pattern string to find.
855
856        @returns: True on success. False otherwise.
857
858        """
859        return self._proxy.btmon_find(pattern_str)
860
861
862    @proxy_thread_safe
863    def advmon_check_manager_interface_exist(self):
864        """Check if AdvertisementMonitorManager1 interface is available.
865
866        @returns: True if Manager interface is available, False otherwise.
867
868        """
869        return self._proxy.advmon_check_manager_interface_exist()
870
871
872    @proxy_thread_safe
873    def advmon_read_supported_types(self):
874        """Read the Advertisement Monitor supported monitor types.
875
876        @returns: List of supported advertisement monitor types.
877
878        """
879        return self._proxy.advmon_read_supported_types()
880
881
882    @proxy_thread_safe
883    def advmon_read_supported_features(self):
884        """Read the Advertisement Monitor supported features.
885
886        @returns: List of supported advertisement monitor features.
887
888        """
889        return self._proxy.advmon_read_supported_features()
890
891
892    @proxy_thread_safe
893    def advmon_create_app(self):
894        """Create an advertisement monitor app.
895
896        @returns: app id, once the app is created.
897
898        """
899        return self._proxy.advmon_create_app()
900
901
902    @proxy_thread_safe
903    def advmon_exit_app(self, app_id):
904        """Exit an advertisement monitor app.
905
906        @param app_id: the app id.
907
908        @returns: True on success, False otherwise.
909
910        """
911        return self._proxy.advmon_exit_app(app_id)
912
913
914    @proxy_thread_safe
915    def advmon_kill_app(self, app_id):
916        """Kill an advertisement monitor app by sending SIGKILL.
917
918        @param app_id: the app id.
919
920        @returns: True on success, False otherwise.
921
922        """
923        return self._proxy.advmon_kill_app(app_id)
924
925
926    @proxy_thread_safe
927    def advmon_register_app(self, app_id):
928        """Register an advertisement monitor app.
929
930        @param app_id: the app id.
931
932        @returns: True on success, False otherwise.
933
934        """
935        return self._proxy.advmon_register_app(app_id)
936
937
938    @proxy_thread_safe
939    def advmon_unregister_app(self, app_id):
940        """Unregister an advertisement monitor app.
941
942        @param app_id: the app id.
943
944        @returns: True on success, False otherwise.
945
946        """
947        return self._proxy.advmon_unregister_app(app_id)
948
949
950    @proxy_thread_safe
951    def advmon_add_monitor(self, app_id, monitor_data):
952        """Create an Advertisement Monitor object.
953
954        @param app_id: the app id.
955        @param monitor_data: the list containing monitor type, RSSI filter
956                             values and patterns.
957
958        @returns: monitor id, once the monitor is created, None otherwise.
959
960        """
961        return self._proxy.advmon_add_monitor(app_id, monitor_data)
962
963
964    @proxy_thread_safe
965    def advmon_remove_monitor(self, app_id, monitor_id):
966        """Remove the Advertisement Monitor object.
967
968        @param app_id: the app id.
969        @param monitor_id: the monitor id.
970
971        @returns: True on success, False otherwise.
972
973        """
974        return self._proxy.advmon_remove_monitor(app_id, monitor_id)
975
976
977    @proxy_thread_safe
978    def advmon_get_event_count(self, app_id, monitor_id, event):
979        """Read the count of a particular event on the given monitor.
980
981        @param app_id: the app id.
982        @param monitor_id: the monitor id.
983        @param event: name of the specific event or 'All' for all events.
984
985        @returns: count of the specific event or dict of counts of all events.
986
987        """
988        return self._proxy.advmon_get_event_count(app_id, monitor_id, event)
989
990
991    @proxy_thread_safe
992    def advmon_reset_event_count(self, app_id, monitor_id, event):
993        """Reset the count of a particular event on the given monitor.
994
995        @param app_id: the app id.
996        @param monitor_id: the monitor id.
997        @param event: name of the specific event or 'All' for all events.
998
999        @returns: True on success, False otherwise.
1000
1001        """
1002        return self._proxy.advmon_reset_event_count(app_id, monitor_id, event)
1003
1004    @proxy_thread_safe
1005    def advmon_interleave_scan_logger_start(self):
1006        """ Start interleave logger recording
1007        """
1008        self._proxy.advmon_interleave_scan_logger_start()
1009
1010    @proxy_thread_safe
1011    def advmon_interleave_scan_logger_stop(self):
1012        """ Stop interleave logger recording
1013
1014        @returns: True if logs were successfully collected,
1015                  False otherwise.
1016
1017        """
1018        return self._proxy.advmon_interleave_scan_logger_stop()
1019
1020    @proxy_thread_safe
1021    def advmon_interleave_scan_logger_get_records(self):
1022        """ Get records in previous log collections
1023
1024        @returns: a list of records, where each item is a record of
1025                  interleave |state| and the |time| the state starts.
1026                  |state| could be {'no filter', 'allowlist'}
1027                  |time| is system time in sec
1028
1029        """
1030        return self._proxy.advmon_interleave_scan_logger_get_records()
1031
1032    @proxy_thread_safe
1033    def advmon_interleave_scan_logger_get_cancel_events(self):
1034        """ Get cancel events in previous log collections
1035
1036        @returns: a list of cancel |time| when a interleave cancel event log
1037                  was found.
1038                  |time| is system time in sec
1039
1040        """
1041        return self._proxy.advmon_interleave_scan_logger_get_cancel_events()
1042
1043    @proxy_thread_safe
1044    def messages_start(self):
1045        """Start messages monitoring."""
1046        self._proxy.messages_start()
1047
1048    @proxy_thread_safe
1049    def messages_stop(self):
1050        """Stop messages monitoring.
1051
1052        @returns: True if logs were successfully gathered since logging started,
1053                else False
1054        """
1055        return self._proxy.messages_stop()
1056
1057    @proxy_thread_safe
1058    def messages_find(self, pattern_str):
1059        """Find if a pattern string exists in messages output.
1060
1061        @param pattern_str: the pattern string to find.
1062
1063        @returns: True on success. False otherwise.
1064
1065        """
1066        return self._proxy.messages_find(pattern_str)
1067
1068    @proxy_thread_safe
1069    def register_advertisement(self, advertisement_data):
1070        """Register an advertisement.
1071
1072        Note that rpc supports only conformable types. Hence, a
1073        dict about the advertisement is passed as a parameter such
1074        that the advertisement object could be contructed on the host.
1075
1076        @param advertisement_data: a dict of the advertisement for
1077                                   the adapter to register.
1078
1079        @returns: True on success. False otherwise.
1080
1081        """
1082        return self._proxy.register_advertisement(advertisement_data)
1083
1084
1085    @proxy_thread_safe
1086    def unregister_advertisement(self, advertisement_data):
1087        """Unregister an advertisement.
1088
1089        @param advertisement_data: a dict of the advertisement to unregister.
1090
1091        @returns: True on success. False otherwise.
1092
1093        """
1094        return self._proxy.unregister_advertisement(advertisement_data)
1095
1096
1097    @proxy_thread_safe
1098    def set_advertising_intervals(self, min_adv_interval_ms,
1099                                  max_adv_interval_ms):
1100        """Set advertising intervals.
1101
1102        @param min_adv_interval_ms: the min advertising interval in ms.
1103        @param max_adv_interval_ms: the max advertising interval in ms.
1104
1105        @returns: True on success. False otherwise.
1106
1107        """
1108        return self._proxy.set_advertising_intervals(min_adv_interval_ms,
1109                                                     max_adv_interval_ms)
1110
1111
1112    @proxy_thread_safe
1113    def reset_advertising(self):
1114        """Reset advertising.
1115
1116        This includes unregister all advertisements, reset advertising
1117        intervals, and disable advertising.
1118
1119        @returns: True on success. False otherwise.
1120
1121        """
1122        return self._proxy.reset_advertising()
1123
1124
1125    @proxy_thread_safe
1126    def create_audio_record_directory(self, audio_record_dir):
1127        """Create the audio recording directory.
1128
1129        @param audio_record_dir: the audio recording directory
1130
1131        @returns: True on success. False otherwise.
1132        """
1133        return self._proxy.create_audio_record_directory(audio_record_dir)
1134
1135
1136    @proxy_thread_safe
1137    def start_capturing_audio_subprocess(self, audio_data, recording_device):
1138        """Start capturing audio in a subprocess.
1139
1140        @param audio_data: the audio test data
1141        @param recording_device: which device recorded the audio,
1142                possible values are 'recorded_by_dut' or 'recorded_by_peer'
1143
1144        @returns: True on success. False otherwise.
1145        """
1146        return self._proxy.start_capturing_audio_subprocess(
1147                json.dumps(audio_data), recording_device)
1148
1149
1150    @proxy_thread_safe
1151    def stop_capturing_audio_subprocess(self):
1152        """Stop capturing audio.
1153
1154        @returns: True on success. False otherwise.
1155        """
1156        return self._proxy.stop_capturing_audio_subprocess()
1157
1158
1159    @proxy_thread_safe
1160    def start_playing_audio_subprocess(self, audio_data):
1161        """Start playing audio in a subprocess.
1162
1163        @param audio_data: the audio test data
1164
1165        @returns: True on success. False otherwise.
1166        """
1167        audio_data = json.dumps(audio_data)
1168        return self._proxy.start_playing_audio_subprocess(audio_data)
1169
1170
1171    @proxy_thread_safe
1172    def stop_playing_audio_subprocess(self):
1173        """Stop playing audio in the subprocess.
1174
1175        @returns: True on success. False otherwise.
1176        """
1177        return self._proxy.stop_playing_audio_subprocess()
1178
1179
1180    @proxy_thread_safe
1181    def play_audio(self, audio_data):
1182        """Play audio.
1183
1184        It blocks until it has completed playing back the audio.
1185
1186        @param audio_data: the audio test data
1187
1188        @returns: True on success. False otherwise.
1189        """
1190        return self._proxy.play_audio(json.dumps(audio_data))
1191
1192
1193    @proxy_thread_safe
1194    def check_audio_frames_legitimacy(self, audio_test_data, recording_device,
1195                                      recorded_file):
1196        """Get the number of frames in the recorded audio file.
1197        @param audio_test_data: the audio test data
1198        @param recording_device: which device recorded the audio,
1199                possible values are 'recorded_by_dut' or 'recorded_by_peer'
1200        @param recorded_file: the recorded file name
1201
1202        @returns: True if audio frames are legitimate.
1203        """
1204        return self._proxy.check_audio_frames_legitimacy(
1205                json.dumps(audio_test_data), recording_device, recorded_file)
1206
1207
1208    @proxy_thread_safe
1209    def convert_audio_sample_rate(self, input_file, out_file, test_data,
1210                                  new_rate):
1211        """Convert audio file to new sample rate.
1212
1213        @param input_file: Path to file to upsample.
1214        @param out_file: Path to create upsampled file.
1215        @param test_data: Dictionary with information about file.
1216        @param new_rate: New rate to upsample file to.
1217
1218        @returns: True if upsampling succeeded, False otherwise.
1219        """
1220        return self._proxy.convert_audio_sample_rate(input_file, out_file,
1221                                                     json.dumps(test_data),
1222                                                     new_rate)
1223
1224
1225    @proxy_thread_safe
1226    def trim_wav_file(self, in_file, out_file, new_duration, test_data,
1227                      tolerance=0.1):
1228        """Trim long file to desired length.
1229
1230        Trims audio file to length by cutting out silence from beginning and
1231        end.
1232
1233        @param in_file: Path to audio file to be trimmed.
1234        @param out_file: Path to trimmed audio file to create.
1235        @param new_duration: A float representing the desired duration of
1236                the resulting trimmed file.
1237        @param test_data: Dictionary containing information about the test file.
1238        @param tolerance: (optional) A float representing the allowable
1239                difference between trimmed file length and desired duration
1240
1241        @returns: True if file was trimmed successfully, False otherwise.
1242        """
1243        return self._proxy.trim_wav_file(in_file, out_file, new_duration,
1244                                         json.dumps(test_data), tolerance)
1245
1246
1247    @proxy_thread_safe
1248    def unzip_audio_test_data(self, tar_path, data_dir):
1249        """Unzip audio test data files.
1250
1251        @param tar_path: Path to audio test data tarball on DUT.
1252        @oaram data_dir: Path to directory where to extract test data directory.
1253
1254        @returns: True if audio test data folder exists, False otherwise.
1255        """
1256        return self._proxy.unzip_audio_test_data(tar_path, data_dir)
1257
1258
1259    @proxy_thread_safe
1260    def convert_raw_to_wav(self, input_file, output_file, test_data):
1261        """Convert raw audio file to wav file.
1262
1263        @oaram input_file: The location of the raw file.
1264        @param output_file: The location to place the resulting wav file.
1265        @param test_data: The data for the file being converted.
1266
1267        @returns: True if conversion was successful, otherwise false.
1268        """
1269        return self._proxy.convert_raw_to_wav(input_file, output_file,
1270                                              json.dumps(test_data))
1271
1272
1273    @proxy_thread_safe
1274    def get_primary_frequencies(self, audio_test_data, recording_device,
1275                                recorded_file):
1276        """Get primary frequencies of the audio test file.
1277
1278        @param audio_test_data: the audio test data
1279        @param recording_device: which device recorded the audio,
1280                possible values are 'recorded_by_dut' or 'recorded_by_peer'
1281        @param recorded_file: the recorded file name
1282
1283        @returns: a list of primary frequencies of channels in the audio file
1284        """
1285        return self._proxy.get_primary_frequencies(
1286                json.dumps(audio_test_data), recording_device, recorded_file)
1287
1288
1289    @proxy_thread_safe
1290    def enable_wbs(self, value):
1291        """Enable or disable wideband speech (wbs) per the value.
1292
1293        @param value: True to enable wbs.
1294
1295        @returns: True if the operation succeeds.
1296        """
1297        logging.debug('%s wbs', 'enable' if value else 'disable')
1298        return self._proxy.enable_wbs(value)
1299
1300
1301    @proxy_thread_safe
1302    def set_player_playback_status(self, status):
1303        """Set playback status for the registered media player.
1304
1305        @param status: playback status in string.
1306
1307        """
1308        logging.debug('Set media player playback status to %s', status)
1309        return self._proxy.set_player_playback_status(status)
1310
1311
1312    @proxy_thread_safe
1313    def set_player_position(self, position):
1314        """Set media position for the registered media player.
1315
1316        @param position: position in micro seconds.
1317
1318        """
1319        logging.debug('Set media player position to %d', position)
1320        return self._proxy.set_player_position(position)
1321
1322
1323    @proxy_thread_safe
1324    def set_player_metadata(self, metadata):
1325        """Set metadata for the registered media player.
1326
1327        @param metadata: dictionary of media metadata.
1328
1329        """
1330        logging.debug('Set media player album:%s artist:%s title:%s',
1331                      metadata.get("album"), metadata.get("artist"),
1332                      metadata.get("title"))
1333        return self._proxy.set_player_metadata(metadata)
1334
1335
1336    @proxy_thread_safe
1337    def set_player_length(self, length):
1338        """Set media length for the registered media player.
1339
1340        @param length: length in micro seconds.
1341
1342        """
1343        logging.debug('Set media player length to %d', length)
1344        return self._proxy.set_player_length(length)
1345
1346
1347    @proxy_thread_safe
1348    def select_input_device(self, device_name):
1349        """Select the audio input device.
1350
1351        @param device_name: the name of the Bluetooth peer device
1352
1353        @returns: True if the operation succeeds.
1354        """
1355        return self._proxy.select_input_device(device_name)
1356
1357
1358    @proxy_thread_safe
1359    def select_output_node(self, node_type):
1360        """Select the audio output node.
1361
1362        @param node_type: the node type of the Bluetooth peer device
1363
1364        @returns: True if the operation succeeds.
1365        """
1366        return self._proxy.select_output_node(node_type)
1367
1368
1369    @proxy_thread_safe
1370    def get_selected_output_device_type(self):
1371        """Get the selected audio output node type.
1372
1373        @returns: the node type of the selected output device.
1374        """
1375        return self._proxy.get_selected_output_device_type()
1376
1377
1378    @proxy_thread_safe
1379    def read_characteristic(self, uuid, address):
1380        """Reads the value of a gatt characteristic.
1381
1382        Reads the current value of a gatt characteristic.
1383
1384        @param uuid: The uuid of the characteristic to read, as a string.
1385        @param address: The MAC address of the remote device.
1386
1387        @returns: A byte array containing the value of the if the uuid/address
1388                      was found in the object tree.
1389                  None if the uuid/address was not found in the object tree, or
1390                      if a DBus exception was raised by the read operation.
1391
1392        """
1393        value = self._proxy.read_characteristic(uuid, address)
1394        if value is None:
1395            return None
1396        return bytearray(base64.standard_b64decode(value))
1397
1398
1399    @proxy_thread_safe
1400    def write_characteristic(self, uuid, address, bytes_to_write):
1401        """Performs a write operation on a gatt characteristic.
1402
1403        Writes to a GATT characteristic on a remote device.
1404
1405        @param uuid: The uuid of the characteristic to write to, as a string.
1406        @param address: The MAC address of the remote device, as a string.
1407        @param bytes_to_write: A byte array containing the data to write.
1408
1409        @returns: True if the write operation does not raise an exception.
1410                  None if the uuid/address was not found in the object tree, or
1411                      if a DBus exception was raised by the write operation.
1412
1413        """
1414        return self._proxy.write_characteristic(
1415            uuid, address, base64.standard_b64encode(bytes_to_write))
1416
1417
1418    @proxy_thread_safe
1419    def exchange_messages(self, tx_object_path, rx_object_path, bytes_to_write):
1420        """Performs a write operation on a gatt characteristic and wait for
1421        the response on another characteristic.
1422
1423        @param tx_object_path: the object path of the characteristic to write.
1424        @param rx_object_path: the object path of the characteristic to read.
1425        @param value: A byte array containing the data to write.
1426
1427        @returns: The value of the characteristic to read from.
1428                  None if the uuid/address was not found in the object tree, or
1429                      if a DBus exception was raised by the write operation.
1430
1431        """
1432        return self._proxy.exchange_messages(
1433            tx_object_path, rx_object_path,
1434            base64.standard_b64encode(bytes_to_write))
1435
1436
1437    @proxy_thread_safe
1438    def start_notify(self, object_path, cccd_value):
1439        """Starts the notification session on the gatt characteristic.
1440
1441        @param object_path: the object path of the characteristic.
1442        @param cccd_value: Possible CCCD values include
1443               0x00 - inferred from the remote characteristic's properties
1444               0x01 - notification
1445               0x02 - indication
1446
1447        @returns: True if the operation succeeds.
1448                  False if the characteristic is not found, or
1449                      if a DBus exception was raised by the operation.
1450
1451        """
1452        return self._proxy.start_notify(object_path, cccd_value)
1453
1454
1455    @proxy_thread_safe
1456    def stop_notify(self, object_path):
1457        """Stops the notification session on the gatt characteristic.
1458
1459        @param object_path: the object path of the characteristic.
1460
1461        @returns: True if the operation succeeds.
1462                  False if the characteristic is not found, or
1463                      if a DBus exception was raised by the operation.
1464
1465        """
1466        return self._proxy.stop_notify(object_path)
1467
1468
1469    @proxy_thread_safe
1470    def is_notifying(self, object_path):
1471        """Is the GATT characteristic in a notifying session?
1472
1473        @param object_path: the object path of the characteristic.
1474
1475        @return True if it is in a notification session. False otherwise.
1476
1477        """
1478        return self._proxy.is_notifying(object_path)
1479
1480
1481    @proxy_thread_safe
1482    def is_characteristic_path_resolved(self, uuid, address):
1483        """Checks whether a characteristic is in the object tree.
1484
1485        Checks whether a characteristic is curently found in the object tree.
1486
1487        @param uuid: The uuid of the characteristic to search for.
1488        @param address: The MAC address of the device on which to search for
1489            the characteristic.
1490
1491        @returns: True if the characteristic is found, False otherwise.
1492
1493        """
1494        return self._proxy.is_characteristic_path_resolved(uuid, address)
1495
1496
1497    @proxy_thread_safe
1498    def get_gatt_attributes_map(self, address):
1499        """Return a JSON formated string of the GATT attributes of a device,
1500        keyed by UUID
1501        @param address: a string of the MAC address of the device
1502
1503        @return: JSON formated string, stored the nested structure of the
1504        attributes. Each attribute has 'path' and ['chrcs' | 'descs'], which
1505        store their object path and children respectively.
1506        """
1507        return self._proxy.get_gatt_attributes_map(address)
1508
1509
1510    @proxy_thread_safe
1511    def get_gatt_service_property(self, object_path, property_name):
1512        """Get property from a service attribute
1513        @param object_path: a string of the object path of the service
1514        @param property_name: a string of a property, ex: 'Value', 'UUID'
1515
1516        @return: the property if success,
1517                 None otherwise
1518        """
1519        return self._proxy.get_gatt_service_property(object_path, property_name)
1520
1521
1522    @proxy_thread_safe
1523    def get_gatt_characteristic_property(self, object_path, property_name):
1524        """Get property from a characteristic attribute
1525        @param object_path: a string of the object path of the characteristic
1526        @param property_name: a string of a property, ex: 'Value', 'UUID'
1527
1528        @return: the property if success,
1529                 None otherwise
1530        """
1531        return self._proxy.get_gatt_characteristic_property(object_path,
1532                                                            property_name)
1533
1534
1535    @proxy_thread_safe
1536    def get_gatt_descriptor_property(self, object_path, property_name):
1537        """Get property from a descriptor attribute
1538        @param object_path: a string of the object path of the descriptor
1539        @param property_name: a string of a property, ex: 'Value', 'UUID'
1540
1541        @return: the property if success,
1542                 None otherwise
1543        """
1544        return self._proxy.get_gatt_descriptor_property(object_path,
1545                                                        property_name)
1546
1547
1548    @proxy_thread_safe
1549    def gatt_characteristic_read_value(self, uuid, object_path):
1550        """Perform method ReadValue on a characteristic attribute
1551        @param uuid: a string of uuid
1552        @param object_path: a string of the object path of the characteristic
1553
1554        @return: base64 string of dbus bytearray
1555        """
1556        return self._proxy.gatt_characteristic_read_value(uuid, object_path)
1557
1558
1559    @proxy_thread_safe
1560    def gatt_descriptor_read_value(self, uuid, object_path):
1561        """Perform method ReadValue on a descriptor attribute
1562        @param uuid: a string of uuid
1563        @param object_path: a string of the object path of the descriptor
1564
1565        @return: base64 string of dbus bytearray
1566        """
1567        return self._proxy.gatt_descriptor_read_value(uuid, object_path)
1568
1569
1570    @proxy_thread_safe
1571    def get_gatt_object_path(self, address, uuid):
1572        """Get property from a characteristic attribute
1573
1574        @param address: The MAC address of the remote device.
1575        @param uuid: The uuid of the attribute.
1576
1577        @return: the object path of the attribute if success,
1578                 none otherwise
1579
1580        """
1581        return self._proxy.get_gatt_object_path(address, uuid)
1582
1583
1584    def copy_logs(self, destination):
1585        """Copy the logs generated by this device to a given location.
1586
1587        @param destination: destination directory for the logs.
1588
1589        """
1590        self.host.collect_logs(self.XMLRPC_LOG_PATH, destination)
1591
1592
1593    @proxy_thread_safe
1594    def get_connection_info(self, address):
1595        """Get device connection info.
1596
1597        @param address: The MAC address of the device.
1598
1599        @returns: On success, a tuple of:
1600                      ( RSSI, transmit_power, max_transmit_power )
1601                  None otherwise.
1602
1603        """
1604        return self._proxy.get_connection_info(address)
1605
1606
1607    @proxy_thread_safe
1608    def set_discovery_filter(self, filter):
1609        """Set the discovery filter.
1610
1611        @param filter: The discovery filter to set.
1612
1613        @return True on success, False otherwise.
1614
1615        """
1616        return self._proxy.set_discovery_filter(filter)
1617
1618
1619    @proxy_thread_safe
1620    def set_le_connection_parameters(self, address, parameters):
1621        """Set the LE connection parameters.
1622
1623        @param address: The MAC address of the device.
1624        @param parameters: The LE connection parameters to set.
1625
1626        @return: True on success. False otherwise.
1627
1628        """
1629        return self._proxy.set_le_connection_parameters(address, parameters)
1630
1631
1632    @proxy_thread_safe
1633    def wait_for_hid_device(self, device_address):
1634        """Wait for hid device with given device address.
1635
1636        Args:
1637            device_address: Peripheral Address
1638
1639        Returns:
1640            True if hid device is found.
1641        """
1642        return self._proxy.wait_for_hid_device(device_address)
1643
1644
1645    @proxy_thread_safe
1646    def bt_caused_last_resume(self):
1647        """Checks if last resume from suspend was caused by bluetooth
1648
1649        @return: True if BT wake path was cause of resume, False otherwise
1650        """
1651
1652        return self._proxy.bt_caused_last_resume()
1653
1654    @proxy_thread_safe
1655    def find_last_suspend_via_powerd_logs(self):
1656        """Finds the last suspend attempt via powerd logs.
1657
1658        @return: Tuple (suspend start time, suspend end time, suspend result) or
1659                 None
1660        """
1661        info = self._proxy.find_last_suspend_via_powerd_logs()
1662
1663        # Currently, we get the date back in string format due to python2/3
1664        # inconsistencies. We can get rid of this once everything is running
1665        # python3 (hopefully)
1666        # TODO - Revisit converting date to string and back in this method
1667        if info:
1668            start_date = datetime.strptime(info[0], self.NATIVE_DATE_FORMAT)
1669            end_date = datetime.strptime(info[1], self.NATIVE_DATE_FORMAT)
1670            ret = info[2]
1671
1672            return (start_date, end_date, ret)
1673
1674        return None
1675
1676    @proxy_thread_safe
1677    def do_suspend(self, seconds, expect_bt_wake):
1678        """Suspend DUT using the power manager.
1679
1680        @param seconds: The number of seconds to suspend the device.
1681        @param expect_bt_wake: Whether we expect bluetooth to wake us from
1682            suspend. If true, we expect this resume will occur early
1683        """
1684
1685        # Do not retry this RPC if it fails or times out
1686        return self._proxy.do_suspend(seconds, expect_bt_wake, __no_retry=True)
1687
1688
1689    @proxy_thread_safe
1690    def get_wlan_vid_pid(self):
1691        """ Return vendor id and product id of the wlan chip on BT/WiFi module
1692
1693        @returns: (vid,pid) on success; (None,None) on failure
1694        """
1695        return self._proxy.get_wlan_vid_pid()
1696
1697    @proxy_thread_safe
1698    def get_bt_module_name(self):
1699        """ Return bluetooth module name for non-USB devices
1700
1701        @returns: Name of the Bluetooth module (or string read from device on
1702                  success); '' on failure
1703        """
1704        return self._proxy.get_bt_module_name()
1705
1706    @proxy_thread_safe
1707    def get_device_time(self):
1708        """ Get the current device time. """
1709        return datetime.strptime(self._proxy.get_device_time(),
1710                                 self.NATIVE_DATE_FORMAT)
1711
1712    @proxy_thread_safe
1713    def close(self, close_host=True):
1714        """Tear down state associated with the client.
1715
1716        @param close_host: If True, shut down the xml rpc server by closing the
1717            underlying host object (which also shuts down all other xml rpc
1718            servers running on the DUT). Otherwise, only shut down the
1719            bluetooth device xml rpc server, which can be desirable if the host
1720            object and/or other xml rpc servers need to be used afterwards.
1721        """
1722        # Turn off the discoverable flag since it may affect future tests.
1723        self._proxy.set_discoverable(False)
1724        # Reset the adapter and leave it on.
1725        self._proxy.reset_on()
1726        # This kills the RPC server.
1727        if close_host:
1728            self.host.close()
1729        elif self._bt_direct_proxy:
1730            self.host.rpc_server_tracker.disconnect(
1731                    constants.BLUETOOTH_DEVICE_XMLRPC_SERVER_PORT)
1732