1#/usr/bin/env python3.4
2#
3# Copyright (C) 2016 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License"); you may not
6# use this file except in compliance with the License. You may obtain a copy of
7# 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, WITHOUT
13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14# License for the specific language governing permissions and limitations under
15# the License.
16"""
17Test suite for GATT over BR/EDR.
18"""
19
20import time
21from queue import Empty
22
23from acts.test_decorators import test_tracker_info
24from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
25from acts.test_utils.bt.bt_test_utils import reset_bluetooth
26from acts.test_utils.bt.bt_constants import gatt_characteristic
27from acts.test_utils.bt.bt_constants import gatt_service_types
28from acts.test_utils.bt.bt_constants import gatt_transport
29from acts.test_utils.bt.bt_constants import gatt_cb_strings
30from acts.test_utils.bt.bt_gatt_utils import GattTestUtilsError
31from acts.test_utils.bt.bt_gatt_utils import disconnect_gatt_connection
32from acts.test_utils.bt.bt_gatt_utils import log_gatt_server_uuids
33from acts.test_utils.bt.bt_gatt_utils import orchestrate_gatt_connection
34from acts.test_utils.bt.bt_gatt_utils import setup_gatt_characteristics
35from acts.test_utils.bt.bt_gatt_utils import setup_gatt_connection
36from acts.test_utils.bt.bt_gatt_utils import setup_gatt_descriptors
37from acts.test_utils.bt.bt_gatt_utils import setup_multiple_services
38from acts.test_utils.bt.bt_test_utils import setup_multiple_devices_for_bt_test
39from acts.test_utils.bt.bt_test_utils import take_btsnoop_logs
40
41
42class GattOverBrEdrTest(BluetoothBaseTest):
43    adv_instances = []
44    bluetooth_gatt_list = []
45    gatt_server_list = []
46    default_timeout = 10
47    default_discovery_timeout = 3
48    per_droid_mac_address = None
49
50    def __init__(self, controllers):
51        BluetoothBaseTest.__init__(self, controllers)
52        self.cen_ad = self.android_devices[0]
53        self.per_ad = self.android_devices[1]
54
55    def setup_class(self):
56        super(BluetoothBaseTest, self).setup_class()
57        self.per_droid_mac_address = self.per_ad.droid.bluetoothGetLocalAddress(
58        )
59        if not self.per_droid_mac_address:
60            return False
61        return True
62
63    def setup_test(self):
64        super(BluetoothBaseTest, self).setup_test()
65        bluetooth_gatt_list = []
66        self.gatt_server_list = []
67        self.adv_instances = []
68
69    def teardown_test(self):
70        for bluetooth_gatt in self.bluetooth_gatt_list:
71            self.cen_ad.droid.gattClientClose(bluetooth_gatt)
72        for gatt_server in self.gatt_server_list:
73            self.per_ad.droid.gattServerClose(gatt_server)
74        return True
75
76    def on_fail(self, test_name, begin_time):
77        take_btsnoop_logs(self.android_devices, self, test_name)
78        reset_bluetooth(self.android_devices)
79
80    def _orchestrate_gatt_disconnection(self, bluetooth_gatt, gatt_callback):
81        self.log.info("Disconnecting from peripheral device.")
82        try:
83            disconnect_gatt_connection(self.cen_ad, bluetooth_gatt,
84                                       gatt_callback)
85            if bluetooth_gatt in self.bluetooth_gatt_list:
86                self.bluetooth_gatt_list.remove(bluetooth_gatt)
87        except GattTestUtilsError as err:
88            self.log.error(err)
89            return False
90        return True
91
92    def _find_service_added_event(self, gatt_server_callback, uuid):
93        event = self.per_ad.ed.pop_event(
94            gatt_cb_strings['serv_added'].format(gatt_server_callback),
95            self.default_timeout)
96        if event['data']['serviceUuid'].lower() != uuid.lower():
97            self.log.info("Uuid mismatch. Found: {}, Expected {}.".format(
98                event['data']['serviceUuid'], uuid))
99            return False
100        return True
101
102    @BluetoothBaseTest.bt_test_wrap
103    @test_tracker_info(uuid='32d32c87-911e-4f14-9654-29fe1431e995')
104    def test_gatt_bredr_connect(self):
105        """Test GATT connection over BR/EDR.
106
107        Test establishing a gatt connection between a GATT server and GATT
108        client.
109
110        Steps:
111        1. Start a generic advertisement.
112        2. Start a generic scanner.
113        3. Find the advertisement and extract the mac address.
114        4. Stop the first scanner.
115        5. Create a GATT connection between the scanner and advertiser.
116        6. Disconnect the GATT connection.
117
118        Expected Result:
119        Verify that a connection was established and then disconnected
120        successfully.
121
122        Returns:
123          Pass if True
124          Fail if False
125
126        TAGS: BR/EDR, Filtering, GATT, Scanning
127        Priority: 0
128        """
129        gatt_server_cb = self.per_ad.droid.gattServerCreateGattServerCallback()
130        gatt_server = self.per_ad.droid.gattServerOpenGattServer(
131            gatt_server_cb)
132        self.gatt_server_list.append(gatt_server)
133        try:
134            bluetooth_gatt, gatt_callback, adv_callback = (
135                orchestrate_gatt_connection(self.cen_ad, self.per_ad,
136                                            gatt_transport['bredr'],
137                                            self.per_droid_mac_address))
138            self.bluetooth_gatt_list.append(bluetooth_gatt)
139        except GattTestUtilsError as err:
140            self.log.error(err)
141            return False
142        return self._orchestrate_gatt_disconnection(bluetooth_gatt,
143                                                    gatt_callback)
144
145    @BluetoothBaseTest.bt_test_wrap
146    @test_tracker_info(uuid='357b697b-a52c-4c2a-997c-00876a018f37')
147    def test_gatt_bredr_connect_trigger_on_read_rssi(self):
148        """Test GATT connection over BR/EDR read RSSI.
149
150        Test establishing a gatt connection between a GATT server and GATT
151        client then read the RSSI.
152
153        Steps:
154        1. Start a generic advertisement.
155        2. Start a generic scanner.
156        3. Find the advertisement and extract the mac address.
157        4. Stop the first scanner.
158        5. Create a GATT connection between the scanner and advertiser.
159        6. From the scanner, request to read the RSSI of the advertiser.
160        7. Disconnect the GATT connection.
161
162        Expected Result:
163        Verify that a connection was established and then disconnected
164        successfully. Verify that the RSSI was ready correctly.
165
166        Returns:
167          Pass if True
168          Fail if False
169
170        TAGS: BR/EDR, Scanning, GATT, RSSI
171        Priority: 1
172        """
173        gatt_server_cb = self.per_ad.droid.gattServerCreateGattServerCallback()
174        gatt_server = self.per_ad.droid.gattServerOpenGattServer(
175            gatt_server_cb)
176        self.gatt_server_list.append(gatt_server)
177        try:
178            bluetooth_gatt, gatt_callback, adv_callback = (
179                orchestrate_gatt_connection(self.cen_ad, self.per_ad,
180                                            gatt_transport['bredr'],
181                                            self.per_droid_mac_address))
182            self.bluetooth_gatt_list.append(bluetooth_gatt)
183        except GattTestUtilsError as err:
184            self.log.error(err)
185            return False
186        if self.cen_ad.droid.gattClientReadRSSI(bluetooth_gatt):
187            self.cen_ad.ed.pop_event(
188                gatt_cb_strings['rd_remote_rssi'].format(gatt_callback),
189                self.default_timeout)
190        return self._orchestrate_gatt_disconnection(bluetooth_gatt,
191                                                    gatt_callback)
192
193    @BluetoothBaseTest.bt_test_wrap
194    @test_tracker_info(uuid='dee9ef28-b872-428a-821b-cc62f27ba936')
195    def test_gatt_bredr_connect_trigger_on_services_discovered(self):
196        """Test GATT connection and discover services of peripheral.
197
198        Test establishing a gatt connection between a GATT server and GATT
199        client the discover all services from the connected device.
200
201        Steps:
202        1. Start a generic advertisement.
203        2. Start a generic scanner.
204        3. Find the advertisement and extract the mac address.
205        4. Stop the first scanner.
206        5. Create a GATT connection between the scanner and advertiser.
207        6. From the scanner (central device), discover services.
208        7. Disconnect the GATT connection.
209
210        Expected Result:
211        Verify that a connection was established and then disconnected
212        successfully. Verify that the service were discovered.
213
214        Returns:
215          Pass if True
216          Fail if False
217
218        TAGS: BR/EDR, Scanning, GATT, Services
219        Priority: 1
220        """
221        gatt_server_cb = self.per_ad.droid.gattServerCreateGattServerCallback()
222        gatt_server = self.per_ad.droid.gattServerOpenGattServer(
223            gatt_server_cb)
224        self.gatt_server_list.append(gatt_server)
225        try:
226            bluetooth_gatt, gatt_callback, adv_callback = (
227                orchestrate_gatt_connection(self.cen_ad, self.per_ad,
228                                            gatt_transport['bredr'],
229                                            self.per_droid_mac_address))
230            self.bluetooth_gatt_list.append(bluetooth_gatt)
231        except GattTestUtilsError as err:
232            self.log.error(err)
233            return False
234        discovered_services_index = -1
235        if self.cen_ad.droid.gattClientDiscoverServices(bluetooth_gatt):
236            event = self.cen_ad.ed.pop_event(
237                gatt_cb_strings['gatt_serv_disc'].format(gatt_callback),
238                self.default_timeout)
239            discovered_services_index = event['data']['ServicesIndex']
240        return self._orchestrate_gatt_disconnection(bluetooth_gatt,
241                                                    gatt_callback)
242
243    @BluetoothBaseTest.bt_test_wrap
244    @test_tracker_info(uuid='01883bdd-0cf8-48fb-bf15-467bbd4f065b')
245    def test_gatt_bredr_connect_trigger_on_services_discovered_iterate_attributes(
246            self):
247        """Test GATT connection and iterate peripherals attributes.
248
249        Test establishing a gatt connection between a GATT server and GATT
250        client and iterate over all the characteristics and descriptors of the
251        discovered services.
252
253        Steps:
254        1. Start a generic advertisement.
255        2. Start a generic scanner.
256        3. Find the advertisement and extract the mac address.
257        4. Stop the first scanner.
258        5. Create a GATT connection between the scanner and advertiser.
259        6. From the scanner (central device), discover services.
260        7. Iterate over all the characteristics and descriptors of the
261        discovered features.
262        8. Disconnect the GATT connection.
263
264        Expected Result:
265        Verify that a connection was established and then disconnected
266        successfully. Verify that the services, characteristics, and descriptors
267        were discovered.
268
269        Returns:
270          Pass if True
271          Fail if False
272
273        TAGS: BR/EDR, Scanning, GATT, Services
274        Characteristics, Descriptors
275        Priority: 1
276        """
277        gatt_server_cb = self.per_ad.droid.gattServerCreateGattServerCallback()
278        gatt_server = self.per_ad.droid.gattServerOpenGattServer(
279            gatt_server_cb)
280        self.gatt_server_list.append(gatt_server)
281        try:
282            bluetooth_gatt, gatt_callback, adv_callback = (
283                orchestrate_gatt_connection(self.cen_ad, self.per_ad,
284                                            gatt_transport['bredr'],
285                                            self.per_droid_mac_address))
286            self.bluetooth_gatt_list.append(bluetooth_gatt)
287        except GattTestUtilsError as err:
288            self.log.error(err)
289            return False
290        discovered_services_index = -1
291        if self.cen_ad.droid.gattClientDiscoverServices(bluetooth_gatt):
292            event = self.cen_ad.ed.pop_event(
293                gatt_cb_strings['gatt_serv_disc'].format(gatt_callback),
294                self.default_timeout)
295            discovered_services_index = event['data']['ServicesIndex']
296            log_gatt_server_uuids(self.cen_ad, discovered_services_index)
297        return self._orchestrate_gatt_disconnection(bluetooth_gatt,
298                                                    gatt_callback)
299
300    @BluetoothBaseTest.bt_test_wrap
301    @test_tracker_info(uuid='d4277bee-da99-4f48-8a4d-f81b5389da18')
302    def test_gatt_bredr_connect_with_service_uuid_variations(self):
303        """Test GATT connection with multiple service uuids.
304
305        Test establishing a gatt connection between a GATT server and GATT
306        client with multiple service uuid variations.
307
308        Steps:
309        1. Start a generic advertisement.
310        2. Start a generic scanner.
311        3. Find the advertisement and extract the mac address.
312        4. Stop the first scanner.
313        5. Create a GATT connection between the scanner and advertiser.
314        6. From the scanner (central device), discover services.
315        7. Verify that all the service uuid variations are found.
316        8. Disconnect the GATT connection.
317
318        Expected Result:
319        Verify that a connection was established and then disconnected
320        successfully. Verify that the service uuid variations are found.
321
322        Returns:
323          Pass if True
324          Fail if False
325
326        TAGS: BR/EDR, Scanning, GATT, Services
327        Priority: 2
328        """
329        gatt_server_cb = self.per_ad.droid.gattServerCreateGattServerCallback()
330        gatt_server = self.per_ad.droid.gattServerOpenGattServer(
331            gatt_server_cb)
332        self.gatt_server_list.append(gatt_server)
333        try:
334            gatt_server_callback, gatt_server = setup_multiple_services(
335                self.per_ad)
336            self.gatt_server_list.append(gatt_server)
337        except GattTestUtilsError as err:
338            self.log.error(err)
339            return False
340        try:
341            bluetooth_gatt, gatt_callback, adv_callback = (
342                orchestrate_gatt_connection(self.cen_ad, self.per_ad,
343                                            gatt_transport['bredr'],
344                                            self.per_droid_mac_address))
345            self.bluetooth_gatt_list.append(bluetooth_gatt)
346        except GattTestUtilsError as err:
347            self.log.error(err)
348            return False
349        discovered_services_index = -1
350        if self.cen_ad.droid.gattClientDiscoverServices(bluetooth_gatt):
351            event = self.cen_ad.ed.pop_event(
352                gatt_cb_strings['gatt_serv_disc'].format(gatt_callback),
353                self.default_timeout)
354            discovered_services_index = event['data']['ServicesIndex']
355            log_gatt_server_uuids(self.cen_ad, discovered_services_index)
356        return self._orchestrate_gatt_disconnection(bluetooth_gatt,
357                                                    gatt_callback)
358
359    @BluetoothBaseTest.bt_test_wrap
360    @test_tracker_info(uuid='15c726dc-788a-4400-9a90-8c6866b24a3a')
361    def test_gatt_bredr_connect_multiple_iterations(self):
362        """Test GATT connections multiple times.
363
364        Test establishing a gatt connection between a GATT server and GATT
365        client with multiple iterations.
366
367        Steps:
368        1. Start a generic advertisement.
369        2. Start a generic scanner.
370        3. Find the advertisement and extract the mac address.
371        4. Stop the first scanner.
372        5. Create a GATT connection between the scanner and advertiser.
373        6. Disconnect the GATT connection.
374        7. Repeat steps 5 and 6 twenty times.
375
376        Expected Result:
377        Verify that a connection was established and then disconnected
378        successfully twenty times.
379
380        Returns:
381          Pass if True
382          Fail if False
383
384        TAGS: BR/EDR, Scanning, GATT, Stress
385        Priority: 1
386        """
387        gatt_server_cb = self.per_ad.droid.gattServerCreateGattServerCallback()
388        gatt_server = self.per_ad.droid.gattServerOpenGattServer(
389            gatt_server_cb)
390        self.gatt_server_list.append(gatt_server)
391        autoconnect = False
392        mac_address = self.per_ad.droid.bluetoothGetLocalAddress()
393        for i in range(20):
394            try:
395                bluetooth_gatt, gatt_callback, adv_callback = (
396                    orchestrate_gatt_connection(self.cen_ad, self.per_ad,
397                                                gatt_transport['bredr'],
398                                                self.per_droid_mac_address))
399                self.bluetooth_gatt_list.append(bluetooth_gatt)
400            except GattTestUtilsError as err:
401                self.log.error(err)
402                return False
403            self.log.info("Disconnecting from peripheral device.")
404            test_result = self._orchestrate_gatt_disconnection(bluetooth_gatt,
405                                                               gatt_callback)
406            if not test_result:
407                self.log.info("Failed to disconnect from peripheral device.")
408                return False
409        return True
410
411    @BluetoothBaseTest.bt_test_wrap
412    @test_tracker_info(uuid='6ec766ca-6358-48ff-9d85-ede4d2756546')
413    def test_bredr_write_descriptor_stress(self):
414        """Test GATT connection writing and reading descriptors.
415
416        Test establishing a gatt connection between a GATT server and GATT
417        client with multiple service uuid variations.
418
419        Steps:
420        1. Start a generic advertisement.
421        2. Start a generic scanner.
422        3. Find the advertisement and extract the mac address.
423        4. Stop the first scanner.
424        5. Create a GATT connection between the scanner and advertiser.
425        6. Discover services.
426        7. Write data to the descriptors of each characteristic 100 times.
427        8. Read the data sent to the descriptors.
428        9. Disconnect the GATT connection.
429
430        Expected Result:
431        Each descriptor in each characteristic is written and read 100 times.
432
433        Returns:
434          Pass if True
435          Fail if False
436
437        TAGS: BR/EDR, Scanning, GATT, Stress, Characteristics, Descriptors
438        Priority: 1
439        """
440        try:
441            gatt_server_callback, gatt_server = setup_multiple_services(
442                self.per_ad)
443            self.gatt_server_list.append(gatt_server)
444        except GattTestUtilsError as err:
445            self.log.error(err)
446            return False
447        try:
448            bluetooth_gatt, gatt_callback, adv_callback = (
449                orchestrate_gatt_connection(self.cen_ad, self.per_ad,
450                                            gatt_transport['bredr'],
451                                            self.per_droid_mac_address))
452            self.bluetooth_gatt_list.append(bluetooth_gatt)
453        except GattTestUtilsError as err:
454            self.log.error(err)
455            return False
456        if self.cen_ad.droid.gattClientDiscoverServices(bluetooth_gatt):
457            try:
458                event = self.cen_ad.ed.pop_event(
459                    gatt_cb_strings['gatt_serv_disc'].format(gatt_callback),
460                    self.default_timeout)
461            except Empty as err:
462                self.log.error("Event not found: {}".format(err))
463                return False
464            discovered_services_index = event['data']['ServicesIndex']
465        else:
466            self.log.info("Failed to discover services.")
467            return False
468        services_count = self.cen_ad.droid.gattClientGetDiscoveredServicesCount(
469            discovered_services_index)
470
471        connected_device_list = self.per_ad.droid.gattServerGetConnectedDevices(
472            gatt_server)
473        if len(connected_device_list) == 0:
474            self.log.info("No devices connected from peripheral.")
475            return False
476        bt_device_id = 0
477        status = 1
478        offset = 1
479        test_value = [1, 2, 3, 4, 5, 6, 7]
480        test_value_return = [1, 2, 3]
481        for i in range(services_count):
482            characteristic_uuids = (
483                self.cen_ad.droid.gattClientGetDiscoveredCharacteristicUuids(
484                    discovered_services_index, i))
485            for characteristic in characteristic_uuids:
486                descriptor_uuids = (
487                    self.cen_ad.droid.gattClientGetDiscoveredDescriptorUuids(
488                        discovered_services_index, i, characteristic))
489                for _ in range(100):
490                    for descriptor in descriptor_uuids:
491                        self.cen_ad.droid.gattClientDescriptorSetValue(
492                            bluetooth_gatt, discovered_services_index, i,
493                            characteristic, descriptor, test_value)
494                        self.cen_ad.droid.gattClientWriteDescriptor(
495                            bluetooth_gatt, discovered_services_index, i,
496                            characteristic, descriptor)
497                        event = self.per_ad.ed.pop_event(gatt_cb_strings[
498                            'desc_write_req'].format(gatt_server_callback),
499                                                         self.default_timeout)
500                        self.log.info(
501                            "onDescriptorWriteRequest event found: {}".format(
502                                event))
503                        request_id = event['data']['requestId']
504                        found_value = event['data']['value']
505                        if found_value != test_value:
506                            self.log.info("Values didn't match. Found: {}, "
507                                          "Expected: {}".format(found_value,
508                                                                test_value))
509                            return False
510                        self.per_ad.droid.gattServerSendResponse(
511                            gatt_server, bt_device_id, request_id, status,
512                            offset, test_value_return)
513                        self.log.info(
514                            "onDescriptorWrite event found: {}".format(
515                                self.cen_ad.ed.pop_event(gatt_cb_strings[
516                                    'desc_write'].format(
517                                        gatt_callback), self.default_timeout)))
518        return True
519