OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # | 2 # |
3 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 4 # Use of this source code is governed by a BSD-style license that can be |
5 # found in the LICENSE file. | 5 # found in the LICENSE file. |
6 | 6 |
7 """Provides an interface to start and stop Android emulator. | 7 """Provides an interface to start and stop Android emulator. |
8 | 8 |
9 Assumes system environment ANDROID_NDK_ROOT has been set. | 9 Assumes system environment ANDROID_NDK_ROOT has been set. |
10 | 10 |
11 Emulator: The class provides the methods to launch/shutdown the emulator with | 11 Emulator: The class provides the methods to launch/shutdown the emulator with |
12 the android virtual device named 'avd_armeabi' . | 12 the android virtual device named 'avd_armeabi' . |
13 """ | 13 """ |
14 | 14 |
15 import logging | 15 import logging |
16 import os | 16 import os |
| 17 import shutil |
17 import signal | 18 import signal |
18 import subprocess | 19 import subprocess |
19 import sys | 20 import sys |
20 import time | 21 import time |
21 | 22 |
22 import time_profile | 23 import time_profile |
23 # TODO(craigdh): Move these pylib dependencies to pylib/utils/. | 24 # TODO(craigdh): Move these pylib dependencies to pylib/utils/. |
24 from pylib import android_commands | 25 from pylib import android_commands |
25 from pylib import cmd_helper | 26 from pylib import cmd_helper |
26 from pylib import constants | 27 from pylib import constants |
| 28 from pylib import pexpect |
27 | 29 |
28 import errors | 30 import errors |
29 import run_command | 31 import run_command |
30 | 32 |
31 # Android API level | 33 # Android API level |
32 API_TARGET = 'android-%s' % constants.ANDROID_SDK_VERSION | 34 API_TARGET = 'android-%s' % constants.ANDROID_SDK_VERSION |
33 | 35 |
34 | 36 |
35 class EmulatorLaunchException(Exception): | 37 class EmulatorLaunchException(Exception): |
36 """Emulator failed to launch.""" | 38 """Emulator failed to launch.""" |
37 pass | 39 pass |
38 | 40 |
39 def _KillAllEmulators(): | 41 def _KillAllEmulators(): |
40 """Kill all running emulators that look like ones we started. | 42 """Kill all running emulators that look like ones we started. |
41 | 43 |
42 There are odd 'sticky' cases where there can be no emulator process | 44 There are odd 'sticky' cases where there can be no emulator process |
43 running but a device slot is taken. A little bot trouble and and | 45 running but a device slot is taken. A little bot trouble and and |
44 we're out of room forever. | 46 we're out of room forever. |
45 """ | 47 """ |
46 emulators = android_commands.GetEmulators() | 48 emulators = android_commands.GetEmulators() |
47 if not emulators: | 49 if not emulators: |
48 return | 50 return |
49 for emu_name in emulators: | 51 for emu_name in emulators: |
50 cmd_helper.GetCmdOutput(['adb', '-s', emu_name, 'emu', 'kill']) | 52 cmd_helper.RunCmd(['adb', '-s', emu_name, 'emu', 'kill']) |
51 logging.info('Emulator killing is async; give a few seconds for all to die.') | 53 logging.info('Emulator killing is async; give a few seconds for all to die.') |
52 for i in range(5): | 54 for i in range(5): |
53 if not android_commands.GetEmulators(): | 55 if not android_commands.GetEmulators(): |
54 return | 56 return |
55 time.sleep(1) | 57 time.sleep(1) |
56 | 58 |
57 | 59 |
58 def DeleteAllTempAVDs(): | 60 def DeleteAllTempAVDs(): |
59 """Delete all temporary AVDs which are created for tests. | 61 """Delete all temporary AVDs which are created for tests. |
60 | 62 |
61 If the test exits abnormally and some temporary AVDs created when testing may | 63 If the test exits abnormally and some temporary AVDs created when testing may |
62 be left in the system. Clean these AVDs. | 64 be left in the system. Clean these AVDs. |
63 """ | 65 """ |
64 avds = android_commands.GetAVDs() | 66 avds = android_commands.GetAVDs() |
65 if not avds: | 67 if not avds: |
66 return | 68 return |
67 for avd_name in avds: | 69 for avd_name in avds: |
68 if 'run_tests_avd' in avd_name: | 70 if 'run_tests_avd' in avd_name: |
69 cmd = ['android', '-s', 'delete', 'avd', '--name', avd_name] | 71 cmd = ['android', '-s', 'delete', 'avd', '--name', avd_name] |
70 cmd_helper.GetCmdOutput(cmd) | 72 cmd_helper.RunCmd(cmd) |
71 logging.info('Delete AVD %s' % avd_name) | 73 logging.info('Delete AVD %s' % avd_name) |
72 | 74 |
73 | 75 |
74 class PortPool(object): | 76 class PortPool(object): |
75 """Pool for emulator port starting position that changes over time.""" | 77 """Pool for emulator port starting position that changes over time.""" |
76 _port_min = 5554 | 78 _port_min = 5554 |
77 _port_max = 5585 | 79 _port_max = 5585 |
78 _port_current_index = 0 | 80 _port_current_index = 0 |
79 | 81 |
80 @classmethod | 82 @classmethod |
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
153 # the time to launch the emulator and a wait-for-device command. | 155 # the time to launch the emulator and a wait-for-device command. |
154 _LAUNCH_TIMEOUT = 120 | 156 _LAUNCH_TIMEOUT = 120 |
155 | 157 |
156 # Timeout interval of wait-for-device command before bouncing to a a | 158 # Timeout interval of wait-for-device command before bouncing to a a |
157 # process life check. | 159 # process life check. |
158 _WAITFORDEVICE_TIMEOUT = 5 | 160 _WAITFORDEVICE_TIMEOUT = 5 |
159 | 161 |
160 # Time to wait for a "wait for boot complete" (property set on device). | 162 # Time to wait for a "wait for boot complete" (property set on device). |
161 _WAITFORBOOT_TIMEOUT = 300 | 163 _WAITFORBOOT_TIMEOUT = 300 |
162 | 164 |
163 def __init__(self, avd_name, abi='x86'): | 165 def __init__(self, avd_name, abi): |
164 """Init an Emulator. | 166 """Init an Emulator. |
165 | 167 |
166 Args: | 168 Args: |
167 avd_name: name of the AVD to create | 169 avd_name: name of the AVD to create |
168 abi: target platform for emulator being created | 170 abi: target platform for emulator being created |
169 """ | 171 """ |
170 android_sdk_root = os.path.join(constants.EMULATOR_SDK_ROOT, | 172 android_sdk_root = os.path.join(constants.EMULATOR_SDK_ROOT, |
171 'android_tools', 'sdk') | 173 'android_tools', 'sdk') |
172 self.emulator = os.path.join(android_sdk_root, 'tools', 'emulator') | 174 self.emulator = os.path.join(android_sdk_root, 'tools', 'emulator') |
173 self.android = os.path.join(android_sdk_root, 'tools', 'android') | 175 self.android = os.path.join(android_sdk_root, 'tools', 'android') |
174 self.popen = None | 176 self.popen = None |
175 self.device = None | 177 self.device = None |
176 self.abi = abi | 178 self.abi = abi |
177 self.avd_name = avd_name | 179 self.avd_name = avd_name |
178 self._CreateAVD() | 180 self._CreateAVD() |
179 | 181 |
180 def _DeviceName(self): | 182 def _DeviceName(self): |
181 """Return our device name.""" | 183 """Return our device name.""" |
182 port = _GetAvailablePort() | 184 port = _GetAvailablePort() |
183 return ('emulator-%d' % port, port) | 185 return ('emulator-%d' % port, port) |
184 | 186 |
185 def _CreateAVD(self): | 187 def _CreateAVD(self): |
186 """Creates an AVD with the given name. | 188 """Creates an AVD with the given name. |
187 | 189 |
188 Return avd_name. | 190 Return avd_name. |
189 """ | 191 """ |
| 192 |
| 193 if self.abi == 'arm': |
| 194 abi_option = 'armeabi-v7a' |
| 195 else: |
| 196 abi_option = 'x86' |
| 197 |
190 avd_command = [ | 198 avd_command = [ |
191 self.android, | 199 self.android, |
192 '--silent', | 200 '--silent', |
193 'create', 'avd', | 201 'create', 'avd', |
194 '--name', self.avd_name, | 202 '--name', self.avd_name, |
195 '--abi', self.abi, | 203 '--abi', abi_option, |
196 '--target', API_TARGET, | 204 '--target', API_TARGET, |
197 '-c', '128M', | |
198 '--force', | 205 '--force', |
199 ] | 206 ] |
200 avd_process = subprocess.Popen(args=avd_command, | 207 avd_cmd_str = ' '.join(avd_command) |
201 stdin=subprocess.PIPE, | 208 logging.info('Create AVD command: %s', avd_cmd_str) |
202 stdout=subprocess.PIPE, | 209 avd_process = pexpect.spawn(avd_cmd_str) |
203 stderr=subprocess.STDOUT) | 210 |
204 avd_process.stdin.write('no\n') | 211 # Instead of creating a custom profile, we overwrite config files. |
205 avd_process.wait() | 212 avd_process.expect('Do you wish to create a custom hardware profile') |
206 logging.info('Create AVD command: %s', ' '.join(avd_command)) | 213 avd_process.sendline('no\n') |
| 214 avd_process.expect('Created AVD \'%s\'' % self.avd_name) |
| 215 |
| 216 # Setup test device as default Galaxy Nexus AVD |
| 217 avd_config_dir = os.path.join(constants.CHROME_DIR, 'build', 'android', |
| 218 'avd_configs') |
| 219 avd_config_ini = os.path.join(avd_config_dir, |
| 220 'AVD_for_Galaxy_Nexus_by_Google_%s.avd' % |
| 221 self.abi, 'config.ini') |
| 222 |
| 223 # Replace current configuration with default Galaxy Nexus config. |
| 224 avds_dir = os.path.join(os.path.expanduser('~'), '.android', 'avd') |
| 225 ini_file = os.path.join(avds_dir, '%s.ini' % self.avd_name) |
| 226 new_config_ini = os.path.join(avds_dir, '%s.avd' % self.avd_name, |
| 227 'config.ini') |
| 228 |
| 229 # Remove config files with defaults to replace with Google's GN settings. |
| 230 os.unlink(ini_file) |
| 231 os.unlink(new_config_ini) |
| 232 |
| 233 # Create new configuration files with Galaxy Nexus by Google settings. |
| 234 with open(ini_file, 'w') as new_ini: |
| 235 new_ini.write('avd.ini.encoding=ISO-8859-1\n') |
| 236 new_ini.write('target=%s\n' % API_TARGET) |
| 237 new_ini.write('path=%s/%s.avd\n' % (avds_dir, self.avd_name)) |
| 238 new_ini.write('path.rel=avd/%s.avd\n' % self.avd_name) |
| 239 |
| 240 shutil.copy(avd_config_ini, new_config_ini) |
207 return self.avd_name | 241 return self.avd_name |
208 | 242 |
| 243 |
209 def _DeleteAVD(self): | 244 def _DeleteAVD(self): |
210 """Delete the AVD of this emulator.""" | 245 """Delete the AVD of this emulator.""" |
211 avd_command = [ | 246 avd_command = [ |
212 self.android, | 247 self.android, |
213 '--silent', | 248 '--silent', |
214 'delete', | 249 'delete', |
215 'avd', | 250 'avd', |
216 '--name', self.avd_name, | 251 '--name', self.avd_name, |
217 ] | 252 ] |
218 avd_process = subprocess.Popen(args=avd_command, | |
219 stdout=subprocess.PIPE, | |
220 stderr=subprocess.STDOUT) | |
221 logging.info('Delete AVD command: %s', ' '.join(avd_command)) | 253 logging.info('Delete AVD command: %s', ' '.join(avd_command)) |
222 avd_process.wait() | 254 cmd_helper.RunCmd(avd_command) |
| 255 |
223 | 256 |
224 def Launch(self, kill_all_emulators): | 257 def Launch(self, kill_all_emulators): |
225 """Launches the emulator asynchronously. Call ConfirmLaunch() to ensure the | 258 """Launches the emulator asynchronously. Call ConfirmLaunch() to ensure the |
226 emulator is ready for use. | 259 emulator is ready for use. |
227 | 260 |
228 If fails, an exception will be raised. | 261 If fails, an exception will be raised. |
229 """ | 262 """ |
230 if kill_all_emulators: | 263 if kill_all_emulators: |
231 _KillAllEmulators() # just to be sure | 264 _KillAllEmulators() # just to be sure |
232 self._AggressiveImageCleanup() | 265 self._AggressiveImageCleanup() |
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
326 logging.critical('emulator _ShutdownOnSignal') | 359 logging.critical('emulator _ShutdownOnSignal') |
327 for sig in self._SIGNALS: | 360 for sig in self._SIGNALS: |
328 signal.signal(sig, signal.SIG_DFL) | 361 signal.signal(sig, signal.SIG_DFL) |
329 self.Shutdown() | 362 self.Shutdown() |
330 raise KeyboardInterrupt # print a stack | 363 raise KeyboardInterrupt # print a stack |
331 | 364 |
332 def _InstallKillHandler(self): | 365 def _InstallKillHandler(self): |
333 """Install a handler to kill the emulator when we exit unexpectedly.""" | 366 """Install a handler to kill the emulator when we exit unexpectedly.""" |
334 for sig in self._SIGNALS: | 367 for sig in self._SIGNALS: |
335 signal.signal(sig, self._ShutdownOnSignal) | 368 signal.signal(sig, self._ShutdownOnSignal) |
OLD | NEW |