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