Home | History | Annotate | Download | only in car_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 Automated tests for the testing passthrough commands in Avrcp/A2dp profile.
     18 """
     19 
     20 import os
     21 import time
     22 
     23 from acts.test_utils.bt.BluetoothBaseTest import BluetoothBaseTest
     24 from acts.test_utils.bt import bt_test_utils
     25 from acts.test_utils.bt import BtEnum
     26 from acts.test_utils.car import car_media_utils
     27 from acts.utils import exe_cmd
     28 from acts.controllers import adb
     29 
     30 DEFAULT_WAIT_TIME = 1.0
     31 DEFAULT_EVENT_TIMEOUT = 1.0
     32 PHONE_MEDIA_BROWSER_SERVICE_NAME = 'BluetoothSL4AAudioSrcMBS'
     33 CAR_MEDIA_BROWSER_SERVICE_NAME = 'A2dpMediaBrowserService'
     34 # This test requires some media files to play, skip and compare metadata.
     35 # The setup part of BtCarMediaPassthroughTest pushes media files from
     36 # the local_media_path in user params defined below to ANDROID_MEDIA_PATH
     37 # via adb. Before running these tests, place some media files in your host
     38 # location.
     39 ANDROID_MEDIA_PATH = '/sdcard/Music/test'
     40 
     41 
     42 class BtCarMediaPassthroughTest(BluetoothBaseTest):
     43     local_media_path = ""
     44 
     45     def setup_class(self):
     46         if not super(BtCarMediaPassthroughTest, self).setup_class():
     47             return False
     48         # AVRCP roles
     49         self.CT = self.android_devices[0]
     50         self.TG = self.android_devices[1]
     51         # A2DP roles for the same devices
     52         self.SNK = self.CT
     53         self.SRC = self.TG
     54         # To keep track of the state of the MediaBrowserService
     55         self.mediaBrowserServiceRunning = False
     56         self.btAddrCT = self.CT.droid.bluetoothGetLocalAddress()
     57         self.btAddrTG = self.TG.droid.bluetoothGetLocalAddress()
     58         self.android_music_path = ANDROID_MEDIA_PATH
     59 
     60         if not "local_media_path" in self.user_params.keys():
     61             self.log.error(
     62                 "Missing mandatory user config \"local_media_path\"!")
     63             return False
     64         self.local_media_path = self.user_params["local_media_path"]
     65         if not os.path.isdir(self.local_media_path):
     66             self.local_media_path = os.path.join(
     67                 self.user_params[Config.key_config_path],
     68                 self.local_media_path)
     69             if not os.path.isdir(self.local_media_path):
     70                 self.log.error("Unable to load user config " + self.
     71                                local_media_path + " from test config file.")
     72                 return False
     73 
     74         # Additional time from the stack reset in setup.
     75         time.sleep(4)
     76         # Pair and connect the devices.
     77         if not bt_test_utils.pair_pri_to_sec(
     78                 self.CT, self.TG, attempts=4, auto_confirm=False):
     79             self.log.error("Failed to pair")
     80             return False
     81 
     82         # TODO - check for Avrcp Connection state as well.
     83         # For now, the passthrough tests will catch Avrcp Connection failures
     84         # But add an explicit test for it.
     85         bt_test_utils.connect_pri_to_sec(
     86             self.SNK, self.SRC, set([BtEnum.BluetoothProfile.A2DP_SINK.value]))
     87 
     88         # Push media files from self.local_media_path to ANDROID_MEDIA_PATH
     89         # Refer to note in the beginning of file
     90         self.TG.adb.push("{} {}".format(self.local_media_path,
     91                                         self.android_music_path))
     92 
     93         return True
     94 
     95     def _init_mbs(self):
     96         """
     97         This is required to be done before running any of the passthrough
     98         commands.
     99         1. Starts up the AvrcpMediaBrowserService on the TG.
    100            This MediaBrowserService is part of the SL4A app
    101         2. Connects a MediaBrowser to the Carkitt's A2dpMediaBrowserService
    102         """
    103         if (not self.mediaBrowserServiceRunning):
    104             self.TG.log.info("Starting AvrcpMediaBrowserService")
    105             self.TG.droid.bluetoothMediaPhoneSL4AMBSStart()
    106             time.sleep(DEFAULT_WAIT_TIME)
    107             self.mediaBrowserServiceRunning = True
    108 
    109         self.CT.droid.bluetoothMediaConnectToCarMBS()
    110         #TODO - Wait for an event back instead of sleep
    111         time.sleep(DEFAULT_WAIT_TIME)
    112 
    113     def teardown_test(self):
    114         # Stop the browser service if it is running to clean up the slate.
    115         if self.mediaBrowserServiceRunning:
    116             self.TG.log.info("Stopping AvrcpMediaBrowserService")
    117             self.TG.droid.bluetoothMediaPhoneSL4AMBSStop()
    118             self.mediaBrowserServiceRunning = False
    119         if not super(BtCarMediaPassthroughTest, self).teardown_test():
    120             return False
    121         # If A2dp connection was disconnected as part of the test, connect it back
    122         if not (car_media_utils.is_a2dp_connected(self.log, self.SNK,
    123                                                   self.SRC)):
    124             result = bt_test_utils.connect_pri_to_sec(
    125                 self.SRC, self.SNK, set([BtEnum.BluetoothProfile.A2DP.value]))
    126             if not result:
    127                 if not bt_test_utils.is_a2dp_src_device_connected(
    128                         self.SRC, self.SNK.droid.bluetoothGetLocalAddress()):
    129                     self.SRC.log.error("Failed to connect on A2dp")
    130                     return False
    131         return True
    132 
    133     #@BluetoothTest(UUID=cf4fae08-f4f6-4e0d-b00a-4f6c41d69ff9)
    134     @BluetoothBaseTest.bt_test_wrap
    135     def test_play_pause(self):
    136         """
    137         Test the Play and Pause passthrough commands
    138 
    139         Pre-Condition:
    140         1. Devices previously bonded & Connected
    141 
    142         Steps:
    143         1. Invoke Play, Pause from CT
    144         2. Wait to receive the corresponding received event from TG
    145 
    146         Returns:
    147         True    if the event was received
    148         False   if the event was not received
    149 
    150         Priority: 0
    151         """
    152         # Set up the MediaBrowserService
    153         self._init_mbs()
    154         if not car_media_utils.send_media_passthrough_cmd(
    155                 self.log, self.CT, self.TG, car_media_utils.CMD_MEDIA_PLAY,
    156                 car_media_utils.EVENT_PLAY_RECEIVED, DEFAULT_EVENT_TIMEOUT):
    157             return False
    158         if not car_media_utils.send_media_passthrough_cmd(
    159                 self.log, self.CT, self.TG, car_media_utils.CMD_MEDIA_PAUSE,
    160                 car_media_utils.EVENT_PAUSE_RECEIVED, DEFAULT_EVENT_TIMEOUT):
    161             return False
    162         return True
    163 
    164     #@BluetoothTest(UUID=15615b26-3a49-4fa0-b369-41962e8de192)
    165     @BluetoothBaseTest.bt_test_wrap
    166     def test_passthrough(self):
    167         """
    168         Test the Skip Next & Skip Previous passthrough commands
    169 
    170         Pre-Condition:
    171         1. Devices previously bonded & Connected
    172 
    173         Steps:
    174         1. Invoke other passthrough commands (skip >> & <<) from CT
    175         2. Wait to receive the corresponding received event from TG
    176 
    177         Returns:
    178         True    if the event was received
    179         False   if the event was not received
    180 
    181         Priority: 0
    182         """
    183         # Set up the MediaBrowserService
    184         self._init_mbs()
    185         if not car_media_utils.send_media_passthrough_cmd(
    186                 self.log, self.CT, self.TG,
    187                 car_media_utils.CMD_MEDIA_SKIP_NEXT,
    188                 car_media_utils.EVENT_SKIP_NEXT_RECEIVED,
    189                 DEFAULT_EVENT_TIMEOUT):
    190             return False
    191         if not car_media_utils.send_media_passthrough_cmd(
    192                 self.log, self.CT, self.TG,
    193                 car_media_utils.CMD_MEDIA_SKIP_PREV,
    194                 car_media_utils.EVENT_SKIP_PREV_RECEIVED,
    195                 DEFAULT_EVENT_TIMEOUT):
    196             return False
    197 
    198         # Just pause media before test ends
    199         if not car_media_utils.send_media_passthrough_cmd(
    200                 self.log, self.CT, self.TG, car_media_utils.CMD_MEDIA_PAUSE,
    201                 car_media_utils.EVENT_PAUSE_RECEIVED):
    202             return False
    203 
    204         return True
    205 
    206     @BluetoothBaseTest.bt_test_wrap
    207     def test_media_metadata(self):
    208         """
    209         Test if the metadata matches between the two ends.
    210         Send some random sequence of passthrough commands and compare metadata.
    211         TODO: truely randomize of the seq of passthrough commands.
    212         Pre-Condition:
    213         1. Devices previously bonded & Connected
    214 
    215         Steps:
    216         1. Invoke Play from CT
    217         2. Compare the metadata between CT and TG. Fail if they don't match
    218         3. Send Skip Next from CT
    219         4. Compare the metadata between CT and TG. Fail if they don't match
    220         5. Repeat steps 3 & 4
    221         6. Send Skip Prev from CT
    222         7. Compare the metadata between CT and TG. Fail if they don't match
    223 
    224         Returns:
    225         True    if the metadata matched all the way
    226         False   if there was a metadata mismatch at any point
    227 
    228         Priority: 0
    229         """
    230         if not (car_media_utils.is_a2dp_connected(self.log, self.SNK,
    231                                                   self.SRC)):
    232             self.SNK.log.error('No A2dp Connection')
    233             return False
    234 
    235         self._init_mbs()
    236         if not car_media_utils.send_media_passthrough_cmd(
    237                 self.log, self.CT, self.TG, car_media_utils.CMD_MEDIA_PLAY,
    238                 car_media_utils.EVENT_PLAY_RECEIVED, DEFAULT_EVENT_TIMEOUT):
    239             return False
    240         time.sleep(DEFAULT_WAIT_TIME)
    241         if not car_media_utils.check_metadata(self.log, self.TG, self.CT):
    242             return False
    243 
    244         if not car_media_utils.send_media_passthrough_cmd(
    245                 self.log, self.CT, self.TG,
    246                 car_media_utils.CMD_MEDIA_SKIP_NEXT,
    247                 car_media_utils.EVENT_SKIP_NEXT_RECEIVED,
    248                 DEFAULT_EVENT_TIMEOUT):
    249             return False
    250         time.sleep(DEFAULT_WAIT_TIME)
    251         if not car_media_utils.check_metadata(self.log, self.TG, self.CT):
    252             return False
    253 
    254         if not car_media_utils.send_media_passthrough_cmd(
    255                 self.log, self.CT, self.TG,
    256                 car_media_utils.CMD_MEDIA_SKIP_NEXT,
    257                 car_media_utils.EVENT_SKIP_NEXT_RECEIVED,
    258                 DEFAULT_EVENT_TIMEOUT):
    259             return False
    260         time.sleep(DEFAULT_WAIT_TIME)
    261         if not car_media_utils.check_metadata(self.log, self.TG, self.CT):
    262             return False
    263 
    264         if not car_media_utils.send_media_passthrough_cmd(
    265                 self.log, self.CT, self.TG,
    266                 car_media_utils.CMD_MEDIA_SKIP_PREV,
    267                 car_media_utils.EVENT_SKIP_PREV_RECEIVED,
    268                 DEFAULT_EVENT_TIMEOUT):
    269             return False
    270         time.sleep(DEFAULT_WAIT_TIME)
    271         if not car_media_utils.check_metadata(self.log, self.TG, self.CT):
    272             return False
    273 
    274     @BluetoothBaseTest.bt_test_wrap
    275     def test_disconnect_while_media_playing(self):
    276         """
    277         Disconnect BT between CT and TG in the middle of a audio streaming session and check
    278         1) If TG continues to still play music
    279         2) If CT stops playing
    280 
    281         Pre-Condition:
    282         1. Devices previously bonded & Connected
    283 
    284         Steps:
    285         1. Invoke Play from CT
    286         2. Check if both CT and TG are playing music by checking if the respective
    287            MediaSessions are active
    288         3. Fail if either the CT or TG is not playing
    289         4. Disconnect Bluetooth connection between CT and TG
    290         5. Check if the CT MediaSession stopped being active.
    291            Fail if its mediasession is still active.
    292         6. Check if the TG MediaSession is still active.
    293         7. Fail if TG stopped playing music.
    294 
    295         Returns:
    296         True    if the CT stopped playing audio and the TG continued after BT disconnect
    297         False   if the CT still was playing audio or TG stopped after BT disconnect.
    298 
    299         Priority: 0
    300         """
    301         self._init_mbs()
    302         self.log.info("Sending Play command from Car")
    303         if not car_media_utils.send_media_passthrough_cmd(
    304                 self.log, self.CT, self.TG, car_media_utils.CMD_MEDIA_PLAY,
    305                 car_media_utils.EVENT_PLAY_RECEIVED, DEFAULT_EVENT_TIMEOUT):
    306             return False
    307 
    308         time.sleep(DEFAULT_WAIT_TIME)
    309 
    310         self.TG.log.info("Phone Media Sessions:")
    311         if not car_media_utils.isMediaSessionActive(
    312                 self.log, self.TG, PHONE_MEDIA_BROWSER_SERVICE_NAME):
    313             self.TG.log.error("Media not playing in connected Phone")
    314             return False
    315 
    316         self.CT.log.info("Car Media Sessions:")
    317         if not car_media_utils.isMediaSessionActive(
    318                 self.log, self.CT, CAR_MEDIA_BROWSER_SERVICE_NAME):
    319             self.CT.log.error("Media not playing in connected Car")
    320             return False
    321 
    322         self.log.info("Bluetooth Disconnect the car and phone")
    323         result = bt_test_utils.disconnect_pri_from_sec(
    324             self.SRC, self.SNK, [BtEnum.BluetoothProfile.A2DP.value])
    325         if not result:
    326             if bt_test_utils.is_a2dp_src_device_connected(
    327                     self.SRC, self.SNK.droid.bluetoothGetLocalAddress()):
    328                 self.SRC.log.error("Failed to disconnect on A2dp")
    329                 return False
    330 
    331         self.TG.log.info("Phone Media Sessions:")
    332         if not car_media_utils.isMediaSessionActive(
    333                 self.log, self.TG, PHONE_MEDIA_BROWSER_SERVICE_NAME):
    334             self.TG.log.error(
    335                 "Media stopped playing in phone after BT disconnect")
    336             return False
    337 
    338         self.CT.log.info("Car Media Sessions:")
    339         if car_media_utils.isMediaSessionActive(
    340                 self.log, self.CT, CAR_MEDIA_BROWSER_SERVICE_NAME):
    341             self.CT.log.error(
    342                 "Media still playing in a Car after BT disconnect")
    343             return False
    344 
    345         return True
    346 
    347     @BluetoothBaseTest.bt_test_wrap
    348     def test_connect_while_media_playing(self):
    349         """
    350         BT connect SRC and SNK when the SRC is already playing music and verify SNK strarts streaming
    351         after connection.
    352         Connect to another device (Audio Sink) via BT while it is playing audio.
    353         Check if the audio starts streaming on the Sink.
    354 
    355         Pre-Condition:
    356         1. Devices previously bonded & Connected
    357 
    358         Steps:
    359         1. Disconnect TG from CT (since they are connected as a precondition)
    360         2. Play Music on TG (Audio SRC)
    361         3. Get the metadata of the playing music
    362         4. Connect TG and CT
    363         5. Check if the music is streaming on CT (Audio SNK) by checking if its MediaSession became active.
    364         6. Fail if CT is not streaming.
    365         7. Get the metdata from the CT (Audio SNK) and compare it with the metadata from Step 3
    366         8. Fail if the metadata did not match.
    367 
    368         Returns:
    369         True    if the event was received
    370         False   if the event was not received
    371 
    372         Priority: 0
    373         """
    374         self.log.info("Bluetooth Disconnect the car and phone")
    375         result = bt_test_utils.disconnect_pri_from_sec(
    376             self.SRC, self.SNK, [BtEnum.BluetoothProfile.A2DP.value])
    377         if not result:
    378             # Temporary timeout
    379             time.sleep(3)
    380             if bt_test_utils.is_a2dp_src_device_connected(
    381                     self.SRC, self.SNK.droid.bluetoothGetLocalAddress()):
    382                 self.SRC.log.error("Failed to disconnect on A2dp")
    383                 return False
    384 
    385         self._init_mbs()
    386 
    387         # Play Media on Phone
    388         self.TG.droid.bluetoothMediaHandleMediaCommandOnPhone(
    389             car_media_utils.CMD_MEDIA_PLAY)
    390         # At this point, media should be playing only on phone, not on Car, since they are disconnected
    391         if not car_media_utils.isMediaSessionActive(
    392                 self.log, self.TG,
    393                 PHONE_MEDIA_BROWSER_SERVICE_NAME) or car_media_utils.isMediaSessionActive(
    394                     self.log, self.CT, CAR_MEDIA_BROWSER_SERVICE_NAME):
    395             self.log.error("Media playing in wrong end")
    396             return False
    397 
    398         # Get the metadata of the song that the phone is playing
    399         metadata_TG = self.TG.droid.bluetoothMediaGetCurrentMediaMetaData()
    400         if metadata_TG is None:
    401             self.TG.log.error("No Media Metadata available from Phone")
    402             return False
    403 
    404         # Now connect to Car on Bluetooth
    405         if (not bt_test_utils.connect_pri_to_sec(self.SRC, self.SNK, set(
    406             [BtEnum.BluetoothProfile.A2DP.value]))):
    407             return False
    408 
    409         # Wait for a bit for the information to show up in the car side
    410         time.sleep(2)
    411 
    412         # At this point, since we have connected while the Phone was playing media, the car
    413         # should automatically play.  Both devices should have their respective MediaSessions active
    414         if not car_media_utils.isMediaSessionActive(
    415                 self.log, self.TG, PHONE_MEDIA_BROWSER_SERVICE_NAME):
    416             self.TG.log.error("Media not playing in Phone")
    417             return False
    418         if not car_media_utils.isMediaSessionActive(
    419                 self.log, self.CT, CAR_MEDIA_BROWSER_SERVICE_NAME):
    420             self.CT.log.error("Media not playing in Car")
    421             return False
    422 
    423         # Get the metadata from Car and compare it with the Phone's media metadata before the connection happened.
    424         metadata_CT = self.CT.droid.bluetoothMediaGetCurrentMediaMetaData()
    425         if metadata_CT is None:
    426             self.CT.log.info("No Media Metadata available from car")
    427         return car_media_utils.compare_metadata(self.log, metadata_TG,
    428                                                 metadata_CT)
    429