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"""
17This test script exercises different Bluetooth 5 specific scan scenarios.
18It is expected that the second AndroidDevice is able to advertise.
19
20This test script was designed with this setup in mind:
21Shield box one: Android Device, Android Device
22"""
23
24from queue import Empty
25
26from acts import asserts
27from acts.test_decorators import test_tracker_info
28from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
29from acts.test_utils.bt.bt_constants import ble_scan_settings_phys
30from acts.test_utils.bt.bt_constants import ble_scan_settings_modes
31from acts.test_utils.bt.bt_constants import batch_scan_result
32from acts.test_utils.bt.bt_test_utils import cleanup_scanners_and_advertisers
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 reset_bluetooth
36from acts.test_utils.bt.bt_constants import scan_result
37from acts.test_utils.bt.bt_constants import advertising_set_on_own_address_read
38from acts.test_utils.bt.bt_constants import advertising_set_started
39from acts import signals
40
41
42class Bt5ScanTest(BluetoothBaseTest):
43    default_timeout = 10
44    report_delay = 2000
45    scan_callbacks = []
46    adv_callbacks = []
47    active_scan_callback_list = []
48    big_adv_data = {
49        "includeDeviceName": True,
50        "manufacturerData": [0x0123, "00112233445566778899AABBCCDDEE"],
51        "manufacturerData2":
52        [0x2540, [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0xFF]],
53        "serviceData": [
54            "b19d42dc-58ba-4b20-b6c1-6628e7d21de4",
55            "00112233445566778899AABBCCDDEE"
56        ],
57        "serviceData2": [
58            "000042dc-58ba-4b20-b6c1-6628e7d21de4",
59            [0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0xFF]
60        ]
61    }
62
63    def setup_class(self):
64        super(Bt5ScanTest, self).setup_class()
65        self.scn_ad = self.android_devices[0]
66        self.adv_ad = self.android_devices[1]
67
68        if not self.scn_ad.droid.bluetoothIsLeExtendedAdvertisingSupported():
69            raise signals.TestAbortClass(
70                "Scanner does not support LE Extended Advertising")
71
72        if not self.adv_ad.droid.bluetoothIsLeExtendedAdvertisingSupported():
73            raise signals.TestAbortClass(
74                "Advertiser does not support LE Extended Advertising")
75
76    def teardown_test(self):
77        cleanup_scanners_and_advertisers(
78            self.scn_ad, self.active_scan_callback_list, self.adv_ad, [])
79        self.active_scan_callback_list = []
80
81    def on_exception(self, test_name, begin_time):
82        reset_bluetooth(self.android_devices)
83
84    # This one does not relly test anything, but display very helpful
85    # information that might help with debugging.
86    @BluetoothBaseTest.bt_test_wrap
87    @test_tracker_info(uuid='787e0877-269f-4b9b-acb0-b98a8bb3770a')
88    def test_capabilities(self):
89        """Test capabilities
90
91        Test BT 5.0 scan scapabilities
92
93        Steps:
94        1. Test various vapabilities.
95
96        Expected Result:
97        Pass
98
99        Returns:
100          Pass if True
101          Fail if False
102
103        TAGS: BT5.0, Scanning
104        Priority: 2
105        """
106        d = self.scn_ad.droid
107        sup2M = d.bluetoothIsLe2MPhySupported()
108        supCoded = d.bluetoothIsLeCodedPhySupported()
109        supExt = d.bluetoothIsLeExtendedAdvertisingSupported()
110        supPeriodic = d.bluetoothIsLePeriodicAdvertisingSupported()
111        maxDataLen = d.bluetoothGetLeMaximumAdvertisingDataLength()
112        self.log.info("Scanner capabilities:")
113        self.log.info("LE 2M: " + str(sup2M) + ", LE Coded: " + str(
114            supCoded) + ", LE Extended Advertising: " + str(
115                supExt) + ", LE Periodic Advertising: " + str(supPeriodic) +
116                      ", maximum advertising data length: " + str(maxDataLen))
117        d = self.adv_ad.droid
118        sup2M = d.bluetoothIsLe2MPhySupported()
119        supCoded = d.bluetoothIsLeCodedPhySupported()
120        supExt = d.bluetoothIsLeExtendedAdvertisingSupported()
121        supPeriodic = d.bluetoothIsLePeriodicAdvertisingSupported()
122        maxDataLen = d.bluetoothGetLeMaximumAdvertisingDataLength()
123        self.log.info("Advertiser capabilities:")
124        self.log.info("LE 2M: " + str(sup2M) + ", LE Coded: " + str(
125            supCoded) + ", LE Extended Advertising: " + str(
126                supExt) + ", LE Periodic Advertising: " + str(supPeriodic) +
127                      ", maximum advertising data length: " + str(maxDataLen))
128        return True
129
130    @BluetoothBaseTest.bt_test_wrap
131    @test_tracker_info(uuid='62d36679-bb91-465e-897f-2635433aac2f')
132    def test_1m_1m_extended_scan(self):
133        """Test scan on LE 1M PHY using LE 1M PHY as secondary.
134
135        Tests test verify that device is able to receive extended advertising
136        on 1M PHY when secondary is 1M PHY.
137
138        Steps:
139        1. Start advertising set on dut1
140        2. Start scanning on dut0, scan filter set to advertiser's device name
141        3. Try to find an event, expect found
142        4. Stop advertising
143
144        Expected Result:
145        Scan finds a advertisement.
146
147        Returns:
148          Pass if True
149          Fail if False
150
151        TAGS: LE Advertising Extension, BT5, LE, Advertising, Scanning
152        Priority: 1
153        """
154        adv_callback = self.adv_ad.droid.bleAdvSetGenCallback()
155        self.adv_ad.droid.bleAdvSetStartAdvertisingSet({
156            "connectable": True,
157            "legacyMode": False,
158            "primaryPhy": "PHY_LE_1M",
159            "secondaryPhy": "PHY_LE_1M",
160            "interval": 320
161        }, self.big_adv_data, None, None, None, 0, 0, adv_callback)
162
163        self.scn_ad.droid.bleSetScanSettingsLegacy(False)
164        self.scn_ad.droid.bleSetScanSettingsPhy(ble_scan_settings_phys['1m'])
165
166        filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
167            self.scn_ad.droid)
168
169        adv_device_name = self.adv_ad.droid.bluetoothGetLocalName()
170        self.scn_ad.droid.bleSetScanFilterDeviceName(adv_device_name)
171        self.scn_ad.droid.bleBuildScanFilter(filter_list)
172        self.scn_ad.droid.bleStartBleScan(filter_list, scan_settings,
173                                          scan_callback)
174        self.active_scan_callback_list.append(scan_callback)
175
176        try:
177            self.scn_ad.ed.pop_event(
178                scan_result.format(scan_callback), self.default_timeout)
179        except Empty:
180            self.log.error("Scan result not found")
181            self.adv_ad.droid.bleAdvSetStopAdvertisingSet(adv_callback)
182            return False
183
184        self.adv_ad.droid.bleAdvSetStopAdvertisingSet(adv_callback)
185        return True
186
187    @BluetoothBaseTest.bt_test_wrap
188    @test_tracker_info(uuid='3e3c9757-f7b6-4d1d-a2d6-8e2330d1a18e')
189    def test_1m_2m_extended_scan(self):
190        """Test scan on LE 1M PHY using LE 2M PHY as secondary.
191
192        Tests test verify that device is able to receive extended advertising
193        on 1M PHY when secondary is 2M PHY.
194
195        Steps:
196        1. Start advertising set on dut1
197        2. Start scanning on dut0, scan filter set to advertiser's device name
198        3. Try to find an event, expect found
199        4. Stop advertising
200
201        Expected Result:
202        Scan finds a advertisement.
203
204        Returns:
205          Pass if True
206          Fail if False
207
208        TAGS: LE Advertising Extension, BT5, LE, Advertising, Scanning
209        Priority: 1
210        """
211        adv_callback = self.adv_ad.droid.bleAdvSetGenCallback()
212        self.adv_ad.droid.bleAdvSetStartAdvertisingSet({
213            "connectable": True,
214            "legacyMode": False,
215            "primaryPhy": "PHY_LE_1M",
216            "secondaryPhy": "PHY_LE_2M",
217            "interval": 320
218        }, self.big_adv_data, None, None, None, 0, 0, adv_callback)
219
220        self.scn_ad.droid.bleSetScanSettingsLegacy(False)
221        self.scn_ad.droid.bleSetScanSettingsPhy(ble_scan_settings_phys['1m'])
222
223        filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
224            self.scn_ad.droid)
225
226        adv_device_name = self.adv_ad.droid.bluetoothGetLocalName()
227        self.scn_ad.droid.bleSetScanFilterDeviceName(adv_device_name)
228        self.scn_ad.droid.bleBuildScanFilter(filter_list)
229        self.scn_ad.droid.bleStartBleScan(filter_list, scan_settings,
230                                          scan_callback)
231        self.active_scan_callback_list.append(scan_callback)
232
233        try:
234            self.scn_ad.ed.pop_event(
235                scan_result.format(scan_callback), self.default_timeout)
236        except Empty:
237            self.log.error("Scan result not found")
238            self.adv_ad.droid.bleAdvSetStopAdvertisingSet(adv_callback)
239            return False
240
241        self.adv_ad.droid.bleAdvSetStopAdvertisingSet(adv_callback)
242        return True
243
244    @BluetoothBaseTest.bt_test_wrap
245    @test_tracker_info(uuid='236e9e5b-3853-4762-81ae-e88db03d74f3')
246    def test_legacy_scan_result_raw_length(self):
247        """Test that raw scan record data in legacy scan is 62 bytes long.
248
249        This is required for compability with older apps that make this
250        assumption.
251
252        Steps:
253        1. Start legacy advertising set on dut1
254        2. Start scanning on dut0, scan filter set to advertiser's device name
255        3. Try to find an event, expect found, verify scan recurd data length
256        4. Stop advertising
257
258        Expected Result:
259        Scan finds a legacy advertisement of proper size
260
261        Returns:
262          Pass if True
263          Fail if False
264
265        TAGS: LE Advertising Extension, BT5, LE, Advertising, Scanning
266        Priority: 1
267        """
268        adv_callback = self.adv_ad.droid.bleAdvSetGenCallback()
269        self.adv_ad.droid.bleAdvSetStartAdvertisingSet({
270            "connectable": True,
271            "scannable": True,
272            "legacyMode": True,
273            "interval": 320
274        }, {"includeDeviceName": True}, None, None, None, 0, 0, adv_callback)
275
276        self.scn_ad.droid.bleSetScanSettingsLegacy(True)
277        self.scn_ad.droid.bleSetScanSettingsPhy(ble_scan_settings_phys['1m'])
278
279        filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
280            self.scn_ad.droid)
281
282        adv_device_name = self.adv_ad.droid.bluetoothGetLocalName()
283        self.scn_ad.droid.bleSetScanFilterDeviceName(adv_device_name)
284        self.scn_ad.droid.bleBuildScanFilter(filter_list)
285        self.scn_ad.droid.bleStartBleScan(filter_list, scan_settings,
286                                          scan_callback)
287        self.active_scan_callback_list.append(scan_callback)
288
289        try:
290            evt = self.scn_ad.ed.pop_event(
291                scan_result.format(scan_callback), self.default_timeout)
292            rawData = evt['data']['Result']['scanRecord']
293            asserts.assert_true(62 == len(rawData.split(",")),
294                                "Raw data should be 62 bytes long.")
295        except Empty:
296            self.log.error("Scan result not found")
297            self.adv_ad.droid.bleAdvSetStopAdvertisingSet(adv_callback)
298            return False
299
300        self.adv_ad.droid.bleAdvSetStopAdvertisingSet(adv_callback)
301        return True
302
303    @BluetoothBaseTest.bt_test_wrap
304    @test_tracker_info(uuid='04632d8d-4303-476f-8f83-52c16be3713a')
305    def test_duration(self):
306        """Test scanning duration
307
308        Tests BT5.0 scanning duration
309
310        Steps:
311        1. Start advertising set
312        2. Start 5.0 scan
313        3. Scan for advertisement event
314
315        Expected Result:
316        Scan finds a legacy advertisement of proper size
317
318        Returns:
319          Pass if True
320          Fail if False
321
322        TAGS: BT5.0, LE, Advertising, Scanning
323        Priority: 1
324        """
325        adv_callback = self.adv_ad.droid.bleAdvSetGenCallback()
326        self.adv_ad.droid.bleAdvSetStartAdvertisingSet({
327            "connectable": True,
328            "legacyMode": False,
329            "primaryPhy": "PHY_LE_1M",
330            "secondaryPhy": "PHY_LE_2M",
331            "interval": 320
332        }, self.big_adv_data, None, None, None, 0, 0, adv_callback)
333
334        self.scn_ad.droid.bleSetScanSettingsLegacy(False)
335        self.scn_ad.droid.bleSetScanSettingsPhy(ble_scan_settings_phys['1m'])
336
337        filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
338            self.scn_ad.droid)
339
340        adv_device_name = self.adv_ad.droid.bluetoothGetLocalName()
341        self.scn_ad.droid.bleSetScanFilterDeviceName(adv_device_name)
342        self.scn_ad.droid.bleBuildScanFilter(filter_list)
343        self.scn_ad.droid.bleStartBleScan(filter_list, scan_settings,
344                                          scan_callback)
345        self.active_scan_callback_list.append(scan_callback)
346
347        try:
348            self.scn_ad.ed.pop_event(
349                scan_result.format(scan_callback), self.default_timeout)
350        except Empty:
351            self.log.error("Scan result not found")
352            self.adv_ad.droid.bleAdvSetStopAdvertisingSet(adv_callback)
353            return False
354
355        self.adv_ad.droid.bleAdvSetStopAdvertisingSet(adv_callback)
356        return True
357
358    @BluetoothBaseTest.bt_test_wrap
359    @test_tracker_info(uuid='a3704083-0f5c-4a46-b979-32ebc594d6ee')
360    def test_anonymous_advertising(self):
361        """Test anonymous advertising.
362
363        Tests test verify that device is able to receive anonymous advertising
364        on 1M PHY when secondary is 2M PHY.
365
366        Steps:
367        1. Start anonymous advertising set on dut1
368        2. Start scanning on dut0, scan filter set to advertiser's device name
369        3. Try to find an event, expect found
370        4. Stop advertising
371
372        Expected Result:
373        Scan finds a advertisement.
374
375        Returns:
376          Pass if True
377          Fail if False
378
379        TAGS: LE Advertising Extension, BT5, LE, Advertising, Scanning
380        Priority: 1
381        """
382        adv_callback = self.adv_ad.droid.bleAdvSetGenCallback()
383        self.adv_ad.droid.bleAdvSetStartAdvertisingSet({
384            "connectable": False,
385            "anonymous": True,
386            "legacyMode": False,
387            "primaryPhy": "PHY_LE_1M",
388            "secondaryPhy": "PHY_LE_2M",
389            "interval": 320
390        }, self.big_adv_data, None, None, None, 0, 0, adv_callback)
391
392        self.scn_ad.droid.bleSetScanSettingsLegacy(False)
393        self.scn_ad.droid.bleSetScanSettingsPhy(ble_scan_settings_phys['1m'])
394
395        filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
396            self.scn_ad.droid)
397
398        adv_device_name = self.adv_ad.droid.bluetoothGetLocalName()
399        self.scn_ad.droid.bleSetScanFilterDeviceName(adv_device_name)
400        self.scn_ad.droid.bleBuildScanFilter(filter_list)
401        self.scn_ad.droid.bleStartBleScan(filter_list, scan_settings,
402                                          scan_callback)
403        self.active_scan_callback_list.append(scan_callback)
404
405        try:
406            evt = self.scn_ad.ed.pop_event(
407                scan_result.format(scan_callback), self.default_timeout)
408            address = evt['data']['Result']['deviceInfo']['address']
409            asserts.assert_true(
410                '00:00:00:00:00:00' == address,
411                "Anonymous address should be 00:00:00:00:00:00, but was " +
412                str(address))
413        except Empty:
414            self.log.error("Scan result not found")
415            self.adv_ad.droid.bleAdvSetStopAdvertisingSet(adv_callback)
416            return False
417
418        self.adv_ad.droid.bleAdvSetStopAdvertisingSet(adv_callback)
419        return True
420
421    @BluetoothBaseTest.bt_test_wrap
422    @test_tracker_info(uuid='e3277355-eebf-4760-9502-e49a9289f6ab')
423    def test_get_own_address(self):
424        """Test obtaining own address for PTS.
425
426        Test obtaining own address.
427
428        Steps:
429        1. Start advertising set dut1
430        2. Grab address
431        3. Stop advertising
432
433        Expected Result:
434        Callback with address is received.
435
436        Returns:
437          Pass if True
438          Fail if False
439
440        TAGS: LE Advertising Extension, BT5, LE, Advertising
441        Priority: 1
442        """
443        adv_callback = self.adv_ad.droid.bleAdvSetGenCallback()
444        self.adv_ad.droid.bleAdvSetStartAdvertisingSet({
445            "connectable": False,
446            "anonymous": True,
447            "legacyMode": False,
448            "primaryPhy": "PHY_LE_1M",
449            "secondaryPhy": "PHY_LE_2M",
450            "interval": 320
451        }, self.big_adv_data, None, None, None, 0, 0, adv_callback)
452
453        set_id = -1
454
455        try:
456            evt = self.adv_ad.ed.pop_event(
457                advertising_set_started.format(adv_callback),
458                self.default_timeout)
459            self.log.info("data: " + str(evt['data']))
460            set_id = evt['data']['setId']
461        except Empty:
462            self.log.error("did not receive the set started event!")
463            self.adv_ad.droid.bleAdvSetStopAdvertisingSet(adv_callback)
464            return False
465
466        self.adv_ad.droid.bleAdvSetGetOwnAddress(set_id)
467
468        try:
469            evt = self.adv_ad.ed.pop_event(
470                advertising_set_on_own_address_read.format(set_id),
471                self.default_timeout)
472            address = evt['data']['address']
473            self.log.info("Advertiser address is: " + str(address))
474        except Empty:
475            self.log.error("onOwnAddressRead not received.")
476            self.adv_ad.droid.bleAdvSetStopAdvertisingSet(adv_callback)
477            return False
478
479        self.adv_ad.droid.bleAdvSetStopAdvertisingSet(adv_callback)
480        return True
481