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 script to exercises different ways Ble Advertisements can run in
18concurrency. This test was designed to be run in a shield box.
19"""
20
21import concurrent
22import os
23import time
24
25from queue import Empty
26from acts.test_decorators import test_tracker_info
27from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
28from acts.test_utils.bt.bt_test_utils import BtTestUtilsError
29from acts.test_utils.bt.bt_constants import ble_advertise_settings_modes
30from acts.test_utils.bt.bt_constants import ble_scan_settings_callback_types
31from acts.test_utils.bt.bt_constants import ble_scan_settings_modes
32from acts.test_utils.bt.bt_constants import adv_succ
33from acts.test_utils.bt.bt_test_utils import generate_ble_advertise_objects
34from acts.test_utils.bt.bt_test_utils import generate_ble_scan_objects
35from acts.test_utils.bt.bt_test_utils import get_advanced_droid_list
36from acts.test_utils.bt.bt_test_utils import reset_bluetooth
37from acts.test_utils.bt.bt_test_utils import scan_and_verify_n_advertisements
38from acts.test_utils.bt.bt_constants import scan_result
39from acts.test_utils.bt.bt_test_utils import setup_n_advertisements
40from acts.test_utils.bt.bt_test_utils import take_btsnoop_logs
41from acts.test_utils.bt.bt_test_utils import teardown_n_advertisements
42
43
44class ConcurrentBleAdvertisingTest(BluetoothBaseTest):
45    default_timeout = 10
46    droid_list = []
47    max_advertisements = -1
48
49    def setup_class(self):
50        super().setup_class()
51        self.scn_ad = self.android_devices[0]
52        self.adv_ad = self.android_devices[1]
53        self.droid_list = get_advanced_droid_list(self.android_devices)
54        self.max_advertisements = self.droid_list[1]['max_advertisements']
55
56    def setup_test(self):
57        super().setup_test()
58        return reset_bluetooth(self.android_devices)
59
60    def _verify_n_advertisements(self, num_advertisements):
61        try:
62            advertise_callback_list = setup_n_advertisements(
63                self.adv_ad, num_advertisements)
64        except BtTestUtilsError:
65            return False
66        try:
67            scan_and_verify_n_advertisements(self.scn_ad, num_advertisements)
68        except BtTestUtilsError:
69            return False
70        teardown_n_advertisements(self.adv_ad,
71                                  len(advertise_callback_list),
72                                  advertise_callback_list)
73        return True
74
75    @BluetoothBaseTest.bt_test_wrap
76    @test_tracker_info(uuid='abc03874-6d7a-4b5d-9f29-18731a102793')
77    def test_max_advertisements_defaults(self):
78        """Testing max advertisements.
79
80        Test that a single device can have the max advertisements
81        concurrently advertising.
82
83        Steps:
84        1. Setup the scanning android device.
85        2. Setup the advertiser android device.
86        3. Start scanning on the max_advertisements as defined in the script.
87        4. Verify that all advertisements are found.
88
89        Expected Result:
90        All advertisements should start without errors.
91
92        Returns:
93          Pass if True
94          Fail if False
95
96        TAGS: LE, Advertising, Concurrency
97        Priority: 0
98        """
99        return self._verify_n_advertisements(self.max_advertisements)
100
101    @BluetoothBaseTest.bt_test_wrap
102    @test_tracker_info(uuid='50ee137e-eb71-40ef-b72f-a5fd646190d2')
103    def test_max_advertisements_include_device_name_and_filter_device_name(
104            self):
105        """Testing max advertisement variant.
106
107        Test that a single device can have the max advertisements
108        concurrently advertising. Include the device name as a part of the filter
109        and advertisement data.
110
111        Steps:
112        1. Setup the scanning android device.
113        2. Setup the advertiser android device.
114        3. Include device name in each advertisement.
115        4. Include device name filter in the scanner.
116        5. Start scanning on the max_advertisements as defined in the script.
117        6. Verify that all advertisements are found.
118
119        Expected Result:
120        All advertisements should start without errors.
121
122        Returns:
123          Pass if True
124          Fail if False
125
126        TAGS: LE, Advertising, Concurrency
127        Priority: 2
128        """
129        self.adv_ad.droid.bleSetAdvertiseDataIncludeDeviceName(True)
130        self.scn_ad.droid.bleSetScanFilterDeviceName(
131            self.adv_ad.droid.bluetoothGetLocalName())
132        return self._verify_n_advertisements(self.max_advertisements)
133
134    @BluetoothBaseTest.bt_test_wrap
135    @test_tracker_info(uuid='f7e9ba2b-6286-4510-a8a0-f1df831056c0')
136    def test_max_advertisements_exclude_device_name_and_filter_device_name(
137            self):
138        """Test max advertisement variant.
139
140        Test that a single device can have the max advertisements concurrently
141        advertising. Include the device name as a part of the filter but not the
142        advertisement data.
143
144        Steps:
145        1. Setup the scanning android device.
146        2. Setup the advertiser android device.
147        3. Include device name filter in the scanner.
148        4. Start scanning on the max_advertisements as defined in the script.
149        5. Verify that no advertisements are found.
150
151        Expected Result:
152        All advertisements should start without errors.
153
154        Returns:
155          Pass if True
156          Fail if False
157
158        TAGS: LE, Advertising, Concurrency
159        Priority: 2
160        """
161        self.adv_ad.droid.bleSetAdvertiseDataIncludeDeviceName(False)
162        self.scn_ad.droid.bleSetScanFilterDeviceName(
163            self.adv_ad.droid.bluetoothGetLocalName())
164        return not self._verify_n_advertisements(self.max_advertisements)
165
166    @BluetoothBaseTest.bt_test_wrap
167    @test_tracker_info(uuid='6ce102d7-61e1-4ca0-bcfb-767437b86c2b')
168    def test_max_advertisements_with_manufacturer_data(self):
169        """Test max advertisement variant.
170
171        Test that a single device can have the max advertisements concurrently
172        advertising. Include the manufacturer data as a part of the filter and
173        advertisement data.
174
175        Steps:
176        1. Setup the scanning android device.
177        2. Setup the advertiser android device.
178        3. Include manufacturer data in each advertisement.
179        4. Include manufacturer data filter in the scanner.
180        5. Start scanning on the max_advertisements as defined in the script.
181        6. Verify that all advertisements are found.
182
183        Expected Result:
184        All advertisements should start without errors.
185
186        Returns:
187          Pass if True
188          Fail if False
189
190        TAGS: LE, Advertising, Concurrency
191        Priority: 2
192        """
193        self.scn_ad.droid.bleSetScanFilterManufacturerData(1, [1])
194        self.adv_ad.droid.bleAddAdvertiseDataManufacturerId(1, [1])
195        return self._verify_n_advertisements(self.max_advertisements)
196
197    @BluetoothBaseTest.bt_test_wrap
198    @test_tracker_info(uuid='2fc7d5e8-1539-42a8-8681-ce0b8bfc0924')
199    def test_max_advertisements_with_manufacturer_data_mask(self):
200        """Test max advertisements variant.
201
202        Test that a single device can have the max advertisements concurrently
203        advertising. Include the manufacturer data mask as a part of the filter
204        and advertisement data.
205
206        Steps:
207        1. Setup the scanning android device.
208        2. Setup the advertiser android device.
209        3. Include manufacturer data in each advertisement.
210        4. Include manufacturer data mask filter in the scanner.
211        5. Start scanning on the max_advertisements as defined in the script.
212        6. Verify that all advertisements are found.
213
214        Expected Result:
215        All advertisements should start without errors.
216
217        Returns:
218          Pass if True
219          Fail if False
220
221        TAGS: LE, Advertising, Concurrency
222        Priority: 2
223        """
224        self.scn_ad.droid.bleSetScanFilterManufacturerData(1, [1], [1])
225        self.adv_ad.droid.bleAddAdvertiseDataManufacturerId(1, [1])
226        return self._verify_n_advertisements(self.max_advertisements)
227
228    @BluetoothBaseTest.bt_test_wrap
229    @test_tracker_info(uuid='9ef615ed-1705-44ae-ab5b-f7e8fb4bb770')
230    def test_max_advertisements_with_service_data(self):
231        """Test max advertisement variant.
232
233        Test that a single device can have the max advertisements concurrently
234        advertising. Include the service data as a part of the filter and
235        advertisement data.
236
237        Steps:
238        1. Setup the scanning android device.
239        2. Setup the advertiser android device.
240        3. Include service data in each advertisement.
241        4. Include service data filter in the scanner.
242        5. Start scanning on the max_advertisements as defined in the script.
243        6. Verify that all advertisements are found.
244
245        Expected Result:
246        All advertisements should start without errors.
247
248        Returns:
249          Pass if True
250          Fail if False
251
252        TAGS: LE, Advertising, Concurrency
253        Priority: 2
254        """
255        test_result = True
256        filter_list = self.scn_ad.droid.bleGenFilterList()
257        self.scn_ad.droid.bleSetScanFilterServiceData(
258            "0000110A-0000-1000-8000-00805F9B34FB", [11, 17, 80])
259        self.adv_ad.droid.bleAddAdvertiseDataServiceData(
260            "0000110A-0000-1000-8000-00805F9B34FB", [11, 17, 80])
261        return self._verify_n_advertisements(self.max_advertisements)
262
263    @BluetoothBaseTest.bt_test_wrap
264    @test_tracker_info(uuid='9ef615ed-1705-44ae-ab5b-f7e8fb4bb770')
265    def test_max_advertisements_with_manufacturer_data_mask_and_include_device_name(
266            self):
267        """Test max advertisement variant.
268
269        Test that a single device can have the max advertisements concurrently
270        advertising. Include the device name and manufacturer data as a part of
271        the filter and advertisement data.
272
273        Steps:
274        1. Setup the scanning android device.
275        2. Setup the advertiser android device.
276        3. Include device name and manufacturer data in each advertisement.
277        4. Include device name and manufacturer data filter in the scanner.
278        5. Start scanning on the max_advertisements as defined in the script.
279        6. Verify that all advertisements are found.
280
281        Expected Result:
282        All advertisements should start without errors.
283
284        Returns:
285          Pass if True
286          Fail if False
287
288        TAGS: LE, Advertising, Concurrency
289        Priority: 2
290        """
291        self.adv_ad.droid.bleSetAdvertiseDataIncludeDeviceName(True)
292        self.scn_ad.droid.bleSetScanFilterDeviceName(
293            self.adv_ad.droid.bluetoothGetLocalName())
294        self.scn_ad.droid.bleSetScanFilterManufacturerData(1, [1], [1])
295        self.adv_ad.droid.bleAddAdvertiseDataManufacturerId(1, [1])
296        return self._verify_n_advertisements(self.max_advertisements)
297
298    @BluetoothBaseTest.bt_test_wrap
299    @test_tracker_info(uuid='c2ca85fb-6663-431d-aa30-5286a85dbbe0')
300    def test_max_advertisements_with_service_uuids(self):
301        """Test max advertisement variant.
302
303        Test that a single device can have the max advertisements concurrently
304        advertising. Include the service uuid as a part of the filter and
305        advertisement data.
306
307        Steps:
308        1. Setup the scanning android device.
309        2. Setup the advertiser android device.
310        3. Include service uuid in each advertisement.
311        4. Include service uuid filter in the scanner.
312        5. Start scanning on the max_advertisements as defined in the script.
313        6. Verify that all advertisements are found.
314
315        Expected Result:
316        All advertisements should start without errors.
317
318        Returns:
319          Pass if True
320          Fail if False
321
322        TAGS: LE, Advertising, Concurrency
323        Priority: 1
324        """
325        self.scn_ad.droid.bleSetScanFilterServiceUuid(
326            "00000000-0000-1000-8000-00805f9b34fb")
327        self.adv_ad.droid.bleSetAdvertiseDataSetServiceUuids(
328            ["00000000-0000-1000-8000-00805f9b34fb"])
329        return self._verify_n_advertisements(self.max_advertisements)
330
331    @BluetoothBaseTest.bt_test_wrap
332    @test_tracker_info(uuid='756e026f-64d7-4a2f-935a-3790c0ac4503')
333    def test_max_advertisements_with_service_uuid_and_service_mask(self):
334        """Test max advertisements variant.
335
336        Test that a single device can have the max advertisements concurrently
337        advertising. Include the service mask as a part of the filter and
338        advertisement data.
339
340        Steps:
341        1. Setup the scanning android device.
342        2. Setup the advertiser android device.
343        3. Include service uuid in each advertisement.
344        4. Include service mask filter in the scanner.
345        5. Start scanning on the max_advertisements as defined in the script.
346        6. Verify that all advertisements are found.
347
348        Expected Result:
349        All advertisements should start without errors.
350
351        Returns:
352          Pass if True
353          Fail if False
354
355        TAGS: LE, Advertising, Concurrency
356        Priority: 2
357        """
358        self.scn_ad.droid.bleSetScanFilterServiceUuid(
359            "00000000-0000-1000-8000-00805f9b34fb",
360            "00000000-0000-1000-8000-00805f9b34fb")
361        self.adv_ad.droid.bleSetAdvertiseDataSetServiceUuids(
362            ["00000000-0000-1000-8000-00805f9b34fb"])
363        return self._verify_n_advertisements(self.max_advertisements)
364
365    @BluetoothBaseTest.bt_test_wrap
366    @test_tracker_info(uuid='79c4b6cd-9f07-49a9-829f-69b29ea8d322')
367    def test_max_advertisements_plus_one(self):
368        """Test max advertisements plus one.
369
370        Test that a single device can have the max advertisements concurrently
371        advertising but fail on starting the max advertisements plus one.
372        filter and advertisement data.
373
374        Steps:
375        1. Setup the scanning android device.
376        2. Setup the advertiser android device.
377        3. Start max_advertisements + 1.
378
379        Expected Result:
380        The last advertisement should fail.
381
382        Returns:
383          Pass if True
384          Fail if False
385
386        TAGS: LE, Advertising, Concurrency
387        Priority: 0
388        """
389        return not self._verify_n_advertisements(self.max_advertisements + 1)
390
391    @BluetoothBaseTest.bt_test_wrap
392    @test_tracker_info(uuid='0bd6e490-a501-4fe1-88e5-9b77970c0b95')
393    def test_start_two_advertisements_on_same_callback(self):
394        """Test invalid advertisement scenario.
395
396        Test that a single device cannot have two advertisements start on the
397        same callback.
398
399        Steps:
400        1. Setup the scanning android device.
401        2. Setup the advertiser android device.
402        3. Call start ble advertising on the same callback.
403
404        Expected Result:
405        The second call of start advertising on the same callback should fail.
406
407        Returns:
408          Pass if True
409          Fail if False
410
411        TAGS: LE, Advertising, Concurrency
412        Priority: 1
413        """
414        test_result = True
415        advertise_callback, advertise_data, advertise_settings = (
416            generate_ble_advertise_objects(self.adv_ad.droid))
417        self.adv_ad.droid.bleStartBleAdvertising(
418            advertise_callback, advertise_data, advertise_settings)
419        try:
420            self.adv_ad.ed.pop_event(
421                adv_succ.format(advertise_callback), self.default_timeout)
422        except Empty as error:
423            self.log.error("Test failed with Empty error: {}".format(error))
424            return False
425        except concurrent.futures._base.TimeoutError as error:
426            self.log.debug(
427                "Test failed, filtering callback onSuccess never occurred: {}"
428                .format(error))
429        try:
430            self.adv_ad.droid.bleStartBleAdvertising(
431                advertise_callback, advertise_data, advertise_settings)
432            self.adv_ad.ed.pop_event(
433                adv_succ.format(advertise_callback), self.default_timeout)
434            test_result = False
435        except Empty as error:
436            self.log.debug("Test passed with Empty error: {}".format(error))
437        except concurrent.futures._base.TimeoutError as error:
438            self.log.debug(
439                "Test passed, filtering callback onSuccess never occurred: {}"
440                .format(error))
441
442        return test_result
443
444    @BluetoothBaseTest.bt_test_wrap
445    @test_tracker_info(uuid='12632b31-22b9-4121-80b6-1263b9d90909')
446    def test_toggle_advertiser_bt_state(self):
447        """Test forcing stopping advertisements.
448
449        Test that a single device resets its callbacks when the bluetooth state is
450        reset. There should be no advertisements.
451
452        Steps:
453        1. Setup the scanning android device.
454        2. Setup the advertiser android device.
455        3. Call start ble advertising.
456        4. Toggle bluetooth on and off.
457        5. Scan for any advertisements.
458
459        Expected Result:
460        No advertisements should be found after toggling Bluetooth on the
461        advertising device.
462
463        Returns:
464          Pass if True
465          Fail if False
466
467        TAGS: LE, Advertising, Concurrency
468        Priority: 2
469        """
470        test_result = True
471        self.adv_ad.droid.bleSetAdvertiseDataIncludeDeviceName(True)
472        advertise_callback, advertise_data, advertise_settings = (
473            generate_ble_advertise_objects(self.adv_ad.droid))
474        self.adv_ad.droid.bleStartBleAdvertising(
475            advertise_callback, advertise_data, advertise_settings)
476        try:
477            self.adv_ad.ed.pop_event(
478                adv_succ.format(advertise_callback), self.default_timeout)
479        except Empty as error:
480            self.log.error("Test failed with Empty error: {}".format(error))
481            return False
482        except concurrent.futures._base.TimeoutError as error:
483            self.log.error(
484                "Test failed, filtering callback onSuccess never occurred: {}".
485                format(error))
486        self.scn_ad.droid.bleSetScanSettingsScanMode(
487            ble_scan_settings_modes['low_latency'])
488        self.scn_ad.droid.bleSetScanFilterDeviceName(
489            self.adv_ad.droid.bluetoothGetLocalName())
490        filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
491            self.scn_ad.droid)
492        self.scn_ad.droid.bleBuildScanFilter(filter_list)
493        self.scn_ad.droid.bleStartBleScan(filter_list, scan_settings,
494                                          scan_callback)
495        try:
496            self.scn_ad.ed.pop_event(
497                scan_result.format(scan_callback), self.default_timeout)
498        except Empty as error:
499            self.log.error("Test failed with: {}".format(error))
500            return False
501        self.scn_ad.droid.bleStopBleScan(scan_callback)
502        test_result = reset_bluetooth([self.android_devices[1]])
503        self.scn_ad.droid.bleStartBleScan(filter_list, scan_settings,
504                                          scan_callback)
505        if not test_result:
506            return False
507        try:
508            expected_event = scan_result.format(scan_callback)
509            event = self.scn_ad.ed.pop_event(expected_event,
510                                             self.default_timeout)
511            self.log.error("Event {} not expected. Found: {}".format(
512                expected_event, event))
513            return False
514        except Empty as error:
515            self.log.debug("Test passed with: {}".format(error))
516        self.scn_ad.droid.bleStopBleScan(scan_callback)
517        self.adv_ad.droid.bleStopBleAdvertising(advertise_callback)
518        return True
519
520    @BluetoothBaseTest.bt_test_wrap
521    @test_tracker_info(uuid='785c5c77-d5d4-4d0f-8b7b-3eb1f1646d2c')
522    def test_restart_advertise_callback_after_bt_toggle(self):
523        """Test starting an advertisement on a cleared out callback.
524
525        Test that a single device resets its callbacks when the bluetooth state
526        is reset.
527
528        Steps:
529        1. Setup the scanning android device.
530        2. Setup the advertiser android device.
531        3. Call start ble advertising.
532        4. Toggle bluetooth on and off.
533        5. Call start ble advertising on the same callback.
534
535        Expected Result:
536        Starting an advertisement on a callback id after toggling bluetooth
537        should fail.
538
539        Returns:
540          Pass if True
541          Fail if False
542
543        TAGS: LE, Advertising, Concurrency
544        Priority: 1
545        """
546        test_result = True
547        advertise_callback, advertise_data, advertise_settings = (
548            generate_ble_advertise_objects(self.adv_ad.droid))
549        self.adv_ad.droid.bleStartBleAdvertising(
550            advertise_callback, advertise_data, advertise_settings)
551        try:
552            self.adv_ad.ed.pop_event(
553                adv_succ.format(advertise_callback), self.default_timeout)
554        except Empty as error:
555            self.log.error("Test failed with Empty error: {}".format(error))
556            test_result = False
557        except concurrent.futures._base.TimeoutError as error:
558            self.log.debug(
559                "Test failed, filtering callback onSuccess never occurred: {}".
560                format(error))
561        test_result = reset_bluetooth([self.android_devices[1]])
562        if not test_result:
563            return test_result
564        self.adv_ad.droid.bleStartBleAdvertising(
565            advertise_callback, advertise_data, advertise_settings)
566        try:
567            self.adv_ad.ed.pop_event(
568                adv_succ.format(advertise_callback), self.default_timeout)
569        except Empty as error:
570            self.log.error("Test failed with Empty error: {}".format(error))
571            test_result = False
572        except concurrent.futures._base.TimeoutError as error:
573            self.log.debug(
574                "Test failed, filtering callback onSuccess never occurred: {}".
575                format(error))
576        return test_result
577
578    @BluetoothBaseTest.bt_test_wrap
579    @test_tracker_info(uuid='dd5529b7-6774-4580-8b29-d84568c15442')
580    def test_timeout(self):
581        """Test starting advertiser with timeout.
582
583        Test that when a timeout is used, the advertiser is cleaned properly,
584        and next one can be started.
585
586        Steps:
587        1. Setup the advertiser android device with 4 second timeout.
588        2. Call start ble advertising.
589        3. Wait 5 seconds, to make sure advertiser times out.
590        4. Repeat steps 1-4 four times.
591
592        Expected Result:
593        Starting the advertising should succeed each time.
594
595        Returns:
596          Pass if True
597          Fail if False
598
599        TAGS: LE, Advertising, Concurrency
600        Priority: 1
601        """
602        advertise_timeout_s = 4
603        num_iterations = 4
604        test_result = True
605
606        for i in range(0, num_iterations):
607            advertise_callback, advertise_data, advertise_settings = (
608                generate_ble_advertise_objects(self.adv_ad.droid))
609
610            self.adv_ad.droid.bleSetAdvertiseSettingsTimeout(
611                advertise_timeout_s * 1000)
612
613            self.adv_ad.droid.bleStartBleAdvertising(
614                advertise_callback, advertise_data, advertise_settings)
615            try:
616                self.adv_ad.ed.pop_event(
617                    adv_succ.format(advertise_callback), self.default_timeout)
618            except Empty as error:
619                self.log.error("Test failed with Empty error: {}".format(
620                    error))
621                test_result = False
622            except concurrent.futures._base.TimeoutError as error:
623                self.log.debug(
624                    "Test failed, filtering callback onSuccess never occurred: {}".
625                    format(error))
626
627            if not test_result:
628                return test_result
629
630            time.sleep(advertise_timeout_s + 1)
631
632        return test_result
633