Home | History | Annotate | Download | only in bt
      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 """
     17 Test script to automate the Bluetooth Audio Funhaus.
     18 """
     19 from acts.keys import Config
     20 from acts.test_utils.bt.BtMetricsBaseTest import BtMetricsBaseTest
     21 from acts.test_utils.bt.bt_test_utils import bluetooth_enabled_check
     22 from acts.utils import bypass_setup_wizard
     23 from acts.utils import create_dir
     24 from acts.utils import exe_cmd
     25 from acts.utils import sync_device_time
     26 import json
     27 import time
     28 import os
     29 
     30 BT_CONF_PATH = "/data/misc/bluedroid/bt_config.conf"
     31 
     32 
     33 class BtFunhausBaseTest(BtMetricsBaseTest):
     34     """
     35     Base class for Bluetooth A2DP audio tests, this class is in charge of
     36     pushing link key to Android device so that it could be paired with remote
     37     A2DP device, pushing music to Android device, playing audio, monitoring
     38     audio play, and stop playing audio
     39     """
     40     music_file_to_play = ""
     41     device_fails_to_connect_list = []
     42 
     43     def __init__(self, controllers):
     44         BtMetricsBaseTest.__init__(self, controllers)
     45         self.ad = self.android_devices[0]
     46         self.dongle = self.relay_devices[0]
     47 
     48     def _pair_devices(self):
     49         self.ad.droid.bluetoothStartPairingHelper(False)
     50         self.dongle.enter_pairing_mode()
     51 
     52         self.ad.droid.bluetoothBond(self.dongle.mac_address)
     53 
     54         end_time = time.time() + 20
     55         self.ad.log.info("Verifying devices are bonded")
     56         while time.time() < end_time:
     57             bonded_devices = self.ad.droid.bluetoothGetBondedDevices()
     58 
     59             for d in bonded_devices:
     60                 if d['address'] == self.dongle.mac_address:
     61                     self.ad.log.info("Successfully bonded to device.")
     62                     self.log.info("Bonded devices:\n{}".format(bonded_devices))
     63                 return True
     64         self.ad.log.info("Failed to bond devices.")
     65         return False
     66 
     67     def setup_test(self):
     68         super(BtFunhausBaseTest, self).setup_test()
     69         self.dongle.setup()
     70         tries = 5
     71         # Since we are not concerned with pairing in this test, try 5 times.
     72         while tries > 0:
     73             if self._pair_devices():
     74                 return True
     75             else:
     76                 tries -= 1
     77         return False
     78 
     79     def teardown_test(self):
     80         super(BtFunhausBaseTest, self).teardown_test()
     81         self.dongle.clean_up()
     82         return True
     83 
     84     def on_fail(self, test_name, begin_time):
     85         self.dongle.clean_up()
     86         self._collect_bluetooth_manager_dumpsys_logs(self.android_devices)
     87         super(BtFunhausBaseTest, self).on_fail(test_name, begin_time)
     88 
     89     def setup_class(self):
     90         if not super(BtFunhausBaseTest, self).setup_class():
     91             return False
     92         for ad in self.android_devices:
     93             sync_device_time(ad)
     94             # Disable Bluetooth HCI Snoop Logs for audio tests
     95             ad.adb.shell("setprop persist.bluetooth.btsnoopenable false")
     96             if not bypass_setup_wizard(ad):
     97                 self.log.debug(
     98                     "Failed to bypass setup wizard, continuing test.")
     99                 # Add music to the Android device
    100         return self._add_music_to_android_device(ad)
    101 
    102     def _add_music_to_android_device(self, ad):
    103         """
    104         Add music to Android device as specified by the test config
    105         :param ad: Android device
    106         :return: True on success, False on failure
    107         """
    108         self.log.info("Pushing music to the Android device.")
    109         music_path_str = "bt_music"
    110         android_music_path = "/sdcard/Music/"
    111         if music_path_str not in self.user_params:
    112             self.log.error("Need music for audio testcases...")
    113             return False
    114         music_path = self.user_params[music_path_str]
    115         if type(music_path) is list:
    116             self.log.info("Media ready to push as is.")
    117         elif not os.path.isdir(music_path):
    118             music_path = os.path.join(self.user_params[Config.key_config_path],
    119                                       music_path)
    120             if not os.path.isdir(music_path):
    121                 self.log.error(
    122                     "Unable to find music directory {}.".format(music_path))
    123                 return False
    124         if type(music_path) is list:
    125             for item in music_path:
    126                 self.music_file_to_play = item
    127                 ad.adb.push("{} {}".format(item, android_music_path))
    128         else:
    129             for dirname, dirnames, filenames in os.walk(music_path):
    130                 for filename in filenames:
    131                     self.music_file_to_play = filename
    132                     file = os.path.join(dirname, filename)
    133                     # TODO: Handle file paths with spaces
    134                     ad.adb.push("{} {}".format(file, android_music_path))
    135         ad.reboot()
    136         return True
    137 
    138     def _collect_bluetooth_manager_dumpsys_logs(self, ads):
    139         """
    140         Collect "adb shell dumpsys bluetooth_manager" logs
    141         :param ads: list of active Android devices
    142         :return: None
    143         """
    144         for ad in ads:
    145             serial = ad.serial
    146             out_name = "{}_{}".format(serial, "bluetooth_dumpsys.txt")
    147             dumpsys_path = ''.join((ad.log_path, "/BluetoothDumpsys"))
    148             create_dir(dumpsys_path)
    149             cmd = ''.join(
    150                 ("adb -s ", serial, " shell dumpsys bluetooth_manager > ",
    151                  dumpsys_path, "/", out_name))
    152             exe_cmd(cmd)
    153 
    154     def start_playing_music_on_all_devices(self):
    155         """
    156         Start playing music
    157         :return: None
    158         """
    159         self.ad.droid.mediaPlayOpen("file:///sdcard/Music/{}".format(
    160             self.music_file_to_play.split("/")[-1]))
    161         self.ad.droid.mediaPlaySetLooping(True)
    162         self.ad.log.info("Music is now playing.")
    163 
    164     def monitor_music_play_util_deadline(self, end_time, sleep_interval=1):
    165         """
    166         Monitor music play on all devices, if a device's Bluetooth adapter is
    167         OFF or if a device is not connected to any remote Bluetooth devices,
    168         we add them to failure lists bluetooth_off_list and
    169         device_not_connected_list respectively
    170         :param end_time: The deadline in epoch floating point seconds that we
    171             must stop playing
    172         :param sleep_interval: How often to monitor, too small we may drain
    173             too much resources on Android, too big the deadline might be passed
    174             by a maximum of this amount
    175         :return:
    176             status: False iff all devices are off or disconnected otherwise True
    177             bluetooth_off_list: List of ADs that have Bluetooth at OFF state
    178             device_not_connected_list: List of ADs with no remote device
    179                                         connected
    180         """
    181         device_not_connected_list = []
    182         while time.time() < end_time:
    183             if not self.ad.droid.bluetoothCheckState():
    184                 self.ad.log.error("Device {}'s Bluetooth state is off.".format(
    185                     self.ad.serial))
    186                 return False
    187             if self.ad.droid.bluetoothGetConnectedDevices() == 0:
    188                 self.ad.log.error(
    189                     "Bluetooth device not connected. Failing test.")
    190             time.sleep(sleep_interval)
    191         return True
    192 
    193     def play_music_for_duration(self, duration, sleep_interval=1):
    194         """
    195         A convenience method for above methods. It starts run music on all
    196         devices, monitors the health of music play and stops playing them when
    197         time passes the duration
    198         :param duration: Duration in floating point seconds
    199         :param sleep_interval: How often to check the health of music play
    200         :return:
    201             status: False iff all devices are off or disconnected otherwise True
    202             bluetooth_off_list: List of ADs that have Bluetooth at OFF state
    203             device_not_connected_list: List of ADs with no remote device
    204                                         connected
    205         """
    206         start_time = time.time()
    207         end_time = start_time + duration
    208         self.start_playing_music_on_all_devices()
    209         status = self.monitor_music_play_util_deadline(end_time,
    210                                                        sleep_interval)
    211         self.ad.droid.mediaPlayStopAll()
    212         return status
    213