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