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 background scan test scenarios.
18"""
19
20from queue import Empty
21
22from acts import utils
23from acts.test_decorators import test_tracker_info
24from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
25from acts.test_utils.bt.bt_test_utils import bluetooth_off
26from acts.test_utils.bt.bt_test_utils import bluetooth_on
27from acts.test_utils.bt.bt_test_utils import cleanup_scanners_and_advertisers
28from acts.test_utils.bt.bt_test_utils import enable_bluetooth
29from acts.test_utils.bt.bt_test_utils import generate_ble_advertise_objects
30from acts.test_utils.bt.bt_test_utils import generate_ble_scan_objects
31from acts.test_utils.bt.bt_constants import bluetooth_le_off
32from acts.test_utils.bt.bt_constants import bluetooth_le_on
33from acts.test_utils.bt.bt_constants import bt_adapter_states
34from acts.test_utils.bt.bt_constants import ble_scan_settings_modes
35from acts.test_utils.bt.bt_constants import scan_result
36
37import time
38
39
40class BleBackgroundScanTest(BluetoothBaseTest):
41    default_timeout = 10
42    report_delay = 2000
43    scan_callbacks = []
44    adv_callbacks = []
45    active_scan_callback_list = []
46    active_adv_callback_list = []
47
48    def __init__(self, controllers):
49        BluetoothBaseTest.__init__(self, controllers)
50        self.scn_ad = self.android_devices[0]
51        self.adv_ad = self.android_devices[1]
52
53    def setup_class(self):
54        super(BluetoothBaseTest, self).setup_class()
55        utils.set_location_service(self.scn_ad, True)
56        utils.set_location_service(self.adv_ad, True)
57        return True
58
59    def setup_test(self):
60        # Always start tests with Bluetooth enabled and BLE disabled.
61        enable_bluetooth(self.scn_ad.droid, self.scn_ad.ed)
62        self.scn_ad.droid.bluetoothDisableBLE()
63        for a in self.android_devices:
64            a.ed.clear_all_events()
65        return True
66
67    def teardown_test(self):
68        cleanup_scanners_and_advertisers(
69            self.scn_ad, self.active_adv_callback_list, self.adv_ad,
70            self.active_adv_callback_list)
71        self.active_adv_callback_list = []
72        self.active_scan_callback_list = []
73
74    def _setup_generic_advertisement(self):
75        self.adv_ad.droid.bleSetAdvertiseDataIncludeDeviceName(True)
76        adv_callback, adv_data, adv_settings = generate_ble_advertise_objects(
77            self.adv_ad.droid)
78        self.adv_ad.droid.bleStartBleAdvertising(adv_callback, adv_data,
79                                                 adv_settings)
80        self.active_adv_callback_list.append(adv_callback)
81
82    @BluetoothBaseTest.bt_test_wrap
83    @test_tracker_info(uuid='4d13c3a8-1805-44ef-a92a-e385540767f1')
84    def test_background_scan(self):
85        """Test generic background scan.
86
87        Tests LE background scan. The goal is to find scan results even though
88        Bluetooth is turned off.
89
90        Steps:
91        1. Setup an advertisement on dut1
92        2. Enable LE on the Bluetooth Adapter on dut0
93        3. Toggle BT off on dut1
94        4. Start a LE scan on dut0
95        5. Find the advertisement from dut1
96
97        Expected Result:
98        Find a advertisement from the scan instance.
99
100        Returns:
101          Pass if True
102          Fail if False
103
104        TAGS: LE, Advertising, Scanning, Background Scanning
105        Priority: 0
106        """
107        self.scn_ad.droid.bluetoothEnableBLE()
108        self._setup_generic_advertisement()
109        self.scn_ad.droid.bleSetScanSettingsScanMode(
110            ble_scan_settings_modes['low_latency'])
111        filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
112            self.scn_ad.droid)
113        self.scn_ad.droid.bleSetScanFilterDeviceName(
114            self.adv_ad.droid.bluetoothGetLocalName())
115        self.scn_ad.droid.bleBuildScanFilter(filter_list)
116        self.scn_ad.droid.bluetoothToggleState(False)
117        try:
118            self.scn_ad.ed.pop_event(bluetooth_off, self.default_timeout)
119        except Empty:
120            self.log.error("Bluetooth Off event not found. Expected {}".format(
121                bluetooth_off))
122            return False
123        self.scn_ad.droid.bleStartBleScan(filter_list, scan_settings,
124                                          scan_callback)
125        expected_event = scan_result.format(scan_callback)
126        try:
127            self.scn_ad.ed.pop_event(expected_event, self.default_timeout)
128        except Empty:
129            self.log.error("Scan Result event not found. Expected {}".format(
130                expected_event))
131            return False
132        return True
133
134    @BluetoothBaseTest.bt_test_wrap
135    @test_tracker_info(uuid='9c4577f8-5e06-4034-b977-285956734974')
136    def test_background_scan_ble_disabled(self):
137        """Test background LE scanning with LE disabled.
138
139        Tests LE background scan. The goal is to find scan results even though
140        Bluetooth is turned off.
141
142        Steps:
143        1. Setup an advertisement on dut1
144        2. Enable LE on the Bluetooth Adapter on dut0
145        3. Toggle BT off on dut1
146        4. Start a LE scan on dut0
147        5. Find the advertisement from dut1
148
149        Expected Result:
150        Find a advertisement from the scan instance.
151
152        Returns:
153          Pass if True
154          Fail if False
155
156        TAGS: LE, Advertising, Scanning, Background Scanning
157        Priority: 0
158        """
159        self._setup_generic_advertisement()
160        self.scn_ad.droid.bluetoothEnableBLE()
161        self.scn_ad.droid.bleSetScanSettingsScanMode(
162            ble_scan_settings_modes['low_latency'])
163        filter_list, scan_settings, scan_callback = generate_ble_scan_objects(
164            self.scn_ad.droid)
165        self.scn_ad.droid.bleSetScanFilterDeviceName(
166            self.adv_ad.droid.bluetoothGetLocalName())
167        self.scn_ad.droid.bleBuildScanFilter(filter_list)
168        self.scn_ad.droid.bluetoothToggleState(False)
169        try:
170            self.scn_ad.ed.pop_event(bluetooth_off, self.default_timeout)
171        except Empty:
172            self.log.info(self.scn_ad.droid.bluetoothCheckState())
173            self.log.error("Bluetooth Off event not found. Expected {}".format(
174                bluetooth_off))
175            return False
176        try:
177            self.scn_ad.droid.bleStartBleScan(filter_list, scan_settings,
178                                              scan_callback)
179            expected_event = scan_result.format(scan_callback)
180            try:
181                self.scn_ad.ed.pop_event(expected_event, self.default_timeout)
182            except Empty:
183                self.log.error(
184                    "Scan Result event not found. Expected {}".format(
185                        expected_event))
186                return False
187        except Exception:
188            self.log.info(
189                "Was not able to start a background scan as expected.")
190        return True
191
192    @BluetoothBaseTest.bt_test_wrap
193    @test_tracker_info(uuid='0bdd1764-3dc6-4a82-b041-76e48ed0f424')
194    def test_airplane_mode_disables_ble(self):
195        """Try to start LE mode in Airplane Mode.
196
197        This test will enable airplane mode, then attempt to start LE scanning
198        mode.  This should result in bluetooth still being turned off, LE
199        not enabled.
200
201        Steps:
202        1. Start LE only mode.
203        2. Bluetooth should be in LE ONLY mode
204        2. Turn on airplane mode.
205        3. Bluetooth should be OFF
206        4. Try to start LE only mode.
207        5. Bluetooth should stay in OFF mode (LE only start should fail)
208        6. Turn off airplane mode.
209        7. Bluetooth should be OFF.
210
211        Expected Result:
212        No unexpected bluetooth state changes.
213
214        Returns:
215          Pass if True
216          Fail if False
217
218        TAGS: LE, Airplane
219        Priority: 1
220        """
221        ble_state_error_msg = "Bluetooth LE State not OK {}. Expected {} got {}"
222        # Enable BLE always available (effectively enabling BT in location)
223        self.scn_ad.shell.enable_ble_scanning()
224        self.scn_ad.droid.bluetoothEnableBLE()
225        self.scn_ad.droid.bluetoothToggleState(False)
226        try:
227            self.scn_ad.ed.pop_event(bluetooth_off, self.default_timeout)
228        except Empty:
229            self.log.error("Bluetooth Off event not found. Expected {}".format(
230                bluetooth_off))
231            self.log.info(self.scn_ad.droid.bluetoothCheckState())
232            return False
233
234        # Sleep because LE turns off after the bluetooth off event fires
235        time.sleep(self.default_timeout)
236        state = self.scn_ad.droid.bluetoothGetLeState()
237        if state != bt_adapter_states['ble_on']:
238            self.log.error(
239                ble_state_error_msg.format("after BT Disable",
240                                           bt_adapter_states['ble_on'], state))
241            return False
242
243        self.scn_ad.droid.bluetoothListenForBleStateChange()
244        self.scn_ad.droid.connectivityToggleAirplaneMode(True)
245        try:
246            self.scn_ad.ed.pop_event(bluetooth_le_off, self.default_timeout)
247        except Empty:
248            self.log.error(
249                "Bluetooth LE Off event not found. Expected {}".format(
250                    bluetooth_le_off))
251            return False
252        state = self.scn_ad.droid.bluetoothGetLeState()
253        if state != bt_adapter_states['off']:
254            self.log.error(
255                ble_state_error_msg.format("after Airplane Mode ON",
256                                           bt_adapter_states['off'], state))
257            return False
258        result = self.scn_ad.droid.bluetoothEnableBLE()
259        if result:
260            self.log.error(
261                "Bluetooth Enable command succeded when it should have failed (in airplane mode)"
262            )
263            return False
264        state = self.scn_ad.droid.bluetoothGetLeState()
265        if state != bt_adapter_states['off']:
266            self.log.error(
267                "Bluetooth LE State not OK after attempted enable. Expected {} got {}".
268                format(bt_adapter_states['off'], state))
269            return False
270        self.scn_ad.droid.connectivityToggleAirplaneMode(False)
271        # Sleep to let Airplane Mode disable propogate through the system
272        time.sleep(self.default_timeout)
273        state = self.scn_ad.droid.bluetoothGetLeState()
274        if state != bt_adapter_states['off']:
275            self.log.error(
276                ble_state_error_msg.format("after Airplane Mode OFF",
277                                           bt_adapter_states['off'], state))
278            return False
279        return True
280