Home | History | Annotate | Download | only in multimedia
      1 # Copyright 2016 The Chromium OS Authors. All rights reserved.
      2 # Use of this source code is governed by a BSD-style license that can be
      3 # found in the LICENSE file.
      4 
      5 """Resource manager to access the ARC-related functionality."""
      6 
      7 import logging
      8 import os
      9 import pipes
     10 import time
     11 
     12 from autotest_lib.client.bin import utils
     13 from autotest_lib.client.common_lib import error
     14 from autotest_lib.client.common_lib.cros import arc
     15 from autotest_lib.client.cros.multimedia import arc_resource_common
     16 from autotest_lib.client.cros.input_playback import input_playback
     17 
     18 
     19 def set_tag(tag):
     20     """Sets a tag file.
     21 
     22     @param tag: Path to the tag file.
     23 
     24     """
     25     open(tag, 'w').close()
     26 
     27 
     28 def tag_exists(tag):
     29     """Checks if a tag exists.
     30 
     31     @param tag: Path to the tag file.
     32 
     33     """
     34     return os.path.exists(tag)
     35 
     36 
     37 class ArcMicrophoneResourceException(Exception):
     38     """Exceptions in ArcResource."""
     39     pass
     40 
     41 
     42 class ArcMicrophoneResource(object):
     43     """Class to manage microphone app in container."""
     44     _MICROPHONE_ACTIVITY = 'org.chromium.arc.testapp.microphone/.MainActivity'
     45     _MICROPHONE_PACKAGE = 'org.chromium.arc.testapp.microphone'
     46     _MICROPHONE_RECORD_PATH = '/storage/emulated/0/recorded.amr-nb'
     47     _MICROPHONE_PERMISSIONS = ['RECORD_AUDIO', 'WRITE_EXTERNAL_STORAGE',
     48                                'READ_EXTERNAL_STORAGE']
     49 
     50     def __init__(self):
     51         """Initializes a ArcMicrophoneResource."""
     52         self._mic_app_start_time = None
     53 
     54 
     55     def start_microphone_app(self):
     56         """Starts microphone app to start recording.
     57 
     58         Starts microphone app. The app starts recorder itself after start up.
     59 
     60         @raises: ArcMicrophoneResourceException if microphone app is not ready
     61                  yet.
     62 
     63         """
     64         if not tag_exists(arc_resource_common.MicrophoneProps.READY_TAG_FILE):
     65             raise ArcMicrophoneResourceException(
     66                     'Microphone app is not ready yet.')
     67 
     68         if self._mic_app_start_time:
     69             raise ArcMicrophoneResourceException(
     70                     'Microphone app is already started.')
     71 
     72         # In case the permissions are cleared, set the permission again before
     73         # each start of the app.
     74         self._set_permission()
     75         self._start_app()
     76         self._mic_app_start_time = time.time()
     77 
     78 
     79     def stop_microphone_app(self, dest_path):
     80         """Stops microphone app and gets recorded audio file from container.
     81 
     82         Stops microphone app.
     83         Copies the recorded file from container to Cros device.
     84         Deletes the recorded file in container.
     85 
     86         @param dest_path: Destination path of the recorded file on Cros device.
     87 
     88         @raises: ArcMicrophoneResourceException if microphone app is not started
     89                  yet or is still recording.
     90 
     91         """
     92         if not self._mic_app_start_time:
     93             raise ArcMicrophoneResourceException(
     94                     'Recording is not started yet')
     95 
     96         if self._is_recording():
     97             raise ArcMicrophoneResourceException('Still recording')
     98 
     99         self._stop_app()
    100         self._get_file(dest_path)
    101         self._delete_file()
    102 
    103         self._mic_app_start_time = None
    104 
    105 
    106     def _is_recording(self):
    107         """Checks if microphone app is recording audio.
    108 
    109         We use the time stamp of app start up time to determine if app is still
    110         recording audio.
    111 
    112         @returns: True if microphone app is recording, False otherwise.
    113 
    114         """
    115         if not self._mic_app_start_time:
    116             return False
    117 
    118         return (time.time() - self._mic_app_start_time <
    119                 (arc_resource_common.MicrophoneProps.RECORD_SECS +
    120                  arc_resource_common.MicrophoneProps.RECORD_FUZZ_SECS))
    121 
    122 
    123     def _set_permission(self):
    124         """Grants permissions to microphone app."""
    125         for permission in self._MICROPHONE_PERMISSIONS:
    126             arc.adb_shell('pm grant %s android.permission.%s' % (
    127                     pipes.quote(self._MICROPHONE_PACKAGE),
    128                     pipes.quote(permission)))
    129 
    130 
    131     def _start_app(self):
    132         """Starts microphone app."""
    133         arc.adb_shell('am start -W %s' % pipes.quote(self._MICROPHONE_ACTIVITY))
    134 
    135 
    136     def _stop_app(self):
    137         """Stops microphone app.
    138 
    139         Stops the microphone app process.
    140 
    141         """
    142         arc.adb_shell(
    143                 'am force-stop %s' % pipes.quote(self._MICROPHONE_PACKAGE))
    144 
    145 
    146     def _get_file(self, dest_path):
    147         """Gets recorded audio file from container.
    148 
    149         Copies the recorded file from container to Cros device.
    150 
    151         @dest_path: Destination path of the recorded file on Cros device.
    152 
    153         """
    154         arc.adb_cmd('pull %s %s' % (pipes.quote(self._MICROPHONE_RECORD_PATH),
    155                                     pipes.quote(dest_path)))
    156 
    157 
    158     def _delete_file(self):
    159         """Removes the recorded file in container."""
    160         arc.adb_shell('rm %s' % pipes.quote(self._MICROPHONE_RECORD_PATH))
    161 
    162 
    163 class ArcPlayMusicResourceException(Exception):
    164     """Exceptions in ArcPlayMusicResource."""
    165     pass
    166 
    167 
    168 class ArcPlayMusicResource(object):
    169     """Class to manage Play Music app in container."""
    170     _PLAYMUSIC_PACKAGE = 'com.google.android.music'
    171     _PLAYMUSIC_FILE_FOLDER = '/storage/emulated/0/'
    172     _PLAYMUSIC_PERMISSIONS = ['WRITE_EXTERNAL_STORAGE', 'READ_EXTERNAL_STORAGE']
    173     _PLAYMUSIC_ACTIVITY = '.AudioPreview'
    174     _KEYCODE_MEDIA_STOP = 86
    175 
    176     def __init__(self):
    177         """Initializes an ArcPlayMusicResource."""
    178         self._files_pushed = []
    179 
    180 
    181     def set_playback_file(self, file_path):
    182         """Copies file into container.
    183 
    184         @param file_path: Path to the file to play on Cros host.
    185 
    186         @returns: Path to the file in container.
    187 
    188         """
    189         file_name = os.path.basename(file_path)
    190         dest_path = os.path.join(self._PLAYMUSIC_FILE_FOLDER, file_name)
    191 
    192         # pipes.quote is deprecated in 2.7 (but still available).
    193         # It should be replaced by shlex.quote in python 3.3.
    194         arc.adb_cmd('push %s %s' % (pipes.quote(file_path),
    195                                     pipes.quote(dest_path)))
    196 
    197         self._files_pushed.append(dest_path)
    198 
    199         return dest_path
    200 
    201 
    202     def start_playback(self, dest_path):
    203         """Starts Play Music app to play an audio file.
    204 
    205         @param dest_path: The file path in container.
    206 
    207         @raises ArcPlayMusicResourceException: Play Music app is not ready or
    208                                                playback file is not set yet.
    209 
    210         """
    211         if not tag_exists(arc_resource_common.PlayMusicProps.READY_TAG_FILE):
    212             raise ArcPlayMusicResourceException(
    213                     'Play Music app is not ready yet.')
    214 
    215         if dest_path not in self._files_pushed:
    216             raise ArcPlayMusicResourceException(
    217                     'Playback file is not set yet')
    218 
    219         # In case the permissions are cleared, set the permission again before
    220         # each start of the app.
    221         self._set_permission()
    222         self._start_app(dest_path)
    223 
    224 
    225     def _set_permission(self):
    226         """Grants permissions to Play Music app."""
    227         for permission in self._PLAYMUSIC_PERMISSIONS:
    228             arc.adb_shell('pm grant %s android.permission.%s' % (
    229                     pipes.quote(self._PLAYMUSIC_PACKAGE),
    230                     pipes.quote(permission)))
    231 
    232 
    233     def _start_app(self, dest_path):
    234         """Starts Play Music app playing an audio file.
    235 
    236         @param dest_path: Path to the file to play in container.
    237 
    238         """
    239         ext = os.path.splitext(dest_path)[1]
    240         command = ('am start -a android.intent.action.VIEW'
    241                    ' -d "file://%s" -t "audio/%s"'
    242                    ' -n "%s/%s"'% (
    243                           pipes.quote(dest_path), pipes.quote(ext),
    244                           pipes.quote(self._PLAYMUSIC_PACKAGE),
    245                           pipes.quote(self._PLAYMUSIC_ACTIVITY)))
    246         logging.debug(command)
    247         arc.adb_shell(command)
    248 
    249 
    250     def stop_playback(self):
    251         """Stops Play Music app.
    252 
    253         Stops the Play Music app by media key event.
    254 
    255         """
    256         arc.send_keycode(self._KEYCODE_MEDIA_STOP)
    257 
    258 
    259     def cleanup(self):
    260         """Removes the files to play in container."""
    261         for path in self._files_pushed:
    262             arc.adb_shell('rm %s' % pipes.quote(path))
    263         self._files_pushed = []
    264 
    265 
    266 class ArcPlayVideoResourceException(Exception):
    267     """Exceptions in ArcPlayVideoResource."""
    268     pass
    269 
    270 
    271 class ArcPlayVideoResource(object):
    272     """Class to manage Play Video app in container."""
    273     _PLAYVIDEO_PACKAGE = 'org.chromium.arc.testapp.video'
    274     _PLAYVIDEO_ACTIVITY = 'org.chromium.arc.testapp.video/.MainActivity'
    275     _PLAYVIDEO_EXIT_TAG = "/mnt/sdcard/ArcVideoTest.tag"
    276     _PLAYVIDEO_FILE_FOLDER = '/storage/emulated/0/'
    277     _PLAYVIDEO_PERMISSIONS = ['WRITE_EXTERNAL_STORAGE', 'READ_EXTERNAL_STORAGE']
    278     _KEYCODE_MEDIA_PLAY = 126
    279     _KEYCODE_MEDIA_PAUSE = 127
    280     _KEYCODE_MEDIA_STOP = 86
    281 
    282     def __init__(self):
    283         """Initializes an ArcPlayVideoResource."""
    284         self._files_pushed = []
    285 
    286 
    287     def prepare_playback(self, file_path, fullscreen=True):
    288         """Copies file into the container and starts the video player app.
    289 
    290         @param file_path: Path to the file to play on Cros host.
    291         @param fullscreen: Plays the video in fullscreen.
    292 
    293         """
    294         if not tag_exists(arc_resource_common.PlayVideoProps.READY_TAG_FILE):
    295             raise ArcPlayVideoResourceException(
    296                     'Play Video app is not ready yet.')
    297         file_name = os.path.basename(file_path)
    298         dest_path = os.path.join(self._PLAYVIDEO_FILE_FOLDER, file_name)
    299 
    300         # pipes.quote is deprecated in 2.7 (but still available).
    301         # It should be replaced by shlex.quote in python 3.3.
    302         arc.adb_cmd('push %s %s' % (pipes.quote(file_path),
    303                                     pipes.quote(dest_path)))
    304 
    305         # In case the permissions are cleared, set the permission again before
    306         # each start of the app.
    307         self._set_permission()
    308         self._start_app(dest_path)
    309         if fullscreen:
    310             self.set_fullscreen()
    311 
    312 
    313     def set_fullscreen(self):
    314         """Sends F4 keyevent to set fullscreen."""
    315         input_player = input_playback.InputPlayback()
    316         input_player.emulate(input_type='keyboard')
    317         input_player.find_connected_inputs()
    318         input_player.blocking_playback_of_default_file(
    319                 input_type='keyboard', filename='keyboard_f4')
    320 
    321 
    322     def start_playback(self, blocking_secs=None):
    323         """Starts Play Video app to play a video file.
    324 
    325         @param blocking_secs: A positive number indicates the timeout to wait
    326                               for the playback is finished. Set None to make
    327                               it non-blocking.
    328 
    329         @raises ArcPlayVideoResourceException: Play Video app is not ready or
    330                                                playback file is not set yet.
    331 
    332         """
    333         arc.send_keycode(self._KEYCODE_MEDIA_PLAY)
    334 
    335         if blocking_secs:
    336             tag = lambda : arc.check_android_file_exists(
    337                     self._PLAYVIDEO_EXIT_TAG)
    338             exception = error.TestFail('video playback timeout')
    339             utils.poll_for_condition(tag, exception, blocking_secs)
    340 
    341 
    342     def _set_permission(self):
    343         """Grants permissions to Play Video app."""
    344         arc.grant_permissions(
    345                 self._PLAYVIDEO_PACKAGE, self._PLAYVIDEO_PERMISSIONS)
    346 
    347 
    348     def _start_app(self, dest_path):
    349         """Starts Play Video app playing a video file.
    350 
    351         @param dest_path: Path to the file to play in container.
    352 
    353         """
    354         arc.adb_shell('am start --activity-clear-top '
    355                       '--es PATH {} {}'.format(
    356                       pipes.quote(dest_path), self._PLAYVIDEO_ACTIVITY))
    357 
    358 
    359     def pause_playback(self):
    360         """Pauses Play Video app.
    361 
    362         Pauses the Play Video app by media key event.
    363 
    364         """
    365         arc.send_keycode(self._KEYCODE_MEDIA_PAUSE)
    366 
    367 
    368     def stop_playback(self):
    369         """Stops Play Video app.
    370 
    371         Stops the Play Video app by media key event.
    372 
    373         """
    374         arc.send_keycode(self._KEYCODE_MEDIA_STOP)
    375 
    376 
    377     def cleanup(self):
    378         """Removes the files to play in container."""
    379         for path in self._files_pushed:
    380             arc.adb_shell('rm %s' % pipes.quote(path))
    381         self._files_pushed = []
    382 
    383 
    384 class ArcResource(object):
    385     """Class to manage multimedia resource in container.
    386 
    387     @properties:
    388         microphone: The instance of ArcMicrophoneResource for microphone app.
    389         play_music: The instance of ArcPlayMusicResource for music app.
    390         play_video: The instance of ArcPlayVideoResource for video app.
    391 
    392     """
    393     def __init__(self):
    394         self.microphone = ArcMicrophoneResource()
    395         self.play_music = ArcPlayMusicResource()
    396         self.play_video = ArcPlayVideoResource()
    397 
    398 
    399     def cleanup(self):
    400         """Clean up the resources."""
    401         self.play_music.cleanup()
    402         self.play_video.cleanup()
    403