blob: b33831bdd5d9100ef9a61f5cb6627800176df551 [file] [log] [blame]
#!/usr/bin/env python3
#
# Copyright (C) 2016 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
"""
Test script to automate the Bluetooth Audio Funhaus.
"""
from acts.keys import Config
from acts_contrib.test_utils.bt.BtMetricsBaseTest import BtMetricsBaseTest
from acts_contrib.test_utils.bt.bt_test_utils import bluetooth_enabled_check
from acts.utils import bypass_setup_wizard
from acts.utils import exe_cmd
from acts.utils import sync_device_time
import time
import os
BT_CONF_PATH = "/data/misc/bluedroid/bt_config.conf"
class BtFunhausBaseTest(BtMetricsBaseTest):
"""
Base class for Bluetooth A2DP audio tests, this class is in charge of
pushing link key to Android device so that it could be paired with remote
A2DP device, pushing music to Android device, playing audio, monitoring
audio play, and stop playing audio
"""
music_file_to_play = ""
device_fails_to_connect_list = []
def __init__(self, controllers):
BtMetricsBaseTest.__init__(self, controllers)
self.ad = self.android_devices[0]
self.dongle = self.relay_devices[0]
def _pair_devices(self):
self.ad.droid.bluetoothStartPairingHelper(False)
self.dongle.enter_pairing_mode()
self.ad.droid.bluetoothBond(self.dongle.mac_address)
end_time = time.time() + 20
self.ad.log.info("Verifying devices are bonded")
while time.time() < end_time:
bonded_devices = self.ad.droid.bluetoothGetBondedDevices()
for d in bonded_devices:
if d['address'] == self.dongle.mac_address:
self.ad.log.info("Successfully bonded to device.")
self.log.info("Bonded devices:\n{}".format(bonded_devices))
return True
self.ad.log.info("Failed to bond devices.")
return False
def setup_test(self):
super(BtFunhausBaseTest, self).setup_test()
self.dongle.setup()
tries = 5
# Since we are not concerned with pairing in this test, try 5 times.
while tries > 0:
if self._pair_devices():
return True
else:
tries -= 1
return False
def teardown_test(self):
super(BtFunhausBaseTest, self).teardown_test()
self.dongle.clean_up()
return True
def on_fail(self, test_name, begin_time):
self.dongle.clean_up()
self._collect_bluetooth_manager_dumpsys_logs(self.android_devices)
super(BtFunhausBaseTest, self).on_fail(test_name, begin_time)
def setup_class(self):
if not super(BtFunhausBaseTest, self).setup_class():
return False
for ad in self.android_devices:
sync_device_time(ad)
# Disable Bluetooth HCI Snoop Logs for audio tests
ad.adb.shell("setprop persist.bluetooth.btsnoopenable false")
if not bypass_setup_wizard(ad):
self.log.debug(
"Failed to bypass setup wizard, continuing test.")
# Add music to the Android device
return self._add_music_to_android_device(ad)
def _add_music_to_android_device(self, ad):
"""
Add music to Android device as specified by the test config
:param ad: Android device
:return: True on success, False on failure
"""
self.log.info("Pushing music to the Android device.")
music_path_str = "bt_music"
android_music_path = "/sdcard/Music/"
if music_path_str not in self.user_params:
self.log.error("Need music for audio testcases...")
return False
music_path = self.user_params[music_path_str]
if type(music_path) is list:
self.log.info("Media ready to push as is.")
elif not os.path.isdir(music_path):
music_path = os.path.join(
self.user_params[Config.key_config_path.value], music_path)
if not os.path.isdir(music_path):
self.log.error(
"Unable to find music directory {}.".format(music_path))
return False
if type(music_path) is list:
for item in music_path:
self.music_file_to_play = item
ad.adb.push("{} {}".format(item, android_music_path))
else:
for dirname, dirnames, filenames in os.walk(music_path):
for filename in filenames:
self.music_file_to_play = filename
file = os.path.join(dirname, filename)
# TODO: Handle file paths with spaces
ad.adb.push("{} {}".format(file, android_music_path))
ad.reboot()
return True
def _collect_bluetooth_manager_dumpsys_logs(self, ads):
"""
Collect "adb shell dumpsys bluetooth_manager" logs
:param ads: list of active Android devices
:return: None
"""
for ad in ads:
serial = ad.serial
out_name = "{}_{}".format(serial, "bluetooth_dumpsys.txt")
dumpsys_path = ''.join((ad.log_path, "/BluetoothDumpsys"))
os.makedirs(dumpsys_path, exist_ok=True)
cmd = ''.join(
("adb -s ", serial, " shell dumpsys bluetooth_manager > ",
dumpsys_path, "/", out_name))
exe_cmd(cmd)
def start_playing_music_on_all_devices(self):
"""
Start playing music
:return: None
"""
self.ad.droid.mediaPlayOpen("file:///sdcard/Music/{}".format(
self.music_file_to_play.split("/")[-1]))
self.ad.droid.mediaPlaySetLooping(True)
self.ad.log.info("Music is now playing.")
def monitor_music_play_util_deadline(self, end_time, sleep_interval=1):
"""
Monitor music play on all devices, if a device's Bluetooth adapter is
OFF or if a device is not connected to any remote Bluetooth devices,
we add them to failure lists bluetooth_off_list and
device_not_connected_list respectively
:param end_time: The deadline in epoch floating point seconds that we
must stop playing
:param sleep_interval: How often to monitor, too small we may drain
too much resources on Android, too big the deadline might be passed
by a maximum of this amount
:return:
status: False iff all devices are off or disconnected otherwise True
bluetooth_off_list: List of ADs that have Bluetooth at OFF state
device_not_connected_list: List of ADs with no remote device
connected
"""
device_not_connected_list = []
while time.time() < end_time:
if not self.ad.droid.bluetoothCheckState():
self.ad.log.error("Device {}'s Bluetooth state is off.".format(
self.ad.serial))
return False
if self.ad.droid.bluetoothGetConnectedDevices() == 0:
self.ad.log.error(
"Bluetooth device not connected. Failing test.")
time.sleep(sleep_interval)
return True
def play_music_for_duration(self, duration, sleep_interval=1):
"""
A convenience method for above methods. It starts run music on all
devices, monitors the health of music play and stops playing them when
time passes the duration
:param duration: Duration in floating point seconds
:param sleep_interval: How often to check the health of music play
:return:
status: False iff all devices are off or disconnected otherwise True
bluetooth_off_list: List of ADs that have Bluetooth at OFF state
device_not_connected_list: List of ADs with no remote device
connected
"""
start_time = time.time()
end_time = start_time + duration
self.start_playing_music_on_all_devices()
status = self.monitor_music_play_util_deadline(end_time,
sleep_interval)
self.ad.droid.mediaPlayStopAll()
return status