1#/usr/bin/env python3.4
2#
3# Copyright (C) 2016 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License"); you may not
6# use this file except in compliance with the License. You may obtain a copy of
7# the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14# License for the specific language governing permissions and limitations under
15# the License.
16"""
17Test script to exercises Ble Scans can run in concurrency.
18This test was designed to be run in a shield box.
19"""
20
21import concurrent
22import time
23
24from queue import Empty
25from acts.test_decorators import test_tracker_info
26from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
27from acts.test_utils.bt.bt_constants import ble_advertise_settings_modes
28from acts.test_utils.bt.bt_constants import ble_scan_settings_callback_types
29from acts.test_utils.bt.bt_constants import ble_scan_settings_modes
30from acts.test_utils.bt.bt_constants import adv_succ
31from acts.test_utils.bt.bt_test_utils import generate_ble_advertise_objects
32from acts.test_utils.bt.bt_test_utils import reset_bluetooth
33from acts.test_utils.bt.bt_test_utils import scan_failed
34from acts.test_utils.bt.bt_constants import scan_result
35from acts.test_utils.bt.bt_test_utils import take_btsnoop_logs
36
37
38class ConcurrentBleScanningTest(BluetoothBaseTest):
39    default_timeout = 20
40    max_concurrent_scans = 27
41
42    def __init__(self, controllers):
43        BluetoothBaseTest.__init__(self, controllers)
44        self.scn_ad = self.android_devices[0]
45        self.adv_ad = self.android_devices[1]
46
47    def on_fail(self, test_name, begin_time):
48        self.log.debug("Test {} failed. Gathering bugreport and btsnoop logs."
49                       .format(test_name))
50        take_btsnoop_logs(self.android_devices, self, test_name)
51        reset_bluetooth(self.android_devices)
52
53    def setup_test(self):
54        return reset_bluetooth(self.android_devices)
55
56    @BluetoothBaseTest.bt_test_wrap
57    @test_tracker_info(uuid='e7f68b9b-fb3f-48e9-a272-e41c2a32b4bd')
58    def test_max_concurrent_ble_scans(self):
59        """Test max LE scans.
60
61        Test that a single device can have max scans concurrently scanning.
62
63        Steps:
64        1. Initialize scanner
65        2. Initialize advertiser
66        3. Start advertising on the device from step 2
67        4. Create max ble scan callbacks
68        5. Start ble scan on each callback
69        6. Verify that each callback triggers
70        7. Stop all scans and advertisements
71
72        Expected Result:
73        All scanning instances should start without errors and the advertisement
74        should be found on each scan instance.
75
76        Returns:
77          Pass if True
78          Fail if False
79
80        TAGS: LE, Scanning, Concurrency
81        Priority: 0
82        """
83        test_result = True
84        self.adv_ad.droid.bleSetAdvertiseDataIncludeDeviceName(True)
85        self.scn_ad.droid.bleSetScanSettingsCallbackType(
86            ble_scan_settings_callback_types['all_matches'])
87        self.scn_ad.droid.bleSetScanSettingsScanMode(ble_scan_settings_modes[
88            'low_latency'])
89        self.adv_ad.droid.bleSetAdvertiseSettingsAdvertiseMode(
90            ble_advertise_settings_modes['low_latency'])
91        advertise_callback, advertise_data, advertise_settings = (
92            generate_ble_advertise_objects(self.adv_ad.droid))
93        self.adv_ad.droid.bleSetAdvertiseSettingsIsConnectable(False)
94        self.adv_ad.droid.bleStartBleAdvertising(
95            advertise_callback, advertise_data, advertise_settings)
96        try:
97            self.adv_ad.ed.pop_event(
98                adv_succ.format(advertise_callback), self.default_timeout)
99        except Empty as error:
100            self.log.exception("Test failed with Empty error: {}".format(
101                error))
102            test_result = False
103        except concurrent.futures._base.TimeoutError as error:
104            self.log.exception(
105                "Test failed callback onSuccess never occurred: "
106                "{}".format(error))
107            test_result = False
108        if not test_result:
109            return test_result
110        filter_list = self.scn_ad.droid.bleGenFilterList()
111        self.scn_ad.droid.bleSetScanFilterDeviceName(
112            self.adv_ad.droid.bluetoothGetLocalName())
113        self.scn_ad.droid.bleBuildScanFilter(filter_list)
114        scan_settings = self.scn_ad.droid.bleBuildScanSetting()
115        scan_callback_list = []
116        for i in range(self.max_concurrent_scans):
117            self.log.debug("Concurrent Ble Scan iteration {}".format(i + 1))
118            scan_callback = self.scn_ad.droid.bleGenScanCallback()
119            scan_callback_list.append(scan_callback)
120            self.scn_ad.droid.bleStartBleScan(filter_list, scan_settings,
121                                              scan_callback)
122            try:
123                self.scn_ad.ed.pop_event(
124                    scan_result.format(scan_callback), self.default_timeout)
125                self.log.info("Found scan event successfully. Iteration {} "
126                              "successful.".format(i))
127            except Exception:
128                self.log.info("Failed to find a scan result for callback {}"
129                              .format(scan_callback))
130                test_result = False
131                break
132        for callback in scan_callback_list:
133            self.scn_ad.droid.bleStopBleScan(callback)
134        self.adv_ad.droid.bleStopBleAdvertising(advertise_callback)
135        if not test_result:
136            return test_result
137        self.log.info("Waiting for scan callbacks to stop completely.")
138        # Wait for all scan callbacks to stop. There is no confirmation
139        # otherwise.
140        time.sleep(10)
141        return test_result
142
143    @BluetoothBaseTest.bt_test_wrap
144    @test_tracker_info(uuid='58b0c45e-1cbc-420a-9e89-901518ffe3d1')
145    def test_max_concurrent_ble_scans_then_discover_advertisement(self):
146        """Test max LE scans variant.
147
148        Test that a single device can have max scans concurrently scanning.
149
150        Steps:
151        1. Initialize scanner
152        2. Initialize advertiser
153        3. Create max ble scan callbacks
154        4. Start ble scan on each callback
155        5. Start advertising on the device from step 2
156        6. Verify that each callback triggers
157        7. Stop all scans and advertisements
158
159        Expected Result:
160        All scanning instances should start without errors and the advertisement
161        should be found on each scan instance.
162
163        Returns:
164          Pass if True
165          Fail if False
166
167        TAGS: LE, Scanning, Concurrency
168        Priority: 1
169        """
170        self.adv_ad.droid.bleSetAdvertiseDataIncludeDeviceName(True)
171        self.scn_ad.droid.bleSetScanSettingsCallbackType(
172            ble_scan_settings_callback_types['all_matches'])
173        self.scn_ad.droid.bleSetScanSettingsScanMode(ble_scan_settings_modes[
174            'low_latency'])
175        self.adv_ad.droid.bleSetAdvertiseSettingsAdvertiseMode(
176            ble_advertise_settings_modes['low_latency'])
177        advertise_callback, advertise_data, advertise_settings = (
178            generate_ble_advertise_objects(self.adv_ad.droid))
179        filter_list = self.scn_ad.droid.bleGenFilterList()
180        self.scn_ad.droid.bleSetScanFilterDeviceName(
181            self.adv_ad.droid.bluetoothGetLocalName())
182        self.scn_ad.droid.bleBuildScanFilter(filter_list)
183        scan_settings = self.scn_ad.droid.bleBuildScanSetting()
184        scan_callback_list = []
185        for i in range(self.max_concurrent_scans):
186            self.log.debug("Concurrent Ble Scan iteration {}".format(i + 1))
187            scan_callback = self.scn_ad.droid.bleGenScanCallback()
188            scan_callback_list.append(scan_callback)
189            self.scn_ad.droid.bleStartBleScan(filter_list, scan_settings,
190                                              scan_callback)
191        self.adv_ad.droid.bleStartBleAdvertising(
192            advertise_callback, advertise_data, advertise_settings)
193        try:
194            self.adv_ad.ed.pop_event(
195                adv_succ.format(advertise_callback), self.default_timeout)
196        except Empty as error:
197            self.log.exception("Test failed with Empty error: {}".format(
198                error))
199            return False
200        except concurrent.futures._base.TimeoutError as error:
201            self.log.exception("Test failed, filtering callback onSuccess "
202                               "never occurred: {}".format(error))
203            return False
204        i = 0
205        for callback in scan_callback_list:
206            try:
207                self.scn_ad.ed.pop_event(
208                    scan_result.format(scan_callback), self.default_timeout)
209                self.log.info(
210                    "Found scan event successfully. Iteration {} successful."
211                    .format(i))
212            except Exception:
213                self.log.info("Failed to find a scan result for callback {}"
214                              .format(scan_callback))
215                return False
216            i += 1
217        for callback in scan_callback_list:
218            self.scn_ad.droid.bleStopBleScan(callback)
219        self.adv_ad.droid.bleStopBleAdvertising(advertise_callback)
220        return True
221
222    @BluetoothBaseTest.bt_test_wrap
223    @test_tracker_info(uuid='7a45e45c-faf3-4e89-abb7-a52f63e53208')
224    def test_max_concurrent_ble_scans_plus_one(self):
225        """Test mac LE scans variant.
226
227        Test that a single device can have max scans concurrently scanning.
228
229        Steps:
230        1. Initialize scanner
231        3. Create max ble scan callbacks plus one
232        5. Start ble scan on each callback
233        6. Verify that the n+1th scan fails.
234        7. Stop all scans
235
236        Expected Result:
237        The n+1th scan should fail to start.
238
239        Returns:
240          Pass if True
241          Fail if False
242
243        TAGS: LE, Scanning, Concurrency
244        Priority: 1
245        """
246        test_result = True
247        self.scn_ad.droid.bleSetScanSettingsCallbackType(
248            ble_scan_settings_callback_types['all_matches'])
249        self.scn_ad.droid.bleSetScanSettingsScanMode(ble_scan_settings_modes[
250            'low_latency'])
251        filter_list = self.scn_ad.droid.bleGenFilterList()
252        self.scn_ad.droid.bleBuildScanFilter(filter_list)
253        scan_settings = self.scn_ad.droid.bleBuildScanSetting()
254        scan_callback_list = []
255        for i in range(self.max_concurrent_scans):
256            self.log.debug("Concurrent Ble Scan iteration {}".format(i + 1))
257            scan_callback = self.scn_ad.droid.bleGenScanCallback()
258            self.scn_ad.droid.bleStartBleScan(filter_list, scan_settings,
259                                              scan_callback)
260            scan_callback_list.append(scan_callback)
261        scan_callback = self.scn_ad.droid.bleGenScanCallback()
262        self.scn_ad.droid.bleStartBleScan(filter_list, scan_settings,
263                                          scan_callback)
264        try:
265            self.scn_ad.ed.pop_event(
266                scan_failed.format(scan_callback), self.default_timeout)
267            self.log.info(
268                "Found scan event successfully. Iteration {} successful."
269                .format(i))
270        except Exception:
271            self.log.info("Failed to find a onScanFailed event for callback {}"
272                          .format(scan_callback))
273            test_result = False
274        for callback in scan_callback_list:
275            self.scn_ad.droid.bleStopBleScan(callback)
276        return test_result
277
278    @BluetoothBaseTest.bt_test_wrap
279    @test_tracker_info(uuid='5a91f612-69e5-490f-b9d0-50d58a3db736')
280    def test_max_concurrent_ble_scans_verify_scans_stop_independently(self):
281        """Test max LE scans variant.
282
283        Test that a single device can have max scans concurrently scanning.
284
285        Steps:
286        1. Initialize scanner
287        2. Initialize advertiser
288        3. Create max ble scan callbacks
289        4. Start ble scan on each callback
290        5. Start advertising on the device from step 2
291        6. Verify that the first callback triggers
292        7. Stop the scan and repeat steps 6 and 7 until all scans stopped
293
294        Expected Result:
295        All scanning instances should start without errors and the advertisement
296        should be found on each scan instance. All scanning instances should
297        stop successfully.
298
299        Returns:
300          Pass if True
301          Fail if False
302
303        TAGS: LE, Scanning, Concurrency
304        Priority: 1
305        """
306        self.adv_ad.droid.bleSetAdvertiseDataIncludeDeviceName(True)
307        self.scn_ad.droid.bleSetScanSettingsCallbackType(
308            ble_scan_settings_callback_types['all_matches'])
309        self.scn_ad.droid.bleSetScanSettingsScanMode(ble_scan_settings_modes[
310            'low_latency'])
311        self.adv_ad.droid.bleSetAdvertiseSettingsAdvertiseMode(
312            ble_advertise_settings_modes['low_latency'])
313        advertise_callback, advertise_data, advertise_settings = (
314            generate_ble_advertise_objects(self.adv_ad.droid))
315        filter_list = self.scn_ad.droid.bleGenFilterList()
316        self.scn_ad.droid.bleSetScanFilterDeviceName(
317            self.adv_ad.droid.bluetoothGetLocalName())
318        self.scn_ad.droid.bleBuildScanFilter(filter_list)
319        scan_settings = self.scn_ad.droid.bleBuildScanSetting()
320        scan_callback_list = []
321        for i in range(self.max_concurrent_scans):
322            self.log.debug("Concurrent Ble Scan iteration {}".format(i + 1))
323            scan_callback = self.scn_ad.droid.bleGenScanCallback()
324            scan_callback_list.append(scan_callback)
325            self.scn_ad.droid.bleStartBleScan(filter_list, scan_settings,
326                                              scan_callback)
327        self.adv_ad.droid.bleStartBleAdvertising(
328            advertise_callback, advertise_data, advertise_settings)
329        try:
330            self.adv_ad.ed.pop_event(
331                adv_succ.format(advertise_callback), self.default_timeout)
332        except Empty as error:
333            self.log.exception("Test failed with Empty error: {}".format(
334                error))
335            return False
336        except concurrent.futures._base.TimeoutError as error:
337            self.log.exception(
338                "Test failed, filtering callback onSuccess never"
339                " occurred: {}".format(error))
340            return False
341        i = 0
342        for callback in scan_callback_list:
343            expected_scan_event_name = scan_result.format(scan_callback)
344            try:
345                self.scn_ad.ed.pop_event(expected_scan_event_name,
346                                         self.default_timeout)
347                self.log.info(
348                    "Found scan event successfully. Iteration {} successful.".
349                    format(i))
350                i += 1
351            except Exception:
352                self.log.info("Failed to find a scan result for callback {}".
353                              format(scan_callback))
354                return False
355            self.scn_ad.droid.bleStopBleScan(callback)
356        self.adv_ad.droid.bleStopBleAdvertising(advertise_callback)
357        return True
358