1#!/usr/bin/env python3
2#
3#   Copyright 2019 - The Android Open Source Project
4#
5#   Licensed under the Apache License, Version 2.0 (the "License");
6#   you may not use this file except in compliance with the License.
7#   You may obtain a copy of the License at
8#
9#       http://www.apache.org/licenses/LICENSE-2.0
10#
11#   Unless required by applicable law or agreed to in writing, software
12#   distributed under the License is distributed on an "AS IS" BASIS,
13#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14#   See the License for the specific language governing permissions and
15#   limitations under the License.
16
17import inspect
18import logging
19
20from queue import Empty
21
22from acts.controllers.android_device import AndroidDevice
23from acts.controllers.fuchsia_device import FuchsiaDevice
24from acts.test_utils.bt.bt_constants import ble_scan_settings_modes
25from acts.test_utils.bt.bt_constants import gatt_cb_strings
26from acts.test_utils.bt.bt_constants import gatt_event
27from acts.test_utils.bt.bt_constants import scan_result
28from acts.test_utils.bt.bt_gatt_utils import GattTestUtilsError
29from acts.test_utils.bt.bt_gatt_utils import disconnect_gatt_connection
30from acts.test_utils.bt.bt_gatt_utils import setup_gatt_connection
31from acts.test_utils.fuchsia.bt_test_utils import le_scan_for_device_by_name
32
33import acts.test_utils.bt.bt_test_utils as bt_test_utils
34
35
36def create_bluetooth_device(hardware_device):
37    """Creates a generic Bluetooth device based on type of device that is sent
38    to the functions.
39
40    Args:
41        hardware_device: A Bluetooth hardware device that is supported by ACTS.
42    """
43    if isinstance(hardware_device, FuchsiaDevice):
44        return FuchsiaBluetoothDevice(hardware_device)
45    elif isinstance(hardware_device, AndroidDevice):
46        return AndroidBluetoothDevice(hardware_device)
47    else:
48        raise ValueError('Unable to create BluetoothDevice for type %s' %
49                         type(hardware_device))
50
51
52class BluetoothDevice(object):
53    """Class representing a generic Bluetooth device.
54
55    Each object of this class represents a generic Bluetooth device.
56    Android device and Fuchsia devices are the currently supported devices.
57
58    Attributes:
59        device: A generic Bluetooth device.
60    """
61    def __init__(self, device):
62        self.device = device
63        self.log = logging
64
65    def a2dp_initiate_open_stream(self):
66        """Base generic Bluetooth interface. Only called if not overridden by
67        another supported device.
68        """
69        raise NotImplementedError("{} must be defined.".format(
70            inspect.currentframe().f_code.co_name))
71
72    def start_profile_a2dp_sink(self):
73        """Base generic Bluetooth interface. Only called if not overridden by
74        another supported device.
75        """
76        raise NotImplementedError("{} must be defined.".format(
77            inspect.currentframe().f_code.co_name))
78
79    def stop_profile_a2dp_sink(self):
80        """Base generic Bluetooth interface. Only called if not overridden by
81        another supported device.
82        """
83        raise NotImplementedError("{} must be defined.".format(
84            inspect.currentframe().f_code.co_name))
85
86    def start_pairing_helper(self):
87        """Base generic Bluetooth interface. Only called if not overridden by
88        another supported device.
89        """
90        raise NotImplementedError("{} must be defined.".format(
91            inspect.currentframe().f_code.co_name))
92
93    def set_discoverable(self, is_discoverable):
94        """Base generic Bluetooth interface. Only called if not overridden by
95        another supported device.
96        """
97        raise NotImplementedError("{} must be defined.".format(
98            inspect.currentframe().f_code.co_name))
99
100    def bluetooth_toggle_state(self, state):
101        """Base generic Bluetooth interface. Only called if not overridden by
102        another supported device.
103        """
104        raise NotImplementedError("{} must be defined.".format(
105            inspect.currentframe().f_code.co_name))
106
107    def gatt_client_discover_characteristic_by_uuid(self, peer_identifier,
108                                                    uuid):
109        """Base generic Bluetooth interface. Only called if not overridden by
110        another supported device.
111        """
112        raise NotImplementedError("{} must be defined.".format(
113            inspect.currentframe().f_code.co_name))
114
115    def initialize_bluetooth_controller(self):
116        """Base generic Bluetooth interface. Only called if not overridden by
117        another supported device.
118        """
119        raise NotImplementedError("{} must be defined.".format(
120            inspect.currentframe().f_code.co_name))
121
122    def get_pairing_pin(self):
123        """Base generic Bluetooth interface. Only called if not overridden by
124        another supported device.
125        """
126        raise NotImplementedError("{} must be defined.".format(
127            inspect.currentframe().f_code.co_name))
128
129    def input_pairing_pin(self, pin):
130        """Base generic Bluetooth interface. Only called if not overridden by
131        another supported device.
132        """
133        raise NotImplementedError("{} must be defined.".format(
134            inspect.currentframe().f_code.co_name))
135
136    def get_bluetooth_local_address(self):
137        """Base generic Bluetooth interface. Only called if not overridden by
138        another supported device.
139        """
140        raise NotImplementedError("{} must be defined.".format(
141            inspect.currentframe().f_code.co_name))
142
143    def gatt_connect(self, peer_identifier, transport, autoconnect):
144        """Base generic Bluetooth interface. Only called if not overridden by
145        another supported device.
146        """
147        raise NotImplementedError("{} must be defined.".format(
148            inspect.currentframe().f_code.co_name))
149
150    def gatt_client_write_characteristic_without_response_by_handle(
151            self, peer_identifier, handle, value):
152        """Base generic Bluetooth interface. Only called if not overridden by
153        another supported device.
154        """
155        raise NotImplementedError("{} must be defined.".format(
156            inspect.currentframe().f_code.co_name))
157
158    def gatt_client_write_characteristic_by_handle(self, peer_identifier,
159                                                   handle, offset, value):
160        """Base generic Bluetooth interface. Only called if not overridden by
161        another supported device.
162        """
163        raise NotImplementedError("{} must be defined.".format(
164            inspect.currentframe().f_code.co_name))
165
166    def gatt_client_read_characteristic_by_handle(self, peer_identifier,
167                                                  handle):
168        """Base generic Bluetooth interface. Only called if not overridden by
169        another supported device.
170        """
171        raise NotImplementedError("{} must be defined.".format(
172            inspect.currentframe().f_code.co_name))
173
174    def gatt_client_read_long_characteristic_by_handle(self, peer_identifier,
175                                                       handle, offset,
176                                                       max_bytes):
177        """Base generic Bluetooth interface. Only called if not overridden by
178        another supported device.
179        """
180        raise NotImplementedError("{} must be defined.".format(
181            inspect.currentframe().f_code.co_name))
182
183    def gatt_client_enable_notifiy_characteristic_by_handle(
184            self, peer_identifier, handle):
185        """Base generic Bluetooth interface. Only called if not overridden by
186        another supported device.
187        """
188        raise NotImplementedError("{} must be defined.".format(
189            inspect.currentframe().f_code.co_name))
190
191    def gatt_client_disable_notifiy_characteristic_by_handle(
192            self, peer_identifier, handle):
193        """Base generic Bluetooth interface. Only called if not overridden by
194        another supported device.
195        """
196        raise NotImplementedError("{} must be defined.".format(
197            inspect.currentframe().f_code.co_name))
198
199    def gatt_client_read_descriptor_by_handle(self, peer_identifier, handle):
200        """Base generic Bluetooth interface. Only called if not overridden by
201        another supported device.
202        """
203        raise NotImplementedError("{} must be defined.".format(
204            inspect.currentframe().f_code.co_name))
205
206    def gatt_client_write_descriptor_by_handle(self, peer_identifier, handle,
207                                               offset, value):
208        """Base generic Bluetooth interface. Only called if not overridden by
209        another supported device.
210        """
211        raise NotImplementedError("{} must be defined.".format(
212            inspect.currentframe().f_code.co_name))
213
214    def gatt_client_long_read_descriptor_by_handle(self, peer_identifier,
215                                                   handle, offset, max_bytes):
216        """Base generic Bluetooth interface. Only called if not overridden by
217        another supported device.
218        """
219        raise NotImplementedError("{} must be defined.".format(
220            inspect.currentframe().f_code.co_name))
221
222    def gatt_disconnect(self, peer_identifier):
223        """Base generic Bluetooth interface. Only called if not overridden by
224        another supported device.
225        """
226        raise NotImplementedError("{} must be defined.".format(
227            inspect.currentframe().f_code.co_name))
228
229    def gatt_client_refresh(self, peer_identifier):
230        """Base generic Bluetooth interface. Only called if not overridden by
231        another supported device.
232        """
233        raise NotImplementedError("{} must be defined.".format(
234            inspect.currentframe().f_code.co_name))
235
236    def le_scan_with_name_filter(self, name, timeout):
237        """Base generic Bluetooth interface. Only called if not overridden by
238        another supported device.
239        """
240        raise NotImplementedError("{} must be defined.".format(
241            inspect.currentframe().f_code.co_name))
242
243    def log_info(self, log):
244        """Base generic Bluetooth interface. Only called if not overridden by
245        another supported device.
246        """
247        raise NotImplementedError("{} must be defined.".format(
248            inspect.currentframe().f_code.co_name))
249
250    def reset_bluetooth(self):
251        """Base generic Bluetooth interface. Only called if not overridden by
252        another supported device.
253        """
254        raise NotImplementedError("{} must be defined.".format(
255            inspect.currentframe().f_code.co_name))
256
257    def sdp_add_search(self, attribute_list, profile_id):
258        """Base generic Bluetooth interface. Only called if not overridden by
259        another supported device.
260        """
261        raise NotImplementedError("{} must be defined.".format(
262            inspect.currentframe().f_code.co_name))
263
264    def sdp_add_service(self, sdp_record):
265        """Base generic Bluetooth interface. Only called if not overridden by
266        another supported device.
267        """
268        raise NotImplementedError("{} must be defined.".format(
269            inspect.currentframe().f_code.co_name))
270
271    def sdp_clean_up(self):
272        """Base generic Bluetooth interface. Only called if not overridden by
273        another supported device.
274        """
275        raise NotImplementedError("{} must be defined.".format(
276            inspect.currentframe().f_code.co_name))
277
278    def sdp_init(self):
279        """Base generic Bluetooth interface. Only called if not overridden by
280        another supported device.
281        """
282        raise NotImplementedError("{} must be defined.".format(
283            inspect.currentframe().f_code.co_name))
284
285    def sdp_remove_service(self, service_id):
286        """Base generic Bluetooth interface. Only called if not overridden by
287        another supported device.
288        """
289        raise NotImplementedError("{} must be defined.".format(
290            inspect.currentframe().f_code.co_name))
291
292    def start_le_advertisement(self, adv_data, adv_interval):
293        """Base generic Bluetooth interface. Only called if not overridden by
294        another supported device.
295        """
296        raise NotImplementedError("{} must be defined.".format(
297            inspect.currentframe().f_code.co_name))
298
299    def stop_le_advertisement(self):
300        """Base generic Bluetooth interface. Only called if not overridden by
301        another supported device.
302        """
303        raise NotImplementedError("{} must be defined.".format(
304            inspect.currentframe().f_code.co_name))
305
306    def set_bluetooth_local_name(self, name):
307        """Base generic Bluetooth interface. Only called if not overridden by
308        another supported device.
309        """
310        raise NotImplementedError("{} must be defined.".format(
311            inspect.currentframe().f_code.co_name))
312
313    def setup_gatt_server(self, database):
314        """Base generic Bluetooth interface. Only called if not overridden by
315        another supported device.
316        """
317        raise NotImplementedError("{} must be defined.".format(
318            inspect.currentframe().f_code.co_name))
319
320    def close_gatt_server(self):
321        """Base generic Bluetooth interface. Only called if not overridden by
322        another supported device.
323        """
324        raise NotImplementedError("{} must be defined.".format(
325            inspect.currentframe().f_code.co_name))
326
327    def unbond_device(self, peer_identifier):
328        """Base generic Bluetooth interface. Only called if not overridden by
329        another supported device.
330        """
331        raise NotImplementedError("{} must be defined.".format(
332            inspect.currentframe().f_code.co_name))
333
334    def unbond_all_known_devices(self):
335        """Base generic Bluetooth interface. Only called if not overridden by
336        another supported device.
337        """
338        raise NotImplementedError("{} must be defined.".format(
339            inspect.currentframe().f_code.co_name))
340
341    def init_pair(self, peer_identifier, security_level, non_bondable,
342                  transport):
343        """Base generic Bluetooth interface. Only called if not overridden by
344        another supported device.
345        """
346        raise NotImplementedError("{} must be defined.".format(
347            inspect.currentframe().f_code.co_name))
348
349
350class AndroidBluetoothDevice(BluetoothDevice):
351    """Class wrapper for an Android Bluetooth device.
352
353    Each object of this class represents a generic Bluetooth device.
354    Android device and Fuchsia devices are the currently supported devices/
355
356    Attributes:
357        android_device: An Android Bluetooth device.
358    """
359    def __init__(self, android_device):
360        super().__init__(android_device)
361        self.gatt_timeout = 10
362        self.peer_mapping = {}
363        self.discovered_services_index = None
364
365    def _client_wait(self, gatt_event, gatt_callback):
366        return self._timed_pop(gatt_event, gatt_callback)
367
368    def _timed_pop(self, gatt_event, gatt_callback):
369        expected_event = gatt_event["evt"].format(gatt_callback)
370        try:
371            return self.device.ed.pop_event(expected_event, self.gatt_timeout)
372        except Empty as emp:
373            raise AssertionError(gatt_event["err"].format(expected_event))
374
375    def _setup_discovered_services_index(self, bluetooth_gatt):
376        """ Sets the discovered services index for the gatt connection
377        related to the Bluetooth GATT callback object.
378
379        Args:
380            bluetooth_gatt: The BluetoothGatt callback id
381        """
382        if not self.discovered_services_index:
383            self.device.droid.gattClientDiscoverServices(bluetooth_gatt)
384            expected_event = gatt_cb_strings['gatt_serv_disc'].format(
385                self.gatt_callback)
386            event = self.dut.ed.pop_event(expected_event, self.gatt_timeout)
387            self.discovered_services_index = event['data']['ServicesIndex']
388
389    def a2dp_initiate_open_stream(self):
390        raise NotImplementedError("{} not yet implemented.".format(
391            inspect.currentframe().f_code.co_name))
392
393    def start_profile_a2dp_sink(self):
394        raise NotImplementedError("{} not yet implemented.".format(
395            inspect.currentframe().f_code.co_name))
396
397    def stop_profile_a2dp_sink(self):
398        raise NotImplementedError("{} not yet implemented.".format(
399            inspect.currentframe().f_code.co_name))
400
401    def bluetooth_toggle_state(self, state):
402        self.device.droid.bluetoothToggleState(state)
403
404    def set_discoverable(self, is_discoverable):
405        """ Sets the device's discoverability.
406
407        Args:
408            is_discoverable: True if discoverable, false if not discoverable
409        """
410        if is_discoverable:
411            self.device.droid.bluetoothMakeDiscoverable()
412        else:
413            self.device.droid.bluetoothMakeUndiscoverable()
414
415    def initialize_bluetooth_controller(self):
416        """ Just pass for Android as there is no concept of initializing
417        a Bluetooth controller.
418        """
419        pass
420
421    def start_pairing_helper(self):
422        """ Starts the Android pairing helper.
423        """
424        self.device.droid.bluetoothStartPairingHelper(True)
425
426    def gatt_client_write_characteristic_without_response_by_handle(
427            self, peer_identifier, handle, value):
428        """ Perform a GATT Client write Characteristic without response to
429        remote peer GATT server database.
430
431        Args:
432            peer_identifier: The mac address associated with the GATT connection
433            handle: The characteristic handle (or instance id).
434            value: The list of bytes to write.
435        Returns:
436            True if success, False if failure.
437        """
438        peer_info = self.peer_mapping.get(peer_identifier)
439        if not peer_info:
440            self.log.error(
441                "Peer idenifier {} not currently connected or unknown.".format(
442                    peer_identifier))
443            return False
444        self._setup_discovered_services_index()
445        self.device.droid.gattClientWriteCharacteristicByInstanceId(
446            peer_info.get('bluetooth_gatt'), self.discovered_services_index,
447            handle, value)
448        try:
449            event = self._client_wait(gatt_event['char_write'],
450                                      peer_info.get('gatt_callback'))
451        except AssertionError as err:
452            self.log.error("Failed to write Characteristic: {}".format(err))
453        return True
454
455    def gatt_client_write_characteristic_by_handle(self, peer_identifier,
456                                                   handle, offset, value):
457        """ Perform a GATT Client write Characteristic without response to
458        remote peer GATT server database.
459
460        Args:
461            peer_identifier: The mac address associated with the GATT connection
462            handle: The characteristic handle (or instance id).
463            offset: Not used yet.
464            value: The list of bytes to write.
465        Returns:
466            True if success, False if failure.
467        """
468        peer_info = self.peer_mapping.get(peer_identifier)
469        if not peer_info:
470            self.log.error(
471                "Peer idenifier {} not currently connected or unknown.".format(
472                    peer_identifier))
473            return False
474        self._setup_discovered_services_index()
475        self.device.droid.gattClientWriteCharacteristicByInstanceId(
476            peer_info.get('bluetooth_gatt'), self.discovered_services_index,
477            handle, value)
478        try:
479            event = self._client_wait(gatt_event['char_write'],
480                                      peer_info.get('gatt_callback'))
481        except AssertionError as err:
482            self.log.error("Failed to write Characteristic: {}".format(err))
483        return True
484
485    def gatt_client_read_characteristic_by_handle(self, peer_identifier,
486                                                  handle):
487        """ Perform a GATT Client read Characteristic to remote peer GATT
488        server database.
489
490        Args:
491            peer_identifier: The mac address associated with the GATT connection
492            handle: The characteristic handle (or instance id).
493        Returns:
494            Value of Characteristic if success, None if failure.
495        """
496        peer_info = self.peer_mapping.get(peer_identifier)
497        if not peer_info:
498            self.log.error(
499                "Peer idenifier {} not currently connected or unknown.".format(
500                    peer_identifier))
501            return False
502        self._setup_discovered_services_index()
503        self.dut.droid.gattClientReadCharacteristicByInstanceId(
504            peer_info.get('bluetooth_gatt'), self.discovered_services_index,
505            handle)
506        try:
507            event = self._client_wait(gatt_event['char_read'],
508                                      peer_info.get('gatt_callback'))
509        except AssertionError as err:
510            self.log.error("Failed to read Characteristic: {}".format(err))
511
512        return event['data']['CharacteristicValue']
513
514    def gatt_client_read_long_characteristic_by_handle(self, peer_identifier,
515                                                       handle, offset,
516                                                       max_bytes):
517        """ Perform a GATT Client read Characteristic to remote peer GATT
518        server database.
519
520        Args:
521            peer_identifier: The mac address associated with the GATT connection
522            offset: Not used yet.
523            handle: The characteristic handle (or instance id).
524            max_bytes: Not used yet.
525        Returns:
526            Value of Characteristic if success, None if failure.
527        """
528        peer_info = self.peer_mapping.get(peer_identifier)
529        if not peer_info:
530            self.log.error(
531                "Peer idenifier {} not currently connected or unknown.".format(
532                    peer_identifier))
533            return False
534        self._setup_discovered_services_index()
535        self.dut.droid.gattClientReadCharacteristicByInstanceId(
536            peer_info.get('bluetooth_gatt'), self.discovered_services_index,
537            handle)
538        try:
539            event = self._client_wait(gatt_event['char_read'],
540                                      peer_info.get('gatt_callback'))
541        except AssertionError as err:
542            self.log.error("Failed to read Characteristic: {}".format(err))
543
544        return event['data']['CharacteristicValue']
545
546    def gatt_client_enable_notifiy_characteristic_by_handle(
547            self, peer_identifier, handle):
548        """ Perform a GATT Client enable Characteristic notification to remote
549        peer GATT server database.
550
551        Args:
552            peer_identifier: The mac address associated with the GATT connection
553            handle: The characteristic handle.
554        Returns:
555            True is success, False if failure.
556        """
557        raise NotImplementedError("{} not yet implemented.".format(
558            inspect.currentframe().f_code.co_name))
559
560    def gatt_client_disable_notifiy_characteristic_by_handle(
561            self, peer_identifier, handle):
562        """ Perform a GATT Client disable Characteristic notification to remote
563        peer GATT server database.
564
565        Args:
566            peer_identifier: The mac address associated with the GATT connection
567            handle: The characteristic handle.
568        Returns:
569            True is success, False if failure.
570        """
571        raise NotImplementedError("{} not yet implemented.".format(
572            inspect.currentframe().f_code.co_name))
573
574    def gatt_client_read_descriptor_by_handle(self, peer_identifier, handle):
575        """ Perform a GATT Client read Descriptor to remote peer GATT
576        server database.
577
578        Args:
579            peer_identifier: The mac address associated with the GATT connection
580            handle: The Descriptor handle (or instance id).
581        Returns:
582            Value of Descriptor if success, None if failure.
583        """
584        peer_info = self.peer_mapping.get(peer_identifier)
585        if not peer_info:
586            self.log.error(
587                "Peer idenifier {} not currently connected or unknown.".format(
588                    peer_identifier))
589            return False
590        self._setup_discovered_services_index()
591        self.dut.droid.gattClientReadDescriptorByInstanceId(
592            peer_info.get('bluetooth_gatt'), self.discovered_services_index,
593            handle)
594        try:
595            event = self._client_wait(gatt_event['desc_read'],
596                                      peer_info.get('gatt_callback'))
597        except AssertionError as err:
598            self.log.error("Failed to read Descriptor: {}".format(err))
599        # TODO: Implement sending Descriptor value in SL4A such that the data
600        # can be represented by: event['data']['DescriptorValue']
601        return ""
602
603    def gatt_client_write_descriptor_by_handle(self, peer_identifier, handle,
604                                               offset, value):
605        """ Perform a GATT Client write Descriptor to the remote peer GATT
606        server database.
607
608        Args:
609            peer_identifier: The mac address associated with the GATT connection
610            handle: The Descriptor handle (or instance id).
611            offset: Not used yet
612            value: The list of bytes to write.
613        Returns:
614            True if success, False if failure.
615        """
616        peer_info = self.peer_mapping.get(peer_identifier)
617        if not peer_info:
618            self.log.error(
619                "Peer idenifier {} not currently connected or unknown.".format(
620                    peer_identifier))
621            return False
622        self._setup_discovered_services_index()
623        self.device.droid.gattClientWriteDescriptorByInstanceId(
624            peer_info.get('bluetooth_gatt'), self.discovered_services_index,
625            handle, value)
626        try:
627            event = self._client_wait(gatt_event['desc_write'],
628                                      peer_info.get('gatt_callback'))
629        except AssertionError as err:
630            self.log.error("Failed to write Characteristic: {}".format(err))
631        return True
632
633    def gatt_connect(self, peer_identifier, transport, autoconnect=False):
634        """ Perform a GATT connection to a perihperal.
635
636        Args:
637            peer_identifier: The mac address to connect to.
638            transport: Which transport to use.
639            autoconnect: Set autocnnect to True or False.
640        Returns:
641            True if success, False if failure.
642        """
643        try:
644            bluetooth_gatt, gatt_callback = setup_gatt_connection(
645                self.device, peer_identifier, autoconnect, transport)
646            self.peer_mapping[peer_identifier] = {
647                "bluetooth_gatt": bluetooth_gatt,
648                "gatt_callback": gatt_callback
649            }
650        except GattTestUtilsError as err:
651            self.log.error(err)
652            return False
653        return True
654
655    def gatt_disconnect(self, peer_identifier):
656        """ Perform a GATT disconnect from a perihperal.
657
658        Args:
659            peer_identifier: The peer to disconnect from.
660        Returns:
661            True if success, False if failure.
662        """
663        peer_info = self.peer_mapping.get(peer_identifier)
664        if not peer_info:
665            self.log.error(
666                "No previous connections made to {}".format(peer_identifier))
667            return False
668
669        try:
670            disconnect_gatt_connection(self.device,
671                                       peer_info.get("bluetooth_gatt"),
672                                       peer_info.get("gatt_callback"))
673            self.device.droid.gattClientClose(peer_info.get("bluetooth_gatt"))
674        except GattTestUtilsError as err:
675            self.log.error(err)
676            return False
677        self.device.droid.gattClientClose(peer_info.get("bluetooth_gatt"))
678
679    def gatt_client_refresh(self, peer_identifier):
680        """ Perform a GATT Client Refresh of a perihperal.
681
682        Clears the internal cache and forces a refresh of the services from the
683        remote device.
684
685        Args:
686            peer_identifier: The peer to refresh.
687        """
688        peer_info = self.peer_mapping.get(peer_identifier)
689        if not peer_info:
690            self.log.error(
691                "No previous connections made to {}".format(peer_identifier))
692            return False
693        self.device.droid.gattClientRefresh(peer_info["bluetooth_gatt"])
694
695    def le_scan_with_name_filter(self, name, timeout):
696        """ Scan over LE for a specific device name.
697
698         Args:
699            name: The name filter to set.
700            timeout: The timeout to wait to find the advertisement.
701        Returns:
702            Discovered mac address or None
703        """
704        self.device.droid.bleSetScanSettingsScanMode(
705            ble_scan_settings_modes['low_latency'])
706        filter_list = self.device.droid.bleGenFilterList()
707        scan_settings = self.device.droid.bleBuildScanSetting()
708        scan_callback = self.device.droid.bleGenScanCallback()
709        self.device.droid.bleSetScanFilterDeviceName(name)
710        self.device.droid.bleBuildScanFilter(filter_list)
711        self.device.droid.bleSetScanFilterDeviceName(self.name)
712        self.device.droid.bleStartBleScan(filter_list, scan_settings,
713                                          scan_callback)
714        try:
715            event = self.device.ed.pop_event(scan_result.format(scan_callback),
716                                             timeout)
717            return event['data']['Result']['deviceInfo']['address']
718        except Empty as err:
719            self.log.info("Scanner did not find advertisement {}".format(err))
720            return None
721
722    def log_info(self, log):
723        """ Log directly onto the device.
724
725        Args:
726            log: The informative log.
727        """
728        self.device.droid.log.logI(log)
729
730    def set_bluetooth_local_name(self, name):
731        """ Sets the Bluetooth controller's local name
732        Args:
733            name: The name to set.
734        """
735        self.device.droid.bluetoothSetLocalName(name)
736
737    def get_local_bluetooth_address(self):
738        """ Returns the Bluetooth local address.
739        """
740        return self.device.droid.bluetoothGetLocalAddress()
741
742    def reset_bluetooth(self):
743        """ Resets Bluetooth on the Android Device.
744        """
745        bt_test_utils.reset_bluetooth([self.device])
746
747    def sdp_add_search(self, attribute_list, profile_id):
748        """Adds an SDP search record.
749        Args:
750            attribute_list: The list of attributes to set
751            profile_id: The profile ID to set.
752        """
753        # Android devices currently have no hooks to modify the SDP record.
754        pass
755
756    def sdp_add_service(self, sdp_record):
757        """Adds an SDP service record.
758        Args:
759            sdp_record: The dictionary representing the search record to add.
760        Returns:
761            service_id: The service id to track the service record published.
762                None if failed.
763        """
764        # Android devices currently have no hooks to modify the SDP record.
765        pass
766
767    def sdp_clean_up(self):
768        """Cleans up all objects related to SDP.
769        """
770        self.device.sdp_lib.cleanUp()
771
772    def sdp_init(self):
773        """Initializes SDP on the device.
774        """
775        # Android devices currently have no hooks to modify the SDP record.
776        pass
777
778    def sdp_remove_service(self, service_id):
779        """Removes a service based on an input id.
780        Args:
781            service_id: The service ID to remove.
782        """
783        # Android devices currently have no hooks to modify the SDP record.
784        pass
785
786    def unbond_all_known_devices(self):
787        """ Unbond all known remote devices.
788        """
789        self.device.droid.bluetoothFactoryReset()
790
791    def unbond_device(self, peer_identifier):
792        """ Unbond peer identifier.
793
794        Args:
795            peer_identifier: The mac address for the peer to unbond.
796
797        """
798        self.device.droid.bluetoothUnbond(peer_identifier)
799
800    def init_pair(self, peer_identifier, security_level, non_bondable,
801                  transport):
802        """ Send an outgoing pairing request the input peer_identifier.
803
804        Android currently does not support setting various security levels or
805        bondable modes. Making them available for other bluetooth_device
806        variants. Depending on the Address type, Android will figure out the
807        transport to pair automatically.
808
809        Args:
810            peer_identifier: A string representing the device id.
811            security_level: Not yet implemented. See Fuchsia device impl.
812            non_bondable: Not yet implemented. See Fuchsia device impl.
813            transport: Not yet implemented. See Fuchsia device impl.
814
815        """
816        self.dut.droid.bluetoothBond(self.peer_identifier)
817
818
819class FuchsiaBluetoothDevice(BluetoothDevice):
820    """Class wrapper for an Fuchsia Bluetooth device.
821
822    Each object of this class represents a generic luetooth device.
823    Android device and Fuchsia devices are the currently supported devices/
824
825    Attributes:
826        fuchsia_device: A Fuchsia Bluetooth device.
827    """
828    def __init__(self, fuchsia_device):
829        super().__init__(fuchsia_device)
830
831    def a2dp_initiate_open_stream(self):
832        raise NotImplementedError("{} not yet implemented.".format(
833            inspect.currentframe().f_code.co_name))
834
835    def start_profile_a2dp_sink(self):
836        """ Starts the A2DP sink profile.
837        """
838        self.device.control_daemon("bt-a2dp-sink.cmx", "start")
839
840    def stop_profile_a2dp_sink(self):
841        """ Stops the A2DP sink profile.
842        """
843        self.device.control_daemon("bt-a2dp-sink.cmx", "stop")
844
845    def start_pairing_helper(self):
846        self.device.btc_lib.acceptPairing()
847
848    def bluetooth_toggle_state(self, state):
849        """Stub for Fuchsia implementation."""
850        pass
851
852    def set_discoverable(self, is_discoverable):
853        """ Sets the device's discoverability.
854
855        Args:
856            is_discoverable: True if discoverable, false if not discoverable
857        """
858        self.device.btc_lib.setDiscoverable(is_discoverable)
859
860    def get_pairing_pin(self):
861        """ Get the pairing pin from the active pairing delegate.
862        """
863        return self.device.btc_lib.getPairingPin()['result']
864
865    def input_pairing_pin(self, pin):
866        """ Input pairing pin to active pairing delegate.
867
868        Args:
869            pin: The pin to input.
870        """
871        self.device.btc_lib.inputPairingPin(pin)
872
873    def initialize_bluetooth_controller(self):
874        """ Initialize Bluetooth controller for first time use.
875        """
876        self.device.btc_lib.initBluetoothControl()
877
878    def get_local_bluetooth_address(self):
879        """ Returns the Bluetooth local address.
880        """
881        return self.device.btc_lib.getActiveAdapterAddress().get("result")
882
883    def set_bluetooth_local_name(self, name):
884        """ Sets the Bluetooth controller's local name
885        Args:
886            name: The name to set.
887        """
888        self.device.btc_lib.setName(name)
889
890    def gatt_client_write_characteristic_without_response_by_handle(
891            self, peer_identifier, handle, value):
892        """ Perform a GATT Client write Characteristic without response to
893        remote peer GATT server database.
894
895        Args:
896            peer_identifier: The peer to connect to.
897            handle: The characteristic handle.
898            value: The list of bytes to write.
899        Returns:
900            True if success, False if failure.
901        """
902        if (not self._find_service_id_and_connect_to_service_for_handle(
903                peer_identifier, handle)):
904            self.log.error(
905                "Unable to find handle {} in GATT server db.".format(handle))
906            return False
907        result = self.device.gattc_lib.writeCharByIdWithoutResponse(
908            handle, value)
909        if result.get("error") is not None:
910            self.log.error(
911                "Failed to write characteristic handle {} with err: {}".format(
912                    handle, result.get("error")))
913            return False
914        return True
915
916    def gatt_client_write_characteristic_by_handle(self, peer_identifier,
917                                                   handle, offset, value):
918        """ Perform a GATT Client write Characteristic to remote peer GATT
919        server database.
920
921        Args:
922            peer_identifier: The peer to connect to.
923            handle: The characteristic handle.
924            offset: The offset to start writing to.
925            value: The list of bytes to write.
926        Returns:
927            True if success, False if failure.
928        """
929        if (not self._find_service_id_and_connect_to_service_for_handle(
930                peer_identifier, handle)):
931            self.log.error(
932                "Unable to find handle {} in GATT server db.".format(handle))
933            return False
934        result = self.device.gattc_lib.writeCharById(handle, offset, value)
935        if result.get("error") is not None:
936            self.log.error(
937                "Failed to write characteristic handle {} with err: {}".format(
938                    handle, result.get("error")))
939            return False
940        return True
941
942    def gatt_client_read_characteristic_by_handle(self, peer_identifier,
943                                                  handle):
944        """ Perform a GATT Client read Characteristic to remote peer GATT
945        server database.
946
947        Args:
948            peer_identifier: The peer to connect to.
949            handle: The characteristic handle.
950        Returns:
951            Value of Characteristic if success, None if failure.
952        """
953        if (not self._find_service_id_and_connect_to_service_for_handle(
954                peer_identifier, handle)):
955            self.log.error(
956                "Unable to find handle {} in GATT server db.".format(handle))
957            return False
958        result = self.device.gattc_lib.readCharacteristicById(handle)
959        if result.get("error") is not None:
960            self.log.error(
961                "Failed to read characteristic handle {} with err: {}".format(
962                    handle, result.get("error")))
963            return None
964        return result.get("result")
965
966    def gatt_client_read_long_characteristic_by_handle(self, peer_identifier,
967                                                       handle, offset,
968                                                       max_bytes):
969        """ Perform a GATT Client read Characteristic to remote peer GATT
970        server database.
971
972        Args:
973            peer_identifier: The peer to connect to.
974            handle: The characteristic handle.
975            offset: The offset to start reading.
976            max_bytes: The max bytes to return for each read.
977        Returns:
978            Value of Characteristic if success, None if failure.
979        """
980        if (not self._find_service_id_and_connect_to_service_for_handle(
981                peer_identifier, handle)):
982            self.log.error(
983                "Unable to find handle {} in GATT server db.".format(handle))
984            return False
985        result = self.device.gattc_lib.readLongCharacteristicById(
986            handle, offset, max_bytes)
987        if result.get("error") is not None:
988            self.log.error(
989                "Failed to read characteristic handle {} with err: {}".format(
990                    handle, result.get("error")))
991            return None
992        return result.get("result")
993
994    def gatt_client_enable_notifiy_characteristic_by_handle(
995            self, peer_identifier, handle):
996        """ Perform a GATT Client enable Characteristic notification to remote
997        peer GATT server database.
998
999        Args:
1000            peer_identifier: The peer to connect to.
1001            handle: The characteristic handle.
1002        Returns:
1003            True is success, False if failure.
1004        """
1005        if (not self._find_service_id_and_connect_to_service_for_handle(
1006                peer_identifier, handle)):
1007            self.log.error(
1008                "Unable to find handle {} in GATT server db.".format(handle))
1009            return False
1010        result = self.device.gattc_lib.enableNotifyCharacteristic(handle)
1011        if result.get("error") is not None:
1012            self.log.error(
1013                "Failed to enable characteristic notifications for handle {} "
1014                "with err: {}".format(handle, result.get("error")))
1015            return None
1016        return result.get("result")
1017
1018    def gatt_client_disable_notifiy_characteristic_by_handle(
1019            self, peer_identifier, handle):
1020        """ Perform a GATT Client disable Characteristic notification to remote
1021        peer GATT server database.
1022
1023        Args:
1024            peer_identifier: The peer to connect to.
1025            handle: The characteristic handle.
1026        Returns:
1027            True is success, False if failure.
1028        """
1029        if (not self._find_service_id_and_connect_to_service_for_handle(
1030                peer_identifier, handle)):
1031            self.log.error(
1032                "Unable to find handle {} in GATT server db.".format(handle))
1033            return False
1034        result = self.device.gattc_lib.disableNotifyCharacteristic(handle)
1035        if result.get("error") is not None:
1036            self.log.error(
1037                "Failed to disable characteristic notifications for handle {} "
1038                "with err: {}".format(peer_identifier, result.get("error")))
1039            return None
1040        return result.get("result")
1041
1042    def gatt_client_read_descriptor_by_handle(self, peer_identifier, handle):
1043        """ Perform a GATT Client read Descriptor to remote peer GATT server
1044        database.
1045
1046        Args:
1047            peer_identifier: The peer to connect to.
1048            handle: The Descriptor handle.
1049        Returns:
1050            Value of Descriptor if success, None if failure.
1051        """
1052        if (not self._find_service_id_and_connect_to_service_for_handle(
1053                peer_identifier, handle)):
1054            self.log.error(
1055                "Unable to find handle {} in GATT server db.".format(handle))
1056            return False
1057        result = self.device.gattc_lib.readDescriptorById(handle)
1058        if result.get("error") is not None:
1059            self.log.error(
1060                "Failed to read descriptor for handle {} with err: {}".format(
1061                    peer_identifier, result.get("error")))
1062            return None
1063        return result.get("result")
1064
1065    def gatt_client_write_descriptor_by_handle(self, peer_identifier, handle,
1066                                               offset, value):
1067        """ Perform a GATT Client write Descriptor to remote peer GATT server
1068        database.
1069
1070        Args:
1071            peer_identifier: The peer to connect to.
1072            handle: The Descriptor handle.
1073            offset: The offset to start writing at.
1074            value: The list of bytes to write.
1075        Returns:
1076            True if success, False if failure.
1077        """
1078        if (not self._find_service_id_and_connect_to_service_for_handle(
1079                peer_identifier, handle)):
1080            self.log.error(
1081                "Unable to find handle {} in GATT server db.".format(handle))
1082            return False
1083        result = self.device.gattc_lib.writeDescriptorById(
1084            handle, offset, value)
1085        if result.get("error") is not None:
1086            self.log.error(
1087                "Failed to write descriptor for handle {} with err: {}".format(
1088                    peer_identifier, result.get("error")))
1089            return None
1090        return True
1091
1092    def gatt_connect(self, peer_identifier, transport, autoconnect):
1093        """ Perform a GATT connection to a perihperal.
1094
1095        Args:
1096            peer_identifier: The peer to connect to.
1097            transport: Not implemented.
1098            autoconnect: Not implemented.
1099        Returns:
1100            True if success, False if failure.
1101        """
1102        connection_result = self.device.gattc_lib.bleConnectToPeripheral(
1103            peer_identifier)
1104        if connection_result.get("error") is not None:
1105            self.log.error("Failed to connect to peer id {}: {}".format(
1106                peer_identifier, connection_result.get("error")))
1107            return False
1108        return True
1109
1110    def gatt_client_refresh(self, peer_identifier):
1111        """ Perform a GATT Client Refresh of a perihperal.
1112
1113        Clears the internal cache and forces a refresh of the services from the
1114        remote device. In Fuchsia there is no FIDL api to automatically do this
1115        yet. Therefore just read all Characteristics which satisfies the same
1116        requirements.
1117
1118        Args:
1119            peer_identifier: The peer to refresh.
1120        """
1121        self._read_all_characteristics(peer_identifier)
1122
1123    def gatt_client_discover_characteristic_by_uuid(self, peer_identifier,
1124                                                    uuid):
1125        """ Perform a GATT Client Refresh of a perihperal.
1126
1127        Clears the internal cache and forces a refresh of the services from the
1128        remote device. In Fuchsia there is no FIDL api to automatically do this
1129        yet. Therefore just read all Characteristics which satisfies the same
1130        requirements.
1131
1132        Args:
1133            peer_identifier: The peer to refresh.
1134        """
1135        self._read_all_characteristics(peer_identifier, uuid)
1136
1137    def gatt_disconnect(self, peer_identifier):
1138        """ Perform a GATT disconnect from a perihperal.
1139
1140        Args:
1141            peer_identifier: The peer to disconnect from.
1142        Returns:
1143            True if success, False if failure.
1144        """
1145        disconnect_result = self.device.gattc_lib.bleDisconnectPeripheral(
1146            peer_identifier)
1147        if disconnect_result.get("error") is None:
1148            self.log.error("Failed to disconnect from peer id {}: {}".format(
1149                peer_identifier, disconnect_result.get("error")))
1150            return False
1151        return True
1152
1153    def reset_bluetooth(self):
1154        """Stub for Fuchsia implementation."""
1155        pass
1156
1157    def sdp_add_search(self, attribute_list, profile_id):
1158        """Adds an SDP search record.
1159        Args:
1160            attribute_list: The list of attributes to set
1161            profile_id: The profile ID to set.
1162        """
1163        return self.device.sdp_lib.addSearch(attribute_list, profile_id)
1164
1165    def sdp_add_service(self, sdp_record):
1166        """Adds an SDP service record.
1167        Args:
1168            sdp_record: The dictionary representing the search record to add.
1169        """
1170        return self.device.sdp_lib.addService(sdp_record)
1171
1172    def sdp_clean_up(self):
1173        """Cleans up all objects related to SDP.
1174        """
1175        return self.device.sdp_lib.cleanUp()
1176
1177    def sdp_init(self):
1178        """Initializes SDP on the device.
1179        """
1180        return self.device.sdp_lib.init()
1181
1182    def sdp_remove_service(self, service_id):
1183        """Removes a service based on an input id.
1184        Args:
1185            service_id: The service ID to remove.
1186        """
1187        return self.device.sdp_lib.init()
1188
1189    def start_le_advertisement(self, adv_data, adv_interval):
1190        """ Starts an LE advertisement
1191
1192        Args:
1193            adv_data: Advertisement data.
1194            adv_interval: Advertisement interval.
1195        """
1196        self.device.ble_lib.bleStartBleAdvertising(adv_data, adv_interval)
1197
1198    def stop_le_advertisement(self):
1199        """ Stop active LE advertisement.
1200        """
1201        self.device.ble_lib.bleStopBleAdvertising()
1202
1203    def setup_gatt_server(self, database):
1204        """ Sets up an input GATT server.
1205
1206        Args:
1207            database: A dictionary representing the GATT database to setup.
1208        """
1209        self.device.gatts_lib.publishServer(database)
1210
1211    def close_gatt_server(self):
1212        """ Closes an existing GATT server.
1213        """
1214        self.device.gatts_lib.closeServer()
1215
1216    def le_scan_with_name_filter(self, name, timeout):
1217        """ Scan over LE for a specific device name.
1218
1219        Args:
1220            name: The name filter to set.
1221            timeout: The timeout to wait to find the advertisement.
1222        Returns:
1223            Discovered device id or None
1224        """
1225        partial_match = True
1226        return le_scan_for_device_by_name(self.device, self.device.log, name,
1227                                          timeout, partial_match)
1228
1229    def log_info(self, log):
1230        """ Log directly onto the device.
1231
1232        Args:
1233            log: The informative log.
1234        """
1235        self.device.logging_lib.logI(log)
1236        pass
1237
1238    def unbond_all_known_devices(self):
1239        """ Unbond all known remote devices.
1240        """
1241        try:
1242            device_list = self.device.btc_lib.getKnownRemoteDevices()['result']
1243            for device_info in device_list:
1244                device = device_list[device_info]
1245                if device['bonded']:
1246                    self.device.btc_lib.forgetDevice(device['id'])
1247        except Exception as err:
1248            self.log.err("Unable to unbond all devices: {}".format(err))
1249
1250    def unbond_device(self, peer_identifier):
1251        """ Unbond peer identifier.
1252
1253        Args:
1254            peer_identifier: The peer identifier for the peer to unbond.
1255
1256        """
1257        self.device.btc_lib.forgetDevice(peer_identifier)
1258
1259    def _find_service_id_and_connect_to_service_for_handle(
1260            self, peer_identifier, handle):
1261        fail_err = "Failed to find handle {} in Peer database."
1262        try:
1263            services = self.device.gattc_lib.listServices(peer_identifier)
1264            for service in services['result']:
1265                service_id = service['id']
1266                self.device.gattc_lib.connectToService(peer_identifier,
1267                                                       service_id)
1268                chars = self.device.gattc_lib.discoverCharacteristics()
1269
1270                for char in chars['result']:
1271                    char_id = char['id']
1272                    if handle == char_id:
1273                        return True
1274                    descriptors = char['descriptors']
1275                    for desc in descriptors:
1276                        desc_id = desc["id"]
1277                        if handle == desc_id:
1278                            return True
1279        except Exception as err:
1280            self.log.error(fail_err.format(err))
1281            return False
1282
1283    def _read_all_characteristics(self, peer_identifier, uuid=None):
1284        fail_err = "Failed to read all characteristics with: {}"
1285        try:
1286            services = self.device.gattc_lib.listServices(peer_identifier)
1287            for service in services['result']:
1288                service_id = service['id']
1289                service_uuid = service['uuid_type']
1290                self.device.gattc_lib.connectToService(peer_identifier,
1291                                                       service_id)
1292                chars = self.device.gattc_lib.discoverCharacteristics()
1293                self.log.info(
1294                    "Reading chars in service uuid: {}".format(service_uuid))
1295
1296                for char in chars['result']:
1297                    char_id = char['id']
1298                    char_uuid = char['uuid_type']
1299                    if uuid and uuid.lower() not in char_uuid.lower():
1300                        continue
1301                    try:
1302                        read_val =  \
1303                            self.device.gattc_lib.readCharacteristicById(
1304                                char_id)
1305                        self.log.info(
1306                            "\tCharacteristic uuid / Value: {} / {}".format(
1307                                char_uuid, read_val['result']))
1308                        str_value = ""
1309                        for val in read_val['result']:
1310                            str_value += chr(val)
1311                        self.log.info("\t\tstr val: {}".format(str_value))
1312                    except Exception as err:
1313                        self.log.error(err)
1314                        pass
1315        except Exception as err:
1316            self.log.error(fail_err.forma(err))
1317
1318    def _perform_read_all_descriptors(self, peer_identifier):
1319        fail_err = "Failed to read all characteristics with: {}"
1320        try:
1321            services = self.device.gattc_lib.listServices(peer_identifier)
1322            for service in services['result']:
1323                service_id = service['id']
1324                service_uuid = service['uuid_type']
1325                self.device.gattc_lib.connectToService(peer_identifier,
1326                                                       service_id)
1327                chars = self.device.gattc_lib.discoverCharacteristics()
1328                self.log.info(
1329                    "Reading descs in service uuid: {}".format(service_uuid))
1330
1331                for char in chars['result']:
1332                    char_id = char['id']
1333                    char_uuid = char['uuid_type']
1334                    descriptors = char['descriptors']
1335                    self.log.info(
1336                        "\tReading descs in char uuid: {}".format(char_uuid))
1337                    for desc in descriptors:
1338                        desc_id = desc["id"]
1339                        desc_uuid = desc["uuid_type"]
1340                    try:
1341                        read_val = self.device.gattc_lib.readDescriptorById(
1342                            desc_id)
1343                        self.log.info(
1344                            "\t\tDescriptor uuid / Value: {} / {}".format(
1345                                desc_uuid, read_val['result']))
1346                    except Exception as err:
1347                        pass
1348        except Exception as err:
1349            self.log.error(fail_err.format(err))
1350
1351    def init_pair(self, peer_identifier, security_level, non_bondable,
1352                  transport):
1353        """ Send an outgoing pairing request the input peer_identifier.
1354
1355        Android currently does not support setting various security levels or
1356        bondable modes. Making them available for other bluetooth_device
1357        variants. Depending on the Address type, Android will figure out the
1358        transport to pair automatically.
1359
1360        Args:
1361            peer_identifier: A string representing the device id.
1362            security_level: The security level required for this pairing request
1363                represented as a u64. (Only for LE pairing)
1364                Available Values
1365                1 - ENCRYPTED: Encrypted without MITM protection
1366                    (unauthenticated)
1367                2 - AUTHENTICATED: Encrypted with MITM protection
1368                    (authenticated)
1369                None: No pairing security level.
1370            non_bondable: A bool representing whether the pairing mode is
1371                bondable or not. None is also accepted. False if bondable, True
1372                if non-bondable
1373            transport: A u64 representing the transport type.
1374                Available Values
1375                1 - BREDR: Classic BR/EDR transport
1376                2 - LE: Bluetooth Low Energy Transport
1377        Returns:
1378            True if successful, False if failed.
1379        """
1380        try:
1381            self.device.btc_lib.pair(peer_identifier, security_level,
1382                                     non_bondable, transport)
1383            return True
1384        except Exception as err:
1385            fail_err = "Failed to pair to peer_identifier {} with: {}".format(
1386                peer_identifier)
1387            self.log.error(fail_err.format(err))
1388