| # Copyright 2016 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Resource manager to access the ARC-related functionality.""" |
| |
| import logging |
| import os |
| import pipes |
| import time |
| |
| from autotest_lib.client.bin import utils |
| from autotest_lib.client.common_lib import error |
| from autotest_lib.client.common_lib.cros import arc |
| from autotest_lib.client.cros.multimedia import arc_resource_common |
| from autotest_lib.client.cros.input_playback import input_playback |
| |
| |
| def set_tag(tag): |
| """Sets a tag file. |
| |
| @param tag: Path to the tag file. |
| |
| """ |
| open(tag, 'w').close() |
| |
| |
| def tag_exists(tag): |
| """Checks if a tag exists. |
| |
| @param tag: Path to the tag file. |
| |
| """ |
| return os.path.exists(tag) |
| |
| |
| class ArcMicrophoneResourceException(Exception): |
| """Exceptions in ArcResource.""" |
| pass |
| |
| |
| class ArcMicrophoneResource(object): |
| """Class to manage microphone app in container.""" |
| _MICROPHONE_ACTIVITY = 'org.chromium.arc.testapp.microphone/.MainActivity' |
| _MICROPHONE_PACKAGE = 'org.chromium.arc.testapp.microphone' |
| _MICROPHONE_RECORD_PATH = '/storage/emulated/0/recorded.amr-nb' |
| _MICROPHONE_PERMISSIONS = ['RECORD_AUDIO', 'WRITE_EXTERNAL_STORAGE', |
| 'READ_EXTERNAL_STORAGE'] |
| |
| def __init__(self): |
| """Initializes a ArcMicrophoneResource.""" |
| self._mic_app_start_time = None |
| |
| |
| def start_microphone_app(self): |
| """Starts microphone app to start recording. |
| |
| Starts microphone app. The app starts recorder itself after start up. |
| |
| @raises: ArcMicrophoneResourceException if microphone app is not ready |
| yet. |
| |
| """ |
| if not tag_exists(arc_resource_common.MicrophoneProps.READY_TAG_FILE): |
| raise ArcMicrophoneResourceException( |
| 'Microphone app is not ready yet.') |
| |
| if self._mic_app_start_time: |
| raise ArcMicrophoneResourceException( |
| 'Microphone app is already started.') |
| |
| # In case the permissions are cleared, set the permission again before |
| # each start of the app. |
| self._set_permission() |
| self._start_app() |
| self._mic_app_start_time = time.time() |
| |
| |
| def stop_microphone_app(self, dest_path): |
| """Stops microphone app and gets recorded audio file from container. |
| |
| Stops microphone app. |
| Copies the recorded file from container to Cros device. |
| Deletes the recorded file in container. |
| |
| @param dest_path: Destination path of the recorded file on Cros device. |
| |
| @raises: ArcMicrophoneResourceException if microphone app is not started |
| yet or is still recording. |
| |
| """ |
| if not self._mic_app_start_time: |
| raise ArcMicrophoneResourceException( |
| 'Recording is not started yet') |
| |
| if self._is_recording(): |
| raise ArcMicrophoneResourceException('Still recording') |
| |
| self._stop_app() |
| self._get_file(dest_path) |
| self._delete_file() |
| |
| self._mic_app_start_time = None |
| |
| |
| def _is_recording(self): |
| """Checks if microphone app is recording audio. |
| |
| We use the time stamp of app start up time to determine if app is still |
| recording audio. |
| |
| @returns: True if microphone app is recording, False otherwise. |
| |
| """ |
| if not self._mic_app_start_time: |
| return False |
| |
| return (time.time() - self._mic_app_start_time < |
| (arc_resource_common.MicrophoneProps.RECORD_SECS + |
| arc_resource_common.MicrophoneProps.RECORD_FUZZ_SECS)) |
| |
| |
| def _set_permission(self): |
| """Grants permissions to microphone app.""" |
| for permission in self._MICROPHONE_PERMISSIONS: |
| arc.adb_shell('pm grant %s android.permission.%s' % ( |
| pipes.quote(self._MICROPHONE_PACKAGE), |
| pipes.quote(permission))) |
| |
| |
| def _start_app(self): |
| """Starts microphone app.""" |
| arc.adb_shell('am start -W %s' % pipes.quote(self._MICROPHONE_ACTIVITY)) |
| |
| |
| def _stop_app(self): |
| """Stops microphone app. |
| |
| Stops the microphone app process. |
| |
| """ |
| arc.adb_shell( |
| 'am force-stop %s' % pipes.quote(self._MICROPHONE_PACKAGE)) |
| |
| |
| def _get_file(self, dest_path): |
| """Gets recorded audio file from container. |
| |
| Copies the recorded file from container to Cros device. |
| |
| @dest_path: Destination path of the recorded file on Cros device. |
| |
| """ |
| arc.adb_cmd('pull %s %s' % (pipes.quote(self._MICROPHONE_RECORD_PATH), |
| pipes.quote(dest_path))) |
| |
| |
| def _delete_file(self): |
| """Removes the recorded file in container.""" |
| arc.adb_shell('rm %s' % pipes.quote(self._MICROPHONE_RECORD_PATH)) |
| |
| |
| class ArcPlayMusicResourceException(Exception): |
| """Exceptions in ArcPlayMusicResource.""" |
| pass |
| |
| |
| class ArcPlayMusicResource(object): |
| """Class to manage Play Music app in container.""" |
| _PLAYMUSIC_PACKAGE = 'com.google.android.music' |
| _PLAYMUSIC_FILE_FOLDER = '/storage/emulated/0/' |
| _PLAYMUSIC_PERMISSIONS = ['WRITE_EXTERNAL_STORAGE', 'READ_EXTERNAL_STORAGE'] |
| _PLAYMUSIC_ACTIVITY = '.AudioPreview' |
| _KEYCODE_MEDIA_STOP = 86 |
| |
| def __init__(self): |
| """Initializes an ArcPlayMusicResource.""" |
| self._files_pushed = [] |
| |
| |
| def set_playback_file(self, file_path): |
| """Copies file into container. |
| |
| @param file_path: Path to the file to play on Cros host. |
| |
| @returns: Path to the file in container. |
| |
| """ |
| file_name = os.path.basename(file_path) |
| dest_path = os.path.join(self._PLAYMUSIC_FILE_FOLDER, file_name) |
| |
| # pipes.quote is deprecated in 2.7 (but still available). |
| # It should be replaced by shlex.quote in python 3.3. |
| arc.adb_cmd('push %s %s' % (pipes.quote(file_path), |
| pipes.quote(dest_path))) |
| |
| self._files_pushed.append(dest_path) |
| |
| return dest_path |
| |
| |
| def start_playback(self, dest_path): |
| """Starts Play Music app to play an audio file. |
| |
| @param dest_path: The file path in container. |
| |
| @raises ArcPlayMusicResourceException: Play Music app is not ready or |
| playback file is not set yet. |
| |
| """ |
| if not tag_exists(arc_resource_common.PlayMusicProps.READY_TAG_FILE): |
| raise ArcPlayMusicResourceException( |
| 'Play Music app is not ready yet.') |
| |
| if dest_path not in self._files_pushed: |
| raise ArcPlayMusicResourceException( |
| 'Playback file is not set yet') |
| |
| # In case the permissions are cleared, set the permission again before |
| # each start of the app. |
| self._set_permission() |
| self._start_app(dest_path) |
| |
| |
| def _set_permission(self): |
| """Grants permissions to Play Music app.""" |
| for permission in self._PLAYMUSIC_PERMISSIONS: |
| arc.adb_shell('pm grant %s android.permission.%s' % ( |
| pipes.quote(self._PLAYMUSIC_PACKAGE), |
| pipes.quote(permission))) |
| |
| |
| def _start_app(self, dest_path): |
| """Starts Play Music app playing an audio file. |
| |
| @param dest_path: Path to the file to play in container. |
| |
| """ |
| ext = os.path.splitext(dest_path)[1] |
| command = ('am start -a android.intent.action.VIEW' |
| ' -d "file://%s" -t "audio/%s"' |
| ' -n "%s/%s"'% ( |
| pipes.quote(dest_path), pipes.quote(ext), |
| pipes.quote(self._PLAYMUSIC_PACKAGE), |
| pipes.quote(self._PLAYMUSIC_ACTIVITY))) |
| logging.debug(command) |
| arc.adb_shell(command) |
| |
| |
| def stop_playback(self): |
| """Stops Play Music app. |
| |
| Stops the Play Music app by media key event. |
| |
| """ |
| arc.send_keycode(self._KEYCODE_MEDIA_STOP) |
| |
| |
| def cleanup(self): |
| """Removes the files to play in container.""" |
| for path in self._files_pushed: |
| arc.adb_shell('rm %s' % pipes.quote(path)) |
| self._files_pushed = [] |
| |
| |
| class ArcPlayVideoResourceException(Exception): |
| """Exceptions in ArcPlayVideoResource.""" |
| pass |
| |
| |
| class ArcPlayVideoResource(object): |
| """Class to manage Play Video app in container.""" |
| _PLAYVIDEO_PACKAGE = 'org.chromium.arc.testapp.video' |
| _PLAYVIDEO_ACTIVITY = 'org.chromium.arc.testapp.video/.MainActivity' |
| _PLAYVIDEO_EXIT_TAG = "/mnt/sdcard/ArcVideoTest.tag" |
| _PLAYVIDEO_FILE_FOLDER = '/storage/emulated/0/' |
| _PLAYVIDEO_PERMISSIONS = ['WRITE_EXTERNAL_STORAGE', 'READ_EXTERNAL_STORAGE'] |
| _KEYCODE_MEDIA_PLAY = 126 |
| _KEYCODE_MEDIA_PAUSE = 127 |
| _KEYCODE_MEDIA_STOP = 86 |
| |
| def __init__(self): |
| """Initializes an ArcPlayVideoResource.""" |
| self._files_pushed = [] |
| |
| |
| def prepare_playback(self, file_path, fullscreen=True): |
| """Copies file into the container and starts the video player app. |
| |
| @param file_path: Path to the file to play on Cros host. |
| @param fullscreen: Plays the video in fullscreen. |
| |
| """ |
| if not tag_exists(arc_resource_common.PlayVideoProps.READY_TAG_FILE): |
| raise ArcPlayVideoResourceException( |
| 'Play Video app is not ready yet.') |
| file_name = os.path.basename(file_path) |
| dest_path = os.path.join(self._PLAYVIDEO_FILE_FOLDER, file_name) |
| |
| # pipes.quote is deprecated in 2.7 (but still available). |
| # It should be replaced by shlex.quote in python 3.3. |
| arc.adb_cmd('push %s %s' % (pipes.quote(file_path), |
| pipes.quote(dest_path))) |
| |
| # In case the permissions are cleared, set the permission again before |
| # each start of the app. |
| self._set_permission() |
| self._start_app(dest_path) |
| if fullscreen: |
| self.set_fullscreen() |
| |
| |
| def set_fullscreen(self): |
| """Sends F4 keyevent to set fullscreen.""" |
| with input_playback.InputPlayback() as input_player: |
| input_player.emulate(input_type='keyboard') |
| input_player.find_connected_inputs() |
| input_player.blocking_playback_of_default_file( |
| input_type='keyboard', filename='keyboard_f4') |
| |
| |
| def start_playback(self, blocking_secs=None): |
| """Starts Play Video app to play a video file. |
| |
| @param blocking_secs: A positive number indicates the timeout to wait |
| for the playback is finished. Set None to make |
| it non-blocking. |
| |
| @raises ArcPlayVideoResourceException: Play Video app is not ready or |
| playback file is not set yet. |
| |
| """ |
| arc.send_keycode(self._KEYCODE_MEDIA_PLAY) |
| |
| if blocking_secs: |
| tag = lambda : arc.check_android_file_exists( |
| self._PLAYVIDEO_EXIT_TAG) |
| exception = error.TestFail('video playback timeout') |
| utils.poll_for_condition(tag, exception, blocking_secs) |
| |
| |
| def _set_permission(self): |
| """Grants permissions to Play Video app.""" |
| arc.grant_permissions( |
| self._PLAYVIDEO_PACKAGE, self._PLAYVIDEO_PERMISSIONS) |
| |
| |
| def _start_app(self, dest_path): |
| """Starts Play Video app playing a video file. |
| |
| @param dest_path: Path to the file to play in container. |
| |
| """ |
| arc.adb_shell('am start --activity-clear-top ' |
| '--es PATH {} {}'.format( |
| pipes.quote(dest_path), self._PLAYVIDEO_ACTIVITY)) |
| |
| |
| def pause_playback(self): |
| """Pauses Play Video app. |
| |
| Pauses the Play Video app by media key event. |
| |
| """ |
| arc.send_keycode(self._KEYCODE_MEDIA_PAUSE) |
| |
| |
| def stop_playback(self): |
| """Stops Play Video app. |
| |
| Stops the Play Video app by media key event. |
| |
| """ |
| arc.send_keycode(self._KEYCODE_MEDIA_STOP) |
| |
| |
| def cleanup(self): |
| """Removes the files to play in container.""" |
| for path in self._files_pushed: |
| arc.adb_shell('rm %s' % pipes.quote(path)) |
| self._files_pushed = [] |
| |
| |
| class ArcResource(object): |
| """Class to manage multimedia resource in container. |
| |
| @properties: |
| microphone: The instance of ArcMicrophoneResource for microphone app. |
| play_music: The instance of ArcPlayMusicResource for music app. |
| play_video: The instance of ArcPlayVideoResource for video app. |
| |
| """ |
| def __init__(self): |
| self.microphone = ArcMicrophoneResource() |
| self.play_music = ArcPlayMusicResource() |
| self.play_video = ArcPlayVideoResource() |
| |
| |
| def cleanup(self): |
| """Clean up the resources.""" |
| self.play_music.cleanup() |
| self.play_video.cleanup() |