• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 automate the Bluetooth Audio Funhaus.
18"""
19from acts.keys import Config
20from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
21from acts.test_utils.bt.bt_test_utils import bluetooth_enabled_check
22from acts.utils import bypass_setup_wizard
23from acts.utils import create_dir
24from acts.utils import exe_cmd
25from acts.utils import sync_device_time
26from queue import Empty
27import json
28import time
29import os
30
31
32class BtFunhausTest(BluetoothBaseTest):
33    music_file_to_play = ""
34    device_fails_to_connect_list = []
35
36    def __init__(self, controllers):
37        BluetoothBaseTest.__init__(self, controllers)
38
39    def on_fail(self, test_name, begin_time):
40        self._collect_bluetooth_manager_dumpsys_logs(self.android_devices)
41        super(BluetoothBaseTest, self).on_fail(test_name, begin_time)
42
43    def setup_class(self):
44        for ad in self.android_devices:
45            sync_device_time(ad)
46            # Disable Bluetooth HCI Snoop Logs for audio tests
47            ad.droid.bluetoothConfigHciSnoopLog(False)
48            if not bypass_setup_wizard(ad):
49                self.log.debug(
50                    "Failed to bypass setup wizard, continuing test.")
51        if not "bt_config" in self.user_params.keys():
52            self.log.error("Missing mandatory user config \"bt_config\"!")
53            return False
54        bt_config_map_file = self.user_params["bt_config"]
55        return self._setup_bt_config(bt_config_map_file)
56
57    def _setup_bt_config(self, bt_config_map_file):
58        bt_config_map = {}
59        bt_conf_path = "/data/misc/bluedroid/bt_config.conf"
60        if not os.path.isfile(bt_config_map_file):
61            bt_config_map_file = os.path.join(
62                self.user_params[Config.key_config_path], bt_config_map_file)
63            if not os.path.isfile(bt_config_map_file):
64                self.log.error("Unable to load bt_config file {}.".format(
65                    bt_config_map_file))
66                return False
67        try:
68            f = open(bt_config_map_file, 'r')
69            bt_config_map = json.load(f)
70            f.close()
71        except FileNotFoundError:
72            self.log.error("File not found: {}.".format(bt_config_map_file))
73            return False
74        # Connected devices return all upper case mac addresses.
75        # Make the peripheral_info address attribute upper case
76        # in order to ensure the BT mac addresses match.
77        for serial in bt_config_map.keys():
78            mac_address = bt_config_map[serial]["peripheral_info"][
79                "address"].upper()
80            bt_config_map[serial]["peripheral_info"]["address"] = mac_address
81        for ad in self.android_devices:
82            serial = ad.serial
83
84            # Verify serial number in bt_config_map
85            self.log.info("Verify serial number of Android device in config.")
86            if serial not in bt_config_map.keys():
87                self.log.error(
88                    "Missing android device serial {} in bt_config.".format(
89                        serial))
90                return False
91            # Push the bt_config.conf file to Android device
92            self.log.info("Pushing bt_config.conf file to Android device.")
93            config_path = bt_config_map[serial]["config_path"]
94            if not os.path.isfile(config_path):
95                config_path = os.path.join(
96                    self.user_params[Config.key_config_path], config_path)
97                if not os.path.isfile(config_path):
98                    self.log.error("Unable to load bt_config file {}.".format(
99                        config_path))
100                    return False
101            ad.adb.push("{} {}".format(config_path, bt_conf_path))
102
103            # Add music to the Android device
104            self.log.info("Pushing music to the Android device.")
105            music_path_str = "music_path"
106            android_music_path = "/sdcard/Music/"
107            if music_path_str not in self.user_params:
108                self.log.error("Need music for audio testcases...")
109                return False
110
111            music_path = self.user_params[music_path_str]
112            if not os.path.isdir(music_path):
113                music_path = os.path.join(
114                    self.user_params[Config.key_config_path], music_path)
115                if not os.path.isdir(music_path):
116                    self.log.error("Unable to find music directory {}.".format(
117                        music_path))
118                    return False
119            self._add_music_to_primary_android_device(ad, music_path,
120                                                      android_music_path)
121            ad.reboot()
122
123            # Verify Bluetooth is enabled
124            self.log.info("Verifying Bluetooth is enabled on Android Device.")
125            if not bluetooth_enabled_check(ad):
126                self.log.error("Failed to toggle on Bluetooth on device {}".
127                               format(serial))
128                return False
129
130            # Verify Bluetooth device is connected
131            self.log.info(
132                "Waiting up to 10 seconds for device to reconnect...")
133            connected_devices = ad.droid.bluetoothGetConnectedDevices()
134            start_time = time.time()
135            wait_time = 10
136            result = False
137            while time.time() < start_time + wait_time:
138                connected_devices = ad.droid.bluetoothGetConnectedDevices()
139                if len(connected_devices) > 0:
140                    if bt_config_map[serial]["peripheral_info"]["address"] in {
141                            d['address']
142                            for d in connected_devices
143                    }:
144                        result = True
145                        break
146                else:
147                    try:
148                        ad.droid.bluetoothConnectBonded(bt_config_map[serial][
149                            "peripheral_info"]["address"])
150                    except Exception as err:
151                        self.log.error(
152                            "Failed to connect bonded. Err: {}".format(err))
153            if not result:
154                self.log.info("Connected Devices: {}".format(
155                    connected_devices))
156                self.log.info("Bonded Devices: {}".format(
157                    ad.droid.bluetoothGetBondedDevices()))
158                self.log.error(
159                    "Failed to connect to peripheral name: {}, address: {}".
160                    format(bt_config_map[serial]["peripheral_info"]["name"],
161                           bt_config_map[serial]["peripheral_info"][
162                               "address"]))
163                self.device_fails_to_connect_list.append("{}:{}".format(
164                    serial, bt_config_map[serial]["peripheral_info"]["name"]))
165        if len(self.device_fails_to_connect_list) == len(self.android_devices):
166            self.log.error("All devices failed to reconnect.")
167            return False
168        return True
169
170    def _add_music_to_primary_android_device(self, ad, music_path,
171                                             android_music_path):
172        for dirname, dirnames, filenames in os.walk(music_path):
173            for filename in filenames:
174                self.music_file_to_play = filename
175                file = os.path.join(dirname, filename)
176                #TODO: Handle file paths with spaces
177                ad.adb.push("{} {}".format(file, android_music_path))
178        return True
179
180    def _collect_bluetooth_manager_dumpsys_logs(self, ads):
181        for ad in ads:
182            serial = ad.serial
183            out_name = "{}_{}".format(serial, "bluetooth_dumpsys.txt")
184            dumpsys_path = ''.join((ad.log_path, "/BluetoothDumpsys"))
185            create_dir(dumpsys_path)
186            cmd = ''.join(
187                ("adb -s ", serial, " shell dumpsys bluetooth_manager > ",
188                 dumpsys_path, "/", out_name))
189            exe_cmd(cmd)
190
191    @BluetoothBaseTest.bt_test_wrap
192    def test_run_bt_audio_12_hours(self):
193        """Test audio quality over 12 hours.
194
195        This test is designed to run Bluetooth audio for 12 hours
196        and collect relavant logs. If all devices disconnect during
197        the test or Bluetooth is off on all devices, then fail the
198        test.
199
200        Steps:
201        1. For each Android device connected run steps 2-5.
202        2. Open and play media file of music pushed to device
203        3. Set media to loop indefintely.
204        4. After 12 hours collect bluetooth_manager dumpsys information
205        5. Stop media player
206
207        Expected Result:
208        Audio plays for 12 hours over Bluetooth
209
210        Returns:
211          Pass if True
212          Fail if False
213
214        TAGS: Classic, A2DP
215        Priority: 1
216        """
217        for ad in self.android_devices:
218            ad.droid.mediaPlayOpen("file:///sdcard/Music/{}".format(
219                self.music_file_to_play))
220            ad.droid.mediaPlaySetLooping(True)
221            self.log.info("Music is now playing on device {}".format(
222                ad.serial))
223
224        sleep_interval = 120
225        twelve_hours_in_seconds = 43200
226        end_time = time.time() + twelve_hours_in_seconds
227        test_result = True
228        bluetooth_off_list = []
229        device_not_connected_list = []
230        while time.time() < end_time:
231            for ad in self.android_devices:
232                serial = ad.serial
233                if (not ad.droid.bluetoothCheckState() and
234                        serial not in bluetooth_off_list):
235                    self.log.error(
236                        "Device {}'s Bluetooth state is off.".format(serial))
237                    bluetooth_off_list.append(serial)
238                if (ad.droid.bluetoothGetConnectedDevices() == 0 and
239                        serial not in device_not_connected_list):
240                    self.log.error(
241                        "Device {} not connected to any Bluetooth devices.".
242                        format(serial))
243                    device_not_connected_list.append(serial)
244                if len(bluetooth_off_list) == len(self.android_devices):
245                    self.log.error(
246                        "Bluetooth off on all Android devices. Ending Test")
247                    return False
248                if len(device_not_connected_list) == len(self.android_devices):
249                    self.log.error(
250                        "Every Android device has no device connected.")
251                    return False
252            time.sleep(sleep_interval)
253
254        self._collect_bluetooth_manager_dumpsys_logs(self.android_devices)
255        for ad in self.android_devices:
256            ad.droid.mediaPlayStopAll()
257        if len(device_not_connected_list) > 0 or len(bluetooth_off_list) > 0:
258            self.log.info("Devices reported as not connected: {}".format(
259                device_not_connected_list))
260            self.log.info("Devices reported with Bluetooth state off: {}".
261                          format(bluetooth_off_list))
262            return False
263        return True
264
265    def test_setup_fail_if_devices_not_connected(self):
266        """Test for devices connected or not during setup.
267
268        This test is designed to fail if the number of devices having
269        connection issues at time of setup is greater than 0. This lets
270        the test runner know of the stability of the testbed.
271
272        Steps:
273        1. Check lenght of self.device_fails_to_connect_list
274
275        Expected Result:
276        No device should be in a disconnected state.
277
278        Returns:
279          Pass if True
280          Fail if False
281
282        TAGS: None
283        Priority: 1
284        """
285        if len(self.device_fails_to_connect_list) > 0:
286            self.log.error("Devices failed to reconnect:\n{}".format(
287                self.device_fails_to_connect_list))
288            return False
289        return True
290