1 # SPDX-License-Identifier: Apache-2.0 2 # 3 # Copyright (C) 2017, Arm Limited and contributors. 4 # 5 # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 # not use this file except in compliance with the License. 7 # You may obtain a copy of 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. 14 # See the License for the specific language governing permissions and 15 # limitations under the License. 16 # 17 18 import re 19 import os 20 import logging 21 22 from subprocess import Popen, PIPE 23 24 from time import sleep 25 26 from android import Screen, System, Workload 27 from devlib.utils.android import grant_app_permissions 28 29 # Regexps for benchmark synchronization 30 31 REGEXPS = { 32 'start' : '.*Displayed com.google.android.exoplayer2.demo/.PlayerActivity', 33 'duration' : '.*period \[(?P<duration>[0-9]+.*)\]', 34 'end' : '.*state \[.+, .+, E\]' 35 } 36 37 class ExoPlayer(Workload): 38 """ 39 Android ExoPlayer workload 40 41 Exoplayer sources: https://github.com/google/ExoPlayer 42 43 The 'demo' application is used by this workload. 44 It can easily be built by loading the ExoPlayer sources 45 into Android Studio 46 47 Expected apk is 'demo-noExtensions-debug.apk' 48 49 Version r2.4.0 (d979469) is known to work 50 """ 51 52 # Package required by this workload 53 package = 'com.google.android.exoplayer2.demo' 54 action = 'com.google.android.exoplayer.demo.action.VIEW' 55 56 def __init__(self, test_env): 57 super(ExoPlayer, self).__init__(test_env) 58 self._log = logging.getLogger('ExoPlayer') 59 60 def _play(self): 61 62 # Grant app all permissions 63 grant_app_permissions(self._target, self.package) 64 65 # Handle media file location 66 if not self.from_device: 67 remote_file = self._target.path.join( 68 self._target.working_directory, 69 os.path.basename(self.media_file) 70 ) 71 72 self._log.info('Pushing media file to device...') 73 self._target.push( 74 self.media_file, 75 remote_file, 76 timeout = 60 77 ) 78 self._log.info('Media file transfer complete') 79 else: 80 remote_file = self.media_file 81 82 # Prepare logcat monitor 83 monitor = self._target.get_logcat_monitor(REGEXPS.values()) 84 monitor.start() 85 86 # Play media file 87 play_cmd = 'am start -a "{}" -d "file://{}"'\ 88 .format(self.action, remote_file) 89 self._log.info(play_cmd) 90 self._target.execute(play_cmd) 91 92 monitor.wait_for(REGEXPS['start']) 93 self.tracingStart() 94 self._log.info('Playing media file') 95 96 line = monitor.wait_for(REGEXPS['duration'])[0] 97 media_duration_s = int(round(float(re.search(REGEXPS['duration'], line) 98 .group('duration')))) 99 100 self._log.info('Media duration is {}'.format(media_duration_s)) 101 102 if self.play_duration_s and self.play_duration_s < media_duration_s: 103 self._log.info('Waiting {} seconds before ending playback' 104 .format(self.play_duration_s)) 105 sleep(self.play_duration_s) 106 else: 107 self._log.info('Waiting for playback completion ({} seconds)' 108 .format(media_duration_s)) 109 monitor.wait_for(REGEXPS['end'], timeout = media_duration_s + 30) 110 111 self.tracingStop() 112 monitor.stop() 113 self._log.info('Media file playback completed') 114 115 # Remove file if it was pushed 116 if not self.from_device: 117 self._target.remove(remote_file) 118 119 def run(self, out_dir, collect, media_file, from_device=False, play_duration_s=None): 120 """ 121 Run Exoplayer workload 122 123 :param out_dir: Path to experiment directory on the host 124 where to store results. 125 :type out_dir: str 126 127 :param collect: Specifies what to collect. Possible values: 128 - 'energy' 129 - 'systrace' 130 - 'ftrace' 131 - any combination of the above as a single space-separated string. 132 :type collect: list(str) 133 134 :param media_file: Filepath of the media to play 135 Path on device if 'from_device' is True 136 Path on host if 'from_device' is False (default) 137 :type media_file: str 138 139 :param from_device: Whether file to play is already on the device 140 :type from_device: bool 141 142 :param play_duration_s: If set, maximum duration (seconds) of the media playback 143 If not set, media will play to completion 144 :type play_duration_s: int 145 """ 146 147 # Keep track of mandatory parameters 148 self.out_dir = out_dir 149 self.collect = collect 150 self.media_file = media_file 151 self.from_device = from_device 152 self.play_duration_s = play_duration_s 153 154 # Check media file exists 155 if from_device and not self._target.file_exists(self.media_file): 156 raise RuntimeError('Cannot find "{}" on target'.format(self.media_file)) 157 elif not from_device and not os.path.isfile(self.media_file): 158 raise RuntimeError('Cannot find "{}" on host'.format(self.media_file)) 159 160 # Unlock device screen (assume no password required) 161 Screen.unlock(self._target) 162 163 # Close and clear application 164 System.force_stop(self._target, self.package, clear=True) 165 166 # Enable airplane mode 167 System.set_airplane_mode(self._target, on=True) 168 169 # Set min brightness 170 Screen.set_brightness(self._target, auto=False, percent=0) 171 172 # Force screen in PORTRAIT mode 173 Screen.set_orientation(self._target, portrait=True) 174 175 # Launch Exoplayer benchmark 176 self._play() 177 178 # Go back to home screen 179 System.home(self._target) 180 181 # Set orientation back to auto 182 Screen.set_orientation(self._target, auto=True) 183 184 # Set brightness back to auto 185 Screen.set_brightness(self._target, auto=True) 186 187 # Turn off airplane mode 188 System.set_airplane_mode(self._target, on=False) 189 190 # Close and clear application 191 System.force_stop(self._target, self.package, clear=True) 192 193 # vim :set tabstop=4 shiftwidth=4 expandtab 194