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