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