media/webrtc/trunk/build/android/emulator.py

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rwxr-xr-x

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 #!/usr/bin/env python
michael@0 2 #
michael@0 3 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
michael@0 4 # Use of this source code is governed by a BSD-style license that can be
michael@0 5 # found in the LICENSE file.
michael@0 6
michael@0 7 """Provides an interface to start and stop Android emulator.
michael@0 8
michael@0 9 Assumes system environment ANDROID_NDK_ROOT has been set.
michael@0 10
michael@0 11 Emulator: The class provides the methods to launch/shutdown the emulator with
michael@0 12 the android virtual device named 'avd_armeabi' .
michael@0 13 """
michael@0 14
michael@0 15 import logging
michael@0 16 import os
michael@0 17 import signal
michael@0 18 import subprocess
michael@0 19 import sys
michael@0 20 import time
michael@0 21
michael@0 22 from pylib import android_commands
michael@0 23 from pylib import cmd_helper
michael@0 24
michael@0 25 # adb_interface.py is under ../../third_party/android_testrunner/
michael@0 26 sys.path.append(os.path.join(os.path.abspath(os.path.dirname(__file__)), '..',
michael@0 27 '..', 'third_party', 'android_testrunner'))
michael@0 28 import adb_interface
michael@0 29 import errors
michael@0 30 import run_command
michael@0 31
michael@0 32 class EmulatorLaunchException(Exception):
michael@0 33 """Emulator failed to launch."""
michael@0 34 pass
michael@0 35
michael@0 36 def _KillAllEmulators():
michael@0 37 """Kill all running emulators that look like ones we started.
michael@0 38
michael@0 39 There are odd 'sticky' cases where there can be no emulator process
michael@0 40 running but a device slot is taken. A little bot trouble and and
michael@0 41 we're out of room forever.
michael@0 42 """
michael@0 43 emulators = android_commands.GetEmulators()
michael@0 44 if not emulators:
michael@0 45 return
michael@0 46 for emu_name in emulators:
michael@0 47 cmd_helper.GetCmdOutput(['adb', '-s', emu_name, 'emu', 'kill'])
michael@0 48 logging.info('Emulator killing is async; give a few seconds for all to die.')
michael@0 49 for i in range(5):
michael@0 50 if not android_commands.GetEmulators():
michael@0 51 return
michael@0 52 time.sleep(1)
michael@0 53
michael@0 54
michael@0 55 def DeleteAllTempAVDs():
michael@0 56 """Delete all temporary AVDs which are created for tests.
michael@0 57
michael@0 58 If the test exits abnormally and some temporary AVDs created when testing may
michael@0 59 be left in the system. Clean these AVDs.
michael@0 60 """
michael@0 61 avds = android_commands.GetAVDs()
michael@0 62 if not avds:
michael@0 63 return
michael@0 64 for avd_name in avds:
michael@0 65 if 'run_tests_avd' in avd_name:
michael@0 66 cmd = ['android', '-s', 'delete', 'avd', '--name', avd_name]
michael@0 67 cmd_helper.GetCmdOutput(cmd)
michael@0 68 logging.info('Delete AVD %s' % avd_name)
michael@0 69
michael@0 70
michael@0 71 class PortPool(object):
michael@0 72 """Pool for emulator port starting position that changes over time."""
michael@0 73 _port_min = 5554
michael@0 74 _port_max = 5585
michael@0 75 _port_current_index = 0
michael@0 76
michael@0 77 @classmethod
michael@0 78 def port_range(cls):
michael@0 79 """Return a range of valid ports for emulator use.
michael@0 80
michael@0 81 The port must be an even number between 5554 and 5584. Sometimes
michael@0 82 a killed emulator "hangs on" to a port long enough to prevent
michael@0 83 relaunch. This is especially true on slow machines (like a bot).
michael@0 84 Cycling through a port start position helps make us resilient."""
michael@0 85 ports = range(cls._port_min, cls._port_max, 2)
michael@0 86 n = cls._port_current_index
michael@0 87 cls._port_current_index = (n + 1) % len(ports)
michael@0 88 return ports[n:] + ports[:n]
michael@0 89
michael@0 90
michael@0 91 def _GetAvailablePort():
michael@0 92 """Returns an available TCP port for the console."""
michael@0 93 used_ports = []
michael@0 94 emulators = android_commands.GetEmulators()
michael@0 95 for emulator in emulators:
michael@0 96 used_ports.append(emulator.split('-')[1])
michael@0 97 for port in PortPool.port_range():
michael@0 98 if str(port) not in used_ports:
michael@0 99 return port
michael@0 100
michael@0 101
michael@0 102 class Emulator(object):
michael@0 103 """Provides the methods to lanuch/shutdown the emulator.
michael@0 104
michael@0 105 The emulator has the android virtual device named 'avd_armeabi'.
michael@0 106
michael@0 107 The emulator could use any even TCP port between 5554 and 5584 for the
michael@0 108 console communication, and this port will be part of the device name like
michael@0 109 'emulator-5554'. Assume it is always True, as the device name is the id of
michael@0 110 emulator managed in this class.
michael@0 111
michael@0 112 Attributes:
michael@0 113 emulator: Path of Android's emulator tool.
michael@0 114 popen: Popen object of the running emulator process.
michael@0 115 device: Device name of this emulator.
michael@0 116 """
michael@0 117
michael@0 118 # Signals we listen for to kill the emulator on
michael@0 119 _SIGNALS = (signal.SIGINT, signal.SIGHUP)
michael@0 120
michael@0 121 # Time to wait for an emulator launch, in seconds. This includes
michael@0 122 # the time to launch the emulator and a wait-for-device command.
michael@0 123 _LAUNCH_TIMEOUT = 120
michael@0 124
michael@0 125 # Timeout interval of wait-for-device command before bouncing to a a
michael@0 126 # process life check.
michael@0 127 _WAITFORDEVICE_TIMEOUT = 5
michael@0 128
michael@0 129 # Time to wait for a "wait for boot complete" (property set on device).
michael@0 130 _WAITFORBOOT_TIMEOUT = 300
michael@0 131
michael@0 132 def __init__(self, new_avd_name, fast_and_loose):
michael@0 133 """Init an Emulator.
michael@0 134
michael@0 135 Args:
michael@0 136 nwe_avd_name: If set, will create a new temporary AVD.
michael@0 137 fast_and_loose: Loosen up the rules for reliable running for speed.
michael@0 138 Intended for quick testing or re-testing.
michael@0 139
michael@0 140 """
michael@0 141 try:
michael@0 142 android_sdk_root = os.environ['ANDROID_SDK_ROOT']
michael@0 143 except KeyError:
michael@0 144 logging.critical('The ANDROID_SDK_ROOT must be set to run the test on '
michael@0 145 'emulator.')
michael@0 146 raise
michael@0 147 self.emulator = os.path.join(android_sdk_root, 'tools', 'emulator')
michael@0 148 self.android = os.path.join(android_sdk_root, 'tools', 'android')
michael@0 149 self.popen = None
michael@0 150 self.device = None
michael@0 151 self.default_avd = True
michael@0 152 self.fast_and_loose = fast_and_loose
michael@0 153 self.abi = 'armeabi-v7a'
michael@0 154 self.avd = 'avd_armeabi'
michael@0 155 if 'x86' in os.environ.get('TARGET_PRODUCT', ''):
michael@0 156 self.abi = 'x86'
michael@0 157 self.avd = 'avd_x86'
michael@0 158 if new_avd_name:
michael@0 159 self.default_avd = False
michael@0 160 self.avd = self._CreateAVD(new_avd_name)
michael@0 161
michael@0 162 def _DeviceName(self):
michael@0 163 """Return our device name."""
michael@0 164 port = _GetAvailablePort()
michael@0 165 return ('emulator-%d' % port, port)
michael@0 166
michael@0 167 def _CreateAVD(self, avd_name):
michael@0 168 """Creates an AVD with the given name.
michael@0 169
michael@0 170 Return avd_name.
michael@0 171 """
michael@0 172 avd_command = [
michael@0 173 self.android,
michael@0 174 '--silent',
michael@0 175 'create', 'avd',
michael@0 176 '--name', avd_name,
michael@0 177 '--abi', self.abi,
michael@0 178 '--target', 'android-16',
michael@0 179 '-c', '128M',
michael@0 180 '--force',
michael@0 181 ]
michael@0 182 avd_process = subprocess.Popen(args=avd_command,
michael@0 183 stdin=subprocess.PIPE,
michael@0 184 stdout=subprocess.PIPE,
michael@0 185 stderr=subprocess.STDOUT)
michael@0 186 avd_process.stdin.write('no\n')
michael@0 187 avd_process.wait()
michael@0 188 logging.info('Create AVD command: %s', ' '.join(avd_command))
michael@0 189 return avd_name
michael@0 190
michael@0 191 def _DeleteAVD(self):
michael@0 192 """Delete the AVD of this emulator."""
michael@0 193 avd_command = [
michael@0 194 self.android,
michael@0 195 '--silent',
michael@0 196 'delete',
michael@0 197 'avd',
michael@0 198 '--name', self.avd,
michael@0 199 ]
michael@0 200 avd_process = subprocess.Popen(args=avd_command,
michael@0 201 stdout=subprocess.PIPE,
michael@0 202 stderr=subprocess.STDOUT)
michael@0 203 logging.info('Delete AVD command: %s', ' '.join(avd_command))
michael@0 204 avd_process.wait()
michael@0 205
michael@0 206 def Launch(self, kill_all_emulators):
michael@0 207 """Launches the emulator asynchronously. Call ConfirmLaunch() to ensure the
michael@0 208 emulator is ready for use.
michael@0 209
michael@0 210 If fails, an exception will be raised.
michael@0 211 """
michael@0 212 if kill_all_emulators:
michael@0 213 _KillAllEmulators() # just to be sure
michael@0 214 if not self.fast_and_loose:
michael@0 215 self._AggressiveImageCleanup()
michael@0 216 (self.device, port) = self._DeviceName()
michael@0 217 emulator_command = [
michael@0 218 self.emulator,
michael@0 219 # Speed up emulator launch by 40%. Really.
michael@0 220 '-no-boot-anim',
michael@0 221 # The default /data size is 64M.
michael@0 222 # That's not enough for 8 unit test bundles and their data.
michael@0 223 '-partition-size', '512',
michael@0 224 # Enable GPU by default.
michael@0 225 '-gpu', 'on',
michael@0 226 # Use a familiar name and port.
michael@0 227 '-avd', self.avd,
michael@0 228 '-port', str(port)]
michael@0 229 if not self.fast_and_loose:
michael@0 230 emulator_command.extend([
michael@0 231 # Wipe the data. We've seen cases where an emulator
michael@0 232 # gets 'stuck' if we don't do this (every thousand runs or
michael@0 233 # so).
michael@0 234 '-wipe-data',
michael@0 235 ])
michael@0 236 logging.info('Emulator launch command: %s', ' '.join(emulator_command))
michael@0 237 self.popen = subprocess.Popen(args=emulator_command,
michael@0 238 stderr=subprocess.STDOUT)
michael@0 239 self._InstallKillHandler()
michael@0 240
michael@0 241 def _AggressiveImageCleanup(self):
michael@0 242 """Aggressive cleanup of emulator images.
michael@0 243
michael@0 244 Experimentally it looks like our current emulator use on the bot
michael@0 245 leaves image files around in /tmp/android-$USER. If a "random"
michael@0 246 name gets reused, we choke with a 'File exists' error.
michael@0 247 TODO(jrg): is there a less hacky way to accomplish the same goal?
michael@0 248 """
michael@0 249 logging.info('Aggressive Image Cleanup')
michael@0 250 emulator_imagedir = '/tmp/android-%s' % os.environ['USER']
michael@0 251 if not os.path.exists(emulator_imagedir):
michael@0 252 return
michael@0 253 for image in os.listdir(emulator_imagedir):
michael@0 254 full_name = os.path.join(emulator_imagedir, image)
michael@0 255 if 'emulator' in full_name:
michael@0 256 logging.info('Deleting emulator image %s', full_name)
michael@0 257 os.unlink(full_name)
michael@0 258
michael@0 259 def ConfirmLaunch(self, wait_for_boot=False):
michael@0 260 """Confirm the emulator launched properly.
michael@0 261
michael@0 262 Loop on a wait-for-device with a very small timeout. On each
michael@0 263 timeout, check the emulator process is still alive.
michael@0 264 After confirming a wait-for-device can be successful, make sure
michael@0 265 it returns the right answer.
michael@0 266 """
michael@0 267 seconds_waited = 0
michael@0 268 number_of_waits = 2 # Make sure we can wfd twice
michael@0 269 adb_cmd = "adb -s %s %s" % (self.device, 'wait-for-device')
michael@0 270 while seconds_waited < self._LAUNCH_TIMEOUT:
michael@0 271 try:
michael@0 272 run_command.RunCommand(adb_cmd,
michael@0 273 timeout_time=self._WAITFORDEVICE_TIMEOUT,
michael@0 274 retry_count=1)
michael@0 275 number_of_waits -= 1
michael@0 276 if not number_of_waits:
michael@0 277 break
michael@0 278 except errors.WaitForResponseTimedOutError as e:
michael@0 279 seconds_waited += self._WAITFORDEVICE_TIMEOUT
michael@0 280 adb_cmd = "adb -s %s %s" % (self.device, 'kill-server')
michael@0 281 run_command.RunCommand(adb_cmd)
michael@0 282 self.popen.poll()
michael@0 283 if self.popen.returncode != None:
michael@0 284 raise EmulatorLaunchException('EMULATOR DIED')
michael@0 285 if seconds_waited >= self._LAUNCH_TIMEOUT:
michael@0 286 raise EmulatorLaunchException('TIMEOUT with wait-for-device')
michael@0 287 logging.info('Seconds waited on wait-for-device: %d', seconds_waited)
michael@0 288 if wait_for_boot:
michael@0 289 # Now that we checked for obvious problems, wait for a boot complete.
michael@0 290 # Waiting for the package manager is sometimes problematic.
michael@0 291 a = android_commands.AndroidCommands(self.device)
michael@0 292 a.WaitForSystemBootCompleted(self._WAITFORBOOT_TIMEOUT)
michael@0 293
michael@0 294 def Shutdown(self):
michael@0 295 """Shuts down the process started by launch."""
michael@0 296 if not self.default_avd:
michael@0 297 self._DeleteAVD()
michael@0 298 if self.popen:
michael@0 299 self.popen.poll()
michael@0 300 if self.popen.returncode == None:
michael@0 301 self.popen.kill()
michael@0 302 self.popen = None
michael@0 303
michael@0 304 def _ShutdownOnSignal(self, signum, frame):
michael@0 305 logging.critical('emulator _ShutdownOnSignal')
michael@0 306 for sig in self._SIGNALS:
michael@0 307 signal.signal(sig, signal.SIG_DFL)
michael@0 308 self.Shutdown()
michael@0 309 raise KeyboardInterrupt # print a stack
michael@0 310
michael@0 311 def _InstallKillHandler(self):
michael@0 312 """Install a handler to kill the emulator when we exit unexpectedly."""
michael@0 313 for sig in self._SIGNALS:
michael@0 314 signal.signal(sig, self._ShutdownOnSignal)
michael@0 315
michael@0 316 def main(argv):
michael@0 317 Emulator(None, True).Launch(True)
michael@0 318
michael@0 319
michael@0 320 if __name__ == '__main__':
michael@0 321 main(sys.argv)

mercurial