# 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()