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"""
17This test script exercises different GATT connection tests.
18"""
19
20import pprint
21from queue import Empty
22import time
23
24from acts.test_decorators import test_tracker_info
25from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
26from acts.test_utils.bt.bt_constants import ble_advertise_settings_modes
27from acts.test_utils.bt.bt_constants import ble_scan_settings_modes
28from acts.test_utils.bt.bt_constants import ble_scan_settings_match_nums
29from acts.test_utils.bt.bt_constants import bt_profile_constants
30from acts.test_utils.bt.bt_constants import gatt_characteristic
31from acts.test_utils.bt.bt_constants import gatt_descriptor
32from acts.test_utils.bt.bt_constants import gatt_service_types
33from acts.test_utils.bt.bt_constants import gatt_cb_err
34from acts.test_utils.bt.bt_constants import gatt_cb_strings
35from acts.test_utils.bt.bt_constants import gatt_connection_state
36from acts.test_utils.bt.bt_constants import gatt_mtu_size
37from acts.test_utils.bt.bt_constants import gatt_phy_mask
38from acts.test_utils.bt.bt_constants import gatt_transport
39from acts.test_utils.bt.bt_constants import scan_result
40from acts.test_utils.bt.bt_gatt_utils import GattTestUtilsError
41from acts.test_utils.bt.bt_gatt_utils import disconnect_gatt_connection
42from acts.test_utils.bt.bt_gatt_utils import wait_for_gatt_disconnect_event
43from acts.test_utils.bt.bt_gatt_utils import close_gatt_client
44from acts.test_utils.bt.bt_gatt_utils import log_gatt_server_uuids
45from acts.test_utils.bt.bt_gatt_utils import orchestrate_gatt_connection
46from acts.test_utils.bt.bt_gatt_utils import setup_gatt_connection
47from acts.test_utils.bt.bt_gatt_utils import setup_multiple_services
48from acts.test_utils.bt.bt_test_utils import get_mac_address_of_generic_advertisement
49from acts.test_utils.bt.bt_test_utils import clear_bonded_devices
50
51PHYSICAL_DISCONNECT_TIMEOUT = 5
52
53
54class GattConnectTest(BluetoothBaseTest):
55    adv_instances = []
56    bluetooth_gatt_list = []
57    gatt_server_list = []
58    default_timeout = 10
59    default_discovery_timeout = 3
60
61    def __init__(self, controllers):
62        BluetoothBaseTest.__init__(self, controllers)
63        self.cen_ad = self.android_devices[0]
64        self.per_ad = self.android_devices[1]
65
66    def setup_test(self):
67        super(BluetoothBaseTest, self).setup_test()
68        bluetooth_gatt_list = []
69        self.gatt_server_list = []
70        self.adv_instances = []
71        # Ensure there is ample time for a physical disconnect in between
72        # testcases.
73        self.log.info(
74            "Waiting for {} seconds for physical GATT disconnections".format(
75                PHYSICAL_DISCONNECT_TIMEOUT))
76        time.sleep(PHYSICAL_DISCONNECT_TIMEOUT)
77
78    def teardown_test(self):
79        for bluetooth_gatt in self.bluetooth_gatt_list:
80            self.cen_ad.droid.gattClientClose(bluetooth_gatt)
81        for gatt_server in self.gatt_server_list:
82            self.per_ad.droid.gattServerClose(gatt_server)
83        for adv in self.adv_instances:
84            self.per_ad.droid.bleStopBleAdvertising(adv)
85        return True
86
87    def _orchestrate_gatt_disconnection(self, bluetooth_gatt, gatt_callback):
88        self.log.info("Disconnecting from peripheral device.")
89        try:
90            disconnect_gatt_connection(self.cen_ad, bluetooth_gatt,
91                                       gatt_callback)
92            close_gatt_client(self.cen_ad, bluetooth_gatt)
93            if bluetooth_gatt in self.bluetooth_gatt_list:
94                self.bluetooth_gatt_list.remove(bluetooth_gatt)
95        except GattTestUtilsError as err:
96            self.log.error(err)
97            return False
98        return True
99
100    def _find_service_added_event(self, gatt_server_cb, uuid):
101        expected_event = gatt_cb_strings['serv_added'].format(gatt_server_cb)
102        try:
103            event = self.per_ad.ed.pop_event(expected_event,
104                                             self.default_timeout)
105        except Empty:
106            self.log.error(
107                gatt_cb_strings['serv_added_err'].format(expected_event))
108            return False
109        if event['data']['serviceUuid'].lower() != uuid.lower():
110            self.log.error("Uuid mismatch. Found: {}, Expected {}.".format(
111                event['data']['serviceUuid'], uuid))
112            return False
113        return True
114
115    def _verify_mtu_changed_on_client_and_server(
116            self, expected_mtu, gatt_callback, gatt_server_callback):
117        expected_event = gatt_cb_strings['mtu_changed'].format(gatt_callback)
118        try:
119            mtu_event = self.cen_ad.ed.pop_event(expected_event,
120                                                 self.default_timeout)
121            mtu_size_found = mtu_event['data']['MTU']
122            if mtu_size_found != expected_mtu:
123                self.log.error("MTU size found: {}, expected: {}".format(
124                    mtu_size_found, expected_mtu))
125                return False
126        except Empty:
127            self.log.error(
128                gatt_cb_err['mtu_changed_err'].format(expected_event))
129            return False
130
131        expected_event = gatt_cb_strings['mtu_serv_changed'].format(
132            gatt_server_callback)
133        try:
134            mtu_event = self.per_ad.ed.pop_event(expected_event,
135                                                 self.default_timeout)
136            mtu_size_found = mtu_event['data']['MTU']
137            if mtu_size_found != expected_mtu:
138                self.log.error("MTU size found: {}, expected: {}".format(
139                    mtu_size_found, expected_mtu))
140                return False
141        except Empty:
142            self.log.error(
143                gatt_cb_err['mtu_serv_changed_err'].format(expected_event))
144            return False
145        return True
146
147    @BluetoothBaseTest.bt_test_wrap
148    @test_tracker_info(uuid='8a3530a3-c8bb-466b-9710-99e694c38618')
149    def test_gatt_connect(self):
150        """Test GATT connection over LE.
151
152        Test establishing a gatt connection between a GATT server and GATT
153        client.
154
155        Steps:
156        1. Start a generic advertisement.
157        2. Start a generic scanner.
158        3. Find the advertisement and extract the mac address.
159        4. Stop the first scanner.
160        5. Create a GATT connection between the scanner and advertiser.
161        6. Disconnect the GATT connection.
162
163        Expected Result:
164        Verify that a connection was established and then disconnected
165        successfully.
166
167        Returns:
168          Pass if True
169          Fail if False
170
171        TAGS: LE, Advertising, Filtering, Scanning, GATT
172        Priority: 0
173        """
174        gatt_server_cb = self.per_ad.droid.gattServerCreateGattServerCallback()
175        gatt_server = self.per_ad.droid.gattServerOpenGattServer(
176            gatt_server_cb)
177        self.gatt_server_list.append(gatt_server)
178        try:
179            bluetooth_gatt, gatt_callback, adv_callback = (
180                orchestrate_gatt_connection(self.cen_ad, self.per_ad))
181            self.bluetooth_gatt_list.append(bluetooth_gatt)
182        except GattTestUtilsError as err:
183            self.log.error(err)
184            return False
185        self.adv_instances.append(adv_callback)
186        return self._orchestrate_gatt_disconnection(bluetooth_gatt,
187                                                    gatt_callback)
188
189    @BluetoothBaseTest.bt_test_wrap
190    @test_tracker_info(uuid='a839b505-03ac-4783-be7e-1d43129a1948')
191    def test_gatt_connect_stop_advertising(self):
192        """Test GATT connection over LE then stop advertising
193
194        A test case that verifies the GATT connection doesn't
195        disconnect when LE advertisement is stopped.
196
197        Steps:
198        1. Start a generic advertisement.
199        2. Start a generic scanner.
200        3. Find the advertisement and extract the mac address.
201        4. Stop the first scanner.
202        5. Create a GATT connection between the scanner and advertiser.
203        6. Stop the advertiser.
204        7. Verify no connection state changed happened.
205        8. Disconnect the GATT connection.
206
207        Expected Result:
208        Verify that a connection was established and not disconnected
209        when advertisement stops.
210
211        Returns:
212          Pass if True
213          Fail if False
214
215        TAGS: LE, Advertising, Filtering, Scanning, GATT
216        Priority: 0
217        """
218        gatt_server_cb = self.per_ad.droid.gattServerCreateGattServerCallback()
219        gatt_server = self.per_ad.droid.gattServerOpenGattServer(
220            gatt_server_cb)
221        self.gatt_server_list.append(gatt_server)
222        try:
223            bluetooth_gatt, gatt_callback, adv_callback = (
224                orchestrate_gatt_connection(self.cen_ad, self.per_ad))
225            self.bluetooth_gatt_list.append(bluetooth_gatt)
226        except GattTestUtilsError as err:
227            self.log.error(err)
228            return False
229        self.per_ad.droid.bleStopBleAdvertising(adv_callback)
230        try:
231            event = self.cen_ad.ed.pop_event(
232                gatt_cb_strings['gatt_conn_change'].format(
233                    gatt_callback, self.default_timeout))
234            self.log.error(
235                "Connection event found when not expected: {}".format(event))
236            return False
237        except Empty:
238            self.log.info("No connection state change as expected")
239        try:
240            self._orchestrate_gatt_disconnection(bluetooth_gatt, gatt_callback)
241        except Exception as err:
242            self.log.info("Failed to orchestrate disconnect: {}".format(err))
243            return False
244        return True
245
246    @BluetoothBaseTest.bt_test_wrap
247    @test_tracker_info(uuid='b82f91a8-54bb-4779-a117-73dc7fdb28cc')
248    def test_gatt_connect_autoconnect(self):
249        """Test GATT connection over LE.
250
251        Test re-establishing a gatt connection using autoconnect
252        set to True in order to test connection whitelist.
253
254        Steps:
255        1. Start a generic advertisement.
256        2. Start a generic scanner.
257        3. Find the advertisement and extract the mac address.
258        4. Stop the first scanner.
259        5. Create a GATT connection between the scanner and advertiser.
260        6. Disconnect the GATT connection.
261        7. Create a GATT connection with autoconnect set to True
262        8. Disconnect the GATT connection.
263
264        Expected Result:
265        Verify that a connection was re-established and then disconnected
266        successfully.
267
268        Returns:
269          Pass if True
270          Fail if False
271
272        TAGS: LE, Advertising, Filtering, Scanning, GATT
273        Priority: 0
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        autoconnect = False
280        mac_address, adv_callback, scan_callback = (
281            get_mac_address_of_generic_advertisement(self.cen_ad, self.per_ad))
282        try:
283            bluetooth_gatt, gatt_callback = setup_gatt_connection(
284                self.cen_ad, mac_address, autoconnect)
285            self.cen_ad.droid.bleStopBleScan(scan_callback)
286            self.bluetooth_gatt_list.append(bluetooth_gatt)
287        except GattTestUtilsError as err:
288            self.log.error(err)
289            return False
290        try:
291            disconnect_gatt_connection(self.cen_ad, bluetooth_gatt,
292                                       gatt_callback)
293            close_gatt_client(self.cen_ad, bluetooth_gatt)
294            if bluetooth_gatt in self.bluetooth_gatt_list:
295                self.bluetooth_gatt_list.remove(bluetooth_gatt)
296        except GattTestUtilsError as err:
297            self.log.error(err)
298            return False
299        autoconnect = True
300        bluetooth_gatt = self.cen_ad.droid.gattClientConnectGatt(
301            gatt_callback, mac_address, autoconnect, gatt_transport['auto'],
302            False, gatt_phy_mask['1m_mask'])
303        self.bluetooth_gatt_list.append(bluetooth_gatt)
304        expected_event = gatt_cb_strings['gatt_conn_change'].format(
305            gatt_callback)
306        try:
307            event = self.cen_ad.ed.pop_event(expected_event,
308                                             self.default_timeout)
309        except Empty:
310            self.log.error(
311                gatt_cb_err['gatt_conn_change_err'].format(expected_event))
312            test_result = False
313        return self._orchestrate_gatt_disconnection(bluetooth_gatt,
314                                                    gatt_callback)
315
316    @BluetoothBaseTest.bt_test_wrap
317    @test_tracker_info(uuid='e506fa50-7cd9-4bd8-938a-6b85dcfe6bc6')
318    def test_gatt_connect_opportunistic(self):
319        """Test opportunistic GATT connection over LE.
320
321        Test establishing a gatt connection between a GATT server and GATT
322        client in opportunistic mode.
323
324        Steps:
325        1. Start a generic advertisement.
326        2. Start a generic scanner.
327        3. Find the advertisement and extract the mac address.
328        4. Stop the first scanner.
329        5. Create GATT connection 1 between the scanner and advertiser normally
330        6. Create GATT connection 2 between the scanner and advertiser using
331           opportunistic mode
332        7. Disconnect GATT connection 1
333
334        Expected Result:
335        Verify GATT connection 2 automatically disconnects when GATT connection
336        1 disconnect
337
338        Returns:
339          Pass if True
340          Fail if False
341
342        TAGS: LE, Advertising, Filtering, Scanning, GATT
343        Priority: 0
344        """
345        gatt_server_cb = self.per_ad.droid.gattServerCreateGattServerCallback()
346        gatt_server = self.per_ad.droid.gattServerOpenGattServer(
347            gatt_server_cb)
348        self.gatt_server_list.append(gatt_server)
349        mac_address, adv_callback, scan_callback = (
350            get_mac_address_of_generic_advertisement(self.cen_ad, self.per_ad))
351        # Make GATT connection 1
352        try:
353            bluetooth_gatt_1, gatt_callback_1 = setup_gatt_connection(
354                self.cen_ad,
355                mac_address,
356                False,
357                transport=gatt_transport['auto'],
358                opportunistic=False)
359            self.cen_ad.droid.bleStopBleScan(scan_callback)
360            self.bluetooth_gatt_list.append(bluetooth_gatt_1)
361        except GattTestUtilsError as err:
362            self.log.error(err)
363            return False
364        # Make GATT connection 2
365        try:
366            bluetooth_gatt_2, gatt_callback_2 = setup_gatt_connection(
367                self.cen_ad,
368                mac_address,
369                False,
370                transport=gatt_transport['auto'],
371                opportunistic=True)
372            self.bluetooth_gatt_list.append(bluetooth_gatt_2)
373        except GattTestUtilsError as err:
374            self.log.error(err)
375            return False
376        # Disconnect GATT connection 1
377        try:
378            disconnect_gatt_connection(self.cen_ad, bluetooth_gatt_1,
379                                       gatt_callback_1)
380            close_gatt_client(self.cen_ad, bluetooth_gatt_1)
381            if bluetooth_gatt_1 in self.bluetooth_gatt_list:
382                self.bluetooth_gatt_list.remove(bluetooth_gatt_1)
383        except GattTestUtilsError as err:
384            self.log.error(err)
385            return False
386        # Confirm that GATT connection 2 also disconnects
387        wait_for_gatt_disconnect_event(self.cen_ad, gatt_callback_2)
388        close_gatt_client(self.cen_ad, bluetooth_gatt_2)
389        if bluetooth_gatt_2 in self.bluetooth_gatt_list:
390            self.bluetooth_gatt_list.remove(bluetooth_gatt_2)
391        return True
392
393    @BluetoothBaseTest.bt_test_wrap
394    @test_tracker_info(uuid='1e01838e-c4de-4720-9adf-9e0419378226')
395    def test_gatt_request_min_mtu(self):
396        """Test GATT connection over LE and exercise MTU sizes.
397
398        Test establishing a gatt connection between a GATT server and GATT
399        client. Request an MTU size that matches the correct minimum size.
400
401        Steps:
402        1. Start a generic advertisement.
403        2. Start a generic scanner.
404        3. Find the advertisement and extract the mac address.
405        4. Stop the first scanner.
406        5. Create a GATT connection between the scanner and advertiser.
407        6. From the scanner (client) request MTU size change to the
408        minimum value.
409        7. Find the MTU changed event on the client.
410        8. Disconnect the GATT connection.
411
412        Expected Result:
413        Verify that a connection was established and the MTU value found
414        matches the expected MTU value.
415
416        Returns:
417          Pass if True
418          Fail if False
419
420        TAGS: LE, Advertising, Filtering, Scanning, GATT, MTU
421        Priority: 0
422        """
423        gatt_server_cb = self.per_ad.droid.gattServerCreateGattServerCallback()
424        gatt_server = self.per_ad.droid.gattServerOpenGattServer(
425            gatt_server_cb)
426        self.gatt_server_list.append(gatt_server)
427        try:
428            bluetooth_gatt, gatt_callback, adv_callback = (
429                orchestrate_gatt_connection(self.cen_ad, self.per_ad))
430            self.bluetooth_gatt_list.append(bluetooth_gatt)
431        except GattTestUtilsError as err:
432            self.log.error(err)
433            return False
434        self.adv_instances.append(adv_callback)
435        expected_mtu = gatt_mtu_size['min']
436        self.cen_ad.droid.gattClientRequestMtu(bluetooth_gatt, expected_mtu)
437        if not self._verify_mtu_changed_on_client_and_server(
438                expected_mtu, gatt_callback, gatt_server_cb):
439            return False
440        return self._orchestrate_gatt_disconnection(bluetooth_gatt,
441                                                    gatt_callback)
442
443    @BluetoothBaseTest.bt_test_wrap
444    @test_tracker_info(uuid='c1fa3a2d-fb47-47db-bdd1-458928cd6a5f')
445    def test_gatt_request_max_mtu(self):
446        """Test GATT connection over LE and exercise MTU sizes.
447
448        Test establishing a gatt connection between a GATT server and GATT
449        client. Request an MTU size that matches the correct maximum size.
450
451        Steps:
452        1. Start a generic advertisement.
453        2. Start a generic scanner.
454        3. Find the advertisement and extract the mac address.
455        4. Stop the first scanner.
456        5. Create a GATT connection between the scanner and advertiser.
457        6. From the scanner (client) request MTU size change to the
458        maximum value.
459        7. Find the MTU changed event on the client.
460        8. Disconnect the GATT connection.
461
462        Expected Result:
463        Verify that a connection was established and the MTU value found
464        matches the expected MTU value.
465
466        Returns:
467          Pass if True
468          Fail if False
469
470        TAGS: LE, Advertising, Filtering, Scanning, GATT, MTU
471        Priority: 0
472        """
473        gatt_server_cb = self.per_ad.droid.gattServerCreateGattServerCallback()
474        gatt_server = self.per_ad.droid.gattServerOpenGattServer(
475            gatt_server_cb)
476        self.gatt_server_list.append(gatt_server)
477        try:
478            bluetooth_gatt, gatt_callback, adv_callback = (
479                orchestrate_gatt_connection(self.cen_ad, self.per_ad))
480            self.bluetooth_gatt_list.append(bluetooth_gatt)
481        except GattTestUtilsError as err:
482            self.log.error(err)
483            return False
484        self.adv_instances.append(adv_callback)
485        expected_mtu = gatt_mtu_size['max']
486        self.cen_ad.droid.gattClientRequestMtu(bluetooth_gatt, expected_mtu)
487        if not self._verify_mtu_changed_on_client_and_server(
488                expected_mtu, gatt_callback, gatt_server_cb):
489            return False
490        return self._orchestrate_gatt_disconnection(bluetooth_gatt,
491                                                    gatt_callback)
492
493    @BluetoothBaseTest.bt_test_wrap
494    @test_tracker_info(uuid='4416d483-dec3-46cb-8038-4d82620f873a')
495    def test_gatt_request_out_of_bounds_mtu(self):
496        """Test GATT connection over LE and exercise an out of bound MTU size.
497
498        Test establishing a gatt connection between a GATT server and GATT
499        client. Request an MTU size that is the MIN value minus 1.
500
501        Steps:
502        1. Start a generic advertisement.
503        2. Start a generic scanner.
504        3. Find the advertisement and extract the mac address.
505        4. Stop the first scanner.
506        5. Create a GATT connection between the scanner and advertiser.
507        6. From the scanner (client) request MTU size change to the
508        minimum value minus one.
509        7. Find the MTU changed event on the client.
510        8. Disconnect the GATT connection.
511
512        Expected Result:
513        Verify that an MTU changed event was not discovered and that
514        it didn't cause an exception when requesting an out of bounds
515        MTU.
516
517        Returns:
518          Pass if True
519          Fail if False
520
521        TAGS: LE, Advertising, Filtering, Scanning, GATT, MTU
522        Priority: 0
523        """
524        gatt_server_cb = self.per_ad.droid.gattServerCreateGattServerCallback()
525        gatt_server = self.per_ad.droid.gattServerOpenGattServer(
526            gatt_server_cb)
527        self.gatt_server_list.append(gatt_server)
528        try:
529            bluetooth_gatt, gatt_callback, adv_callback = (
530                orchestrate_gatt_connection(self.cen_ad, self.per_ad))
531            self.bluetooth_gatt_list.append(bluetooth_gatt)
532        except GattTestUtilsError as err:
533            self.log.error(err)
534            return False
535        self.adv_instances.append(adv_callback)
536        unexpected_mtu = gatt_mtu_size['min'] - 1
537        self.cen_ad.droid.gattClientRequestMtu(bluetooth_gatt, unexpected_mtu)
538        if self._verify_mtu_changed_on_client_and_server(
539                unexpected_mtu, gatt_callback, gatt_server_cb):
540            return False
541        return self._orchestrate_gatt_disconnection(bluetooth_gatt,
542                                                    gatt_callback)
543
544    @BluetoothBaseTest.bt_test_wrap
545    @test_tracker_info(uuid='31ffb9ca-cc75-43fb-9802-c19f1c5856b6')
546    def test_gatt_connect_trigger_on_read_rssi(self):
547        """Test GATT connection over LE read RSSI.
548
549        Test establishing a gatt connection between a GATT server and GATT
550        client then read the RSSI.
551
552        Steps:
553        1. Start a generic advertisement.
554        2. Start a generic scanner.
555        3. Find the advertisement and extract the mac address.
556        4. Stop the first scanner.
557        5. Create a GATT connection between the scanner and advertiser.
558        6. From the scanner, request to read the RSSI of the advertiser.
559        7. Disconnect the GATT connection.
560
561        Expected Result:
562        Verify that a connection was established and then disconnected
563        successfully. Verify that the RSSI was ready correctly.
564
565        Returns:
566          Pass if True
567          Fail if False
568
569        TAGS: LE, Advertising, Filtering, Scanning, GATT, RSSI
570        Priority: 1
571        """
572        gatt_server_cb = self.per_ad.droid.gattServerCreateGattServerCallback()
573        gatt_server = self.per_ad.droid.gattServerOpenGattServer(
574            gatt_server_cb)
575        self.gatt_server_list.append(gatt_server)
576        try:
577            bluetooth_gatt, gatt_callback, adv_callback = (
578                orchestrate_gatt_connection(self.cen_ad, self.per_ad))
579            self.bluetooth_gatt_list.append(bluetooth_gatt)
580        except GattTestUtilsError as err:
581            self.log.error(err)
582            return False
583        self.adv_instances.append(adv_callback)
584        expected_event = gatt_cb_strings['rd_remote_rssi'].format(
585            gatt_callback)
586        if self.cen_ad.droid.gattClientReadRSSI(bluetooth_gatt):
587            try:
588                self.cen_ad.ed.pop_event(expected_event, self.default_timeout)
589            except Empty:
590                self.log.error(
591                    gatt_cb_err['rd_remote_rssi_err'].format(expected_event))
592        return self._orchestrate_gatt_disconnection(bluetooth_gatt,
593                                                    gatt_callback)
594
595    @BluetoothBaseTest.bt_test_wrap
596    @test_tracker_info(uuid='dee9ef28-b872-428a-821b-cc62f27ba936')
597    def test_gatt_connect_trigger_on_services_discovered(self):
598        """Test GATT connection and discover services of peripheral.
599
600        Test establishing a gatt connection between a GATT server and GATT
601        client the discover all services from the connected device.
602
603        Steps:
604        1. Start a generic advertisement.
605        2. Start a generic scanner.
606        3. Find the advertisement and extract the mac address.
607        4. Stop the first scanner.
608        5. Create a GATT connection between the scanner and advertiser.
609        6. From the scanner (central device), discover services.
610        7. Disconnect the GATT connection.
611
612        Expected Result:
613        Verify that a connection was established and then disconnected
614        successfully. Verify that the service were discovered.
615
616        Returns:
617          Pass if True
618          Fail if False
619
620        TAGS: LE, Advertising, Filtering, Scanning, GATT, Services
621        Priority: 1
622        """
623        gatt_server_cb = self.per_ad.droid.gattServerCreateGattServerCallback()
624        gatt_server = self.per_ad.droid.gattServerOpenGattServer(
625            gatt_server_cb)
626        self.gatt_server_list.append(gatt_server)
627        try:
628            bluetooth_gatt, gatt_callback, adv_callback = (
629                orchestrate_gatt_connection(self.cen_ad, self.per_ad))
630            self.bluetooth_gatt_list.append(bluetooth_gatt)
631        except GattTestUtilsError as err:
632            self.log.error(err)
633            return False
634        self.adv_instances.append(adv_callback)
635        if self.cen_ad.droid.gattClientDiscoverServices(bluetooth_gatt):
636            expected_event = gatt_cb_strings['gatt_serv_disc'].format(
637                gatt_callback)
638            try:
639                event = self.cen_ad.ed.pop_event(expected_event,
640                                                 self.default_timeout)
641            except Empty:
642                self.log.error(
643                    gatt_cb_err['gatt_serv_disc'].format(expected_event))
644                return False
645        return self._orchestrate_gatt_disconnection(bluetooth_gatt,
646                                                    gatt_callback)
647
648    @BluetoothBaseTest.bt_test_wrap
649    @test_tracker_info(uuid='01883bdd-0cf8-48fb-bf15-467bbd4f065b')
650    def test_gatt_connect_trigger_on_services_discovered_iterate_attributes(
651            self):
652        """Test GATT connection and iterate peripherals attributes.
653
654        Test establishing a gatt connection between a GATT server and GATT
655        client and iterate over all the characteristics and descriptors of the
656        discovered services.
657
658        Steps:
659        1. Start a generic advertisement.
660        2. Start a generic scanner.
661        3. Find the advertisement and extract the mac address.
662        4. Stop the first scanner.
663        5. Create a GATT connection between the scanner and advertiser.
664        6. From the scanner (central device), discover services.
665        7. Iterate over all the characteristics and descriptors of the
666        discovered features.
667        8. Disconnect the GATT connection.
668
669        Expected Result:
670        Verify that a connection was established and then disconnected
671        successfully. Verify that the services, characteristics, and descriptors
672        were discovered.
673
674        Returns:
675          Pass if True
676          Fail if False
677
678        TAGS: LE, Advertising, Filtering, Scanning, GATT, Services
679        Characteristics, Descriptors
680        Priority: 1
681        """
682        gatt_server_cb = self.per_ad.droid.gattServerCreateGattServerCallback()
683        gatt_server = self.per_ad.droid.gattServerOpenGattServer(
684            gatt_server_cb)
685        self.gatt_server_list.append(gatt_server)
686        try:
687            bluetooth_gatt, gatt_callback, adv_callback = (
688                orchestrate_gatt_connection(self.cen_ad, self.per_ad))
689            self.bluetooth_gatt_list.append(bluetooth_gatt)
690        except GattTestUtilsError as err:
691            self.log.error(err)
692            return False
693        self.adv_instances.append(adv_callback)
694        if self.cen_ad.droid.gattClientDiscoverServices(bluetooth_gatt):
695            expected_event = gatt_cb_strings['gatt_serv_disc'].format(
696                gatt_callback)
697            try:
698                event = self.cen_ad.ed.pop_event(expected_event,
699                                                 self.default_timeout)
700                discovered_services_index = event['data']['ServicesIndex']
701            except Empty:
702                self.log.error(
703                    gatt_cb_err['gatt_serv_disc'].format(expected_event))
704                return False
705            log_gatt_server_uuids(self.cen_ad, discovered_services_index)
706        return self._orchestrate_gatt_disconnection(bluetooth_gatt,
707                                                    gatt_callback)
708
709    @BluetoothBaseTest.bt_test_wrap
710    @test_tracker_info(uuid='d4277bee-da99-4f48-8a4d-f81b5389da18')
711    def test_gatt_connect_with_service_uuid_variations(self):
712        """Test GATT connection with multiple service uuids.
713
714        Test establishing a gatt connection between a GATT server and GATT
715        client with multiple service uuid variations.
716
717        Steps:
718        1. Start a generic advertisement.
719        2. Start a generic scanner.
720        3. Find the advertisement and extract the mac address.
721        4. Stop the first scanner.
722        5. Create a GATT connection between the scanner and advertiser.
723        6. From the scanner (central device), discover services.
724        7. Verify that all the service uuid variations are found.
725        8. Disconnect the GATT connection.
726
727        Expected Result:
728        Verify that a connection was established and then disconnected
729        successfully. Verify that the service uuid variations are found.
730
731        Returns:
732          Pass if True
733          Fail if False
734
735        TAGS: LE, Advertising, Filtering, Scanning, GATT, Services
736        Priority: 2
737        """
738        try:
739            gatt_server_cb, gatt_server = setup_multiple_services(self.per_ad)
740            self.gatt_server_list.append(gatt_server)
741        except GattTestUtilsError as err:
742            self.log.error(err)
743            return False
744        try:
745            bluetooth_gatt, gatt_callback, adv_callback = (
746                orchestrate_gatt_connection(self.cen_ad, self.per_ad))
747            self.bluetooth_gatt_list.append(bluetooth_gatt)
748        except GattTestUtilsError as err:
749            self.log.error(err)
750            return False
751        self.adv_instances.append(adv_callback)
752        if self.cen_ad.droid.gattClientDiscoverServices(bluetooth_gatt):
753            expected_event = gatt_cb_strings['gatt_serv_disc'].format(
754                gatt_callback)
755            try:
756                event = self.cen_ad.ed.pop_event(expected_event,
757                                                 self.default_timeout)
758            except Empty:
759                self.log.error(
760                    gatt_cb_err['gatt_serv_disc'].format(expected_event))
761                return False
762            discovered_services_index = event['data']['ServicesIndex']
763            log_gatt_server_uuids(self.cen_ad, discovered_services_index)
764
765        return self._orchestrate_gatt_disconnection(bluetooth_gatt,
766                                                    gatt_callback)
767
768    @BluetoothBaseTest.bt_test_wrap
769    @test_tracker_info(uuid='7d3442c5-f71f-44ae-bd35-f2569f01b3b8')
770    def test_gatt_connect_in_quick_succession(self):
771        """Test GATT connections multiple times.
772
773        Test establishing a gatt connection between a GATT server and GATT
774        client with multiple iterations.
775
776        Steps:
777        1. Start a generic advertisement.
778        2. Start a generic scanner.
779        3. Find the advertisement and extract the mac address.
780        4. Stop the first scanner.
781        5. Create a GATT connection between the scanner and advertiser.
782        6. Disconnect the GATT connection.
783        7. Repeat steps 5 and 6 twenty times.
784
785        Expected Result:
786        Verify that a connection was established and then disconnected
787        successfully twenty times.
788
789        Returns:
790          Pass if True
791          Fail if False
792
793        TAGS: LE, Advertising, Filtering, Scanning, GATT, Stress
794        Priority: 1
795        """
796        gatt_server_cb = self.per_ad.droid.gattServerCreateGattServerCallback()
797        gatt_server = self.per_ad.droid.gattServerOpenGattServer(
798            gatt_server_cb)
799        self.gatt_server_list.append(gatt_server)
800        mac_address, adv_callback, scan_callback = get_mac_address_of_generic_advertisement(
801            self.cen_ad, self.per_ad)
802        autoconnect = False
803        for i in range(1000):
804            self.log.info("Starting connection iteration {}".format(i + 1))
805            try:
806                bluetooth_gatt, gatt_callback = setup_gatt_connection(
807                    self.cen_ad, mac_address, autoconnect)
808                self.cen_ad.droid.bleStopBleScan(scan_callback)
809            except GattTestUtilsError as err:
810                self.log.error(err)
811                return False
812            test_result = self._orchestrate_gatt_disconnection(
813                bluetooth_gatt, gatt_callback)
814            if not test_result:
815                self.log.info("Failed to disconnect from peripheral device.")
816                return False
817        self.adv_instances.append(adv_callback)
818        return True
819
820    @BluetoothBaseTest.bt_test_wrap
821    @test_tracker_info(uuid='148469d9-7ab0-4c08-b2e9-7e49e88da1fc')
822    def test_gatt_connect_mitm_attack(self):
823        """Test GATT connection with permission write encrypted mitm.
824
825        Test establishing a gatt connection between a GATT server and GATT
826        client while the GATT server's characteristic includes the property
827        write value and the permission write encrypted mitm value. This will
828        prompt LE pairing and then the devices will create a bond.
829
830        Steps:
831        1. Create a GATT server and server callback on the peripheral device.
832        2. Create a unique service and characteristic uuid on the peripheral.
833        3. Create a characteristic on the peripheral with these properties:
834            gatt_characteristic['property_write'],
835            gatt_characteristic['permission_write_encrypted_mitm']
836        4. Create a GATT service on the peripheral.
837        5. Add the characteristic to the GATT service.
838        6. Create a GATT connection between your central and peripheral device.
839        7. From the central device, discover the peripheral's services.
840        8. Iterate the services found until you find the unique characteristic
841            created in step 3.
842        9. Once found, write a random but valid value to the characteristic.
843        10. Start pairing helpers on both devices immediately after attempting
844            to write to the characteristic.
845        11. Within 10 seconds of writing the characteristic, there should be
846            a prompt to bond the device from the peripheral. The helpers will
847            handle the UI interaction automatically. (see
848            BluetoothConnectionFacade.java bluetoothStartPairingHelper).
849        12. Verify that the two devices are bonded.
850
851        Expected Result:
852        Verify that a connection was established and the devices are bonded.
853
854        Returns:
855          Pass if True
856          Fail if False
857
858        TAGS: LE, Advertising, Filtering, Scanning, GATT, Characteristic, MITM
859        Priority: 1
860        """
861        gatt_server_cb = self.per_ad.droid.gattServerCreateGattServerCallback()
862        gatt_server = self.per_ad.droid.gattServerOpenGattServer(
863            gatt_server_cb)
864        self.gatt_server_list.append(gatt_server)
865        service_uuid = "3846D7A0-69C8-11E4-BA00-0002A5D5C51B"
866        test_uuid = "aa7edd5a-4d1d-4f0e-883a-d145616a1630"
867        bonded = False
868        characteristic = self.per_ad.droid.gattServerCreateBluetoothGattCharacteristic(
869            test_uuid, gatt_characteristic['property_write'],
870            gatt_characteristic['permission_write_encrypted_mitm'])
871        gatt_service = self.per_ad.droid.gattServerCreateService(
872            service_uuid, gatt_service_types['primary'])
873        self.per_ad.droid.gattServerAddCharacteristicToService(
874            gatt_service, characteristic)
875        self.per_ad.droid.gattServerAddService(gatt_server, gatt_service)
876        result = self._find_service_added_event(gatt_server_cb, service_uuid)
877        if not result:
878            return False
879        bluetooth_gatt, gatt_callback, adv_callback = (
880            orchestrate_gatt_connection(self.cen_ad, self.per_ad))
881        self.bluetooth_gatt_list.append(bluetooth_gatt)
882        self.adv_instances.append(adv_callback)
883        if self.cen_ad.droid.gattClientDiscoverServices(bluetooth_gatt):
884            expected_event = gatt_cb_strings['gatt_serv_disc'].format(
885                gatt_callback)
886            try:
887                event = self.cen_ad.ed.pop_event(expected_event,
888                                                 self.default_timeout)
889            except Empty:
890                self.log.error(
891                    gatt_cb_err['gatt_serv_disc'].format(expected_event))
892                return False
893            discovered_services_index = event['data']['ServicesIndex']
894        else:
895            self.log.info("Failed to discover services.")
896            return False
897        test_value = [1, 2, 3, 4, 5, 6, 7]
898        services_count = self.cen_ad.droid.gattClientGetDiscoveredServicesCount(
899            discovered_services_index)
900        for i in range(services_count):
901            characteristic_uuids = (
902                self.cen_ad.droid.gattClientGetDiscoveredCharacteristicUuids(
903                    discovered_services_index, i))
904            for characteristic_uuid in characteristic_uuids:
905                if characteristic_uuid == test_uuid:
906                    self.cen_ad.droid.bluetoothStartPairingHelper()
907                    self.per_ad.droid.bluetoothStartPairingHelper()
908                    self.cen_ad.droid.gattClientCharacteristicSetValue(
909                        bluetooth_gatt, discovered_services_index, i,
910                        characteristic_uuid, test_value)
911                    self.cen_ad.droid.gattClientWriteCharacteristic(
912                        bluetooth_gatt, discovered_services_index, i,
913                        characteristic_uuid)
914                    start_time = time.time() + self.default_timeout
915                    target_name = self.per_ad.droid.bluetoothGetLocalName()
916                    while time.time() < start_time and bonded == False:
917                        bonded_devices = \
918                            self.cen_ad.droid.bluetoothGetBondedDevices()
919                        for device in bonded_devices:
920                            if ('name' in device.keys()
921                                    and device['name'] == target_name):
922                                bonded = True
923                                break
924                    bonded = False
925                    target_name = self.cen_ad.droid.bluetoothGetLocalName()
926                    while time.time() < start_time and bonded == False:
927                        bonded_devices = \
928                            self.per_ad.droid.bluetoothGetBondedDevices()
929                        for device in bonded_devices:
930                            if ('name' in device.keys()
931                                    and device['name'] == target_name):
932                                bonded = True
933                                break
934        for ad in [self.cen_ad, self.per_ad]:
935            if not clear_bonded_devices(ad):
936                return False
937            # Necessary sleep time for entries to update unbonded state
938            time.sleep(2)
939            bonded_devices = ad.droid.bluetoothGetBondedDevices()
940            if len(bonded_devices) > 0:
941                self.log.error(
942                    "Failed to unbond devices: {}".format(bonded_devices))
943                return False
944        return self._orchestrate_gatt_disconnection(bluetooth_gatt,
945                                                    gatt_callback)
946
947    @BluetoothBaseTest.bt_test_wrap
948    @test_tracker_info(uuid='cc3fc361-7bf1-4ee2-9e46-4a27c88ce6a8')
949    def test_gatt_connect_get_connected_devices(self):
950        """Test GATT connections show up in getConnectedDevices
951
952        Test establishing a gatt connection between a GATT server and GATT
953        client. Verify that active connections show up using
954        BluetoothManager.getConnectedDevices API.
955
956        Steps:
957        1. Start a generic advertisement.
958        2. Start a generic scanner.
959        3. Find the advertisement and extract the mac address.
960        4. Stop the first scanner.
961        5. Create a GATT connection between the scanner and advertiser.
962        7. Verify the GATT Client has an open connection to the GATT Server.
963        8. Verify the GATT Server has an open connection to the GATT Client.
964        9. Disconnect the GATT connection.
965
966        Expected Result:
967        Verify that a connection was established, connected devices are found
968        on both the central and peripheral devices, and then disconnected
969        successfully.
970
971        Returns:
972          Pass if True
973          Fail if False
974
975        TAGS: LE, Advertising, Scanning, GATT
976        Priority: 2
977        """
978        gatt_server_cb = self.per_ad.droid.gattServerCreateGattServerCallback()
979        gatt_server = self.per_ad.droid.gattServerOpenGattServer(
980            gatt_server_cb)
981        self.gatt_server_list.append(gatt_server)
982        try:
983            bluetooth_gatt, gatt_callback, adv_callback = (
984                orchestrate_gatt_connection(self.cen_ad, self.per_ad))
985            self.bluetooth_gatt_list.append(bluetooth_gatt)
986        except GattTestUtilsError as err:
987            self.log.error(err)
988            return False
989        conn_cen_devices = self.cen_ad.droid.bluetoothGetConnectedLeDevices(
990            bt_profile_constants['gatt'])
991        conn_per_devices = self.per_ad.droid.bluetoothGetConnectedLeDevices(
992            bt_profile_constants['gatt_server'])
993        target_name = self.per_ad.droid.bluetoothGetLocalName()
994        error_message = ("Connected device {} not found in list of connected "
995                         "devices {}")
996        if not any(d['name'] == target_name for d in conn_cen_devices):
997            self.log.error(error_message.format(target_name, conn_cen_devices))
998            return False
999        # For the GATT server only check the size of the list since
1000        # it may or may not include the device name.
1001        target_name = self.cen_ad.droid.bluetoothGetLocalName()
1002        if not conn_per_devices:
1003            self.log.error(error_message.format(target_name, conn_per_devices))
1004            return False
1005        self.adv_instances.append(adv_callback)
1006        return self._orchestrate_gatt_disconnection(bluetooth_gatt,
1007                                                    gatt_callback)
1008
1009    @BluetoothBaseTest.bt_test_wrap
1010    @test_tracker_info(uuid='a0a37ca6-9fa8-4d35-9fdb-0e25b4b8a363')
1011    def test_gatt_connect_second_adv_after_canceling_first_adv(self):
1012        """Test GATT connection to peripherals second advertising address.
1013
1014        The the ability of cancelling GATT connections and trying to reconnect
1015        to the same device via a different address.
1016
1017        Steps:
1018        1. A starts advertising
1019        2. B starts scanning and finds A's mac address
1020        3. Stop advertisement from step 1. Start a new advertisement on A and
1021            find the new new mac address, B knows of both old and new address.
1022        4. B1 sends connect request to old address of A
1023        5. B1 cancel connect attempt after 10 seconds
1024        6. B1 sends connect request to new address of A
1025        7. Verify B1 establish connection to A in less than 10 seconds
1026
1027        Expected Result:
1028        Verify that a connection was established only on the second
1029            advertisement's mac address.
1030
1031        Returns:
1032          Pass if True
1033          Fail if False
1034
1035        TAGS: LE, Advertising, Scanning, GATT
1036        Priority: 3
1037        """
1038        autoconnect = False
1039        transport = gatt_transport['auto']
1040        opportunistic = False
1041        # Setup a basic Gatt server on the peripheral
1042        gatt_server_cb = self.per_ad.droid.gattServerCreateGattServerCallback()
1043        gatt_server = self.per_ad.droid.gattServerOpenGattServer(
1044            gatt_server_cb)
1045
1046        # Set advertisement settings to include local name in advertisement
1047        # and set the advertising mode to low_latency.
1048        self.per_ad.droid.bleSetAdvertiseSettingsIsConnectable(True)
1049        self.per_ad.droid.bleSetAdvertiseDataIncludeDeviceName(True)
1050        self.per_ad.droid.bleSetAdvertiseSettingsAdvertiseMode(
1051            ble_advertise_settings_modes['low_latency'])
1052
1053        # Setup necessary advertisement objects.
1054        advertise_data = self.per_ad.droid.bleBuildAdvertiseData()
1055        advertise_settings = self.per_ad.droid.bleBuildAdvertiseSettings()
1056        advertise_callback = self.per_ad.droid.bleGenBleAdvertiseCallback()
1057
1058        # Step 1: Start advertisement
1059        self.per_ad.droid.bleStartBleAdvertising(
1060            advertise_callback, advertise_data, advertise_settings)
1061
1062        # Setup scan settings for low_latency scanning and to include the local name
1063        # of the advertisement started in step 1.
1064        filter_list = self.cen_ad.droid.bleGenFilterList()
1065        self.cen_ad.droid.bleSetScanSettingsNumOfMatches(
1066            ble_scan_settings_match_nums['one'])
1067        self.cen_ad.droid.bleSetScanFilterDeviceName(
1068            self.per_ad.droid.bluetoothGetLocalName())
1069        self.cen_ad.droid.bleBuildScanFilter(filter_list)
1070        self.cen_ad.droid.bleSetScanSettingsScanMode(
1071            ble_scan_settings_modes['low_latency'])
1072
1073        # Setup necessary scan objects.
1074        scan_settings = self.cen_ad.droid.bleBuildScanSetting()
1075        scan_callback = self.cen_ad.droid.bleGenScanCallback()
1076
1077        # Step 2: Start scanning on central Android device and find peripheral
1078        # address.
1079        self.cen_ad.droid.bleStartBleScan(filter_list, scan_settings,
1080                                          scan_callback)
1081        expected_event_name = scan_result.format(scan_callback)
1082        try:
1083            mac_address_pre_restart = self.cen_ad.ed.pop_event(
1084                expected_event_name, self.default_timeout)['data']['Result'][
1085                    'deviceInfo']['address']
1086            self.log.info(
1087                "Peripheral advertisement found with mac address: {}".format(
1088                    mac_address_pre_restart))
1089        except Empty:
1090            self.log.info("Peripheral advertisement not found")
1091            test_result = False
1092
1093        # Step 3: Restart peripheral advertising such that a new mac address is
1094        # created.
1095        self.per_ad.droid.bleStopBleAdvertising(advertise_callback)
1096        self.per_ad.droid.bleStartBleAdvertising(
1097            advertise_callback, advertise_data, advertise_settings)
1098
1099        try:
1100            mac_address_post_restart = self.cen_ad.ed.pop_event(
1101                expected_event_name, self.default_timeout)['data']['Result'][
1102                    'deviceInfo']['address']
1103            self.log.info(
1104                "Peripheral advertisement found with mac address: {}".format(
1105                    mac_address_post_restart))
1106        except Empty:
1107            self.log.info("Peripheral advertisement not found")
1108            test_result = False
1109
1110        # Steps 4: Try to connect to the first mac address
1111        gatt_callback = self.cen_ad.droid.gattCreateGattCallback()
1112        self.log.info(
1113            "Gatt Connect to mac address {}.".format(mac_address_pre_restart))
1114        bluetooth_gatt = self.cen_ad.droid.gattClientConnectGatt(
1115            gatt_callback, mac_address_pre_restart, autoconnect, transport,
1116            opportunistic, gatt_phy_mask['1m_mask'])
1117        self.bluetooth_gatt_list.append(bluetooth_gatt)
1118        expected_event = gatt_cb_strings['gatt_conn_change'].format(
1119            gatt_callback)
1120        try:
1121            event = self.cen_ad.ed.pop_event(expected_event,
1122                                             self.default_timeout)
1123            self.log.error(
1124                "Connection callback updated unexpectedly: {}".format(event))
1125            return False
1126        except Empty:
1127            self.log.info("No connection update as expected.")
1128
1129        # Step 5: Cancel connection request.
1130        self.cen_ad.droid.gattClientDisconnect(bluetooth_gatt)
1131
1132        # Step 6: Connect to second mac address.
1133        self.log.info(
1134            "Gatt Connect to mac address {}.".format(mac_address_post_restart))
1135        bluetooth_gatt = self.cen_ad.droid.gattClientConnectGatt(
1136            gatt_callback, mac_address_post_restart, autoconnect, transport,
1137            opportunistic, gatt_phy_mask['1m_mask'])
1138        self.bluetooth_gatt_list.append(bluetooth_gatt)
1139        expected_event = gatt_cb_strings['gatt_conn_change'].format(
1140            gatt_callback)
1141
1142        # Step 7: Verify connection was setup successfully.
1143        try:
1144            event = self.cen_ad.ed.pop_event(expected_event,
1145                                             self.default_timeout)
1146            self.log.info(
1147                "Connection callback updated successfully: {}".format(event))
1148            if event['data']['State'] != gatt_connection_state['connected']:
1149                self.log.error(
1150                    "Could not establish a connection to the peripheral.")
1151                return False
1152        except Empty:
1153            self.log.error("No connection update was found.")
1154            return False
1155        return self.cen_ad.droid.gattClientDisconnect(bluetooth_gatt)
1156