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