| OLD | NEW |
| 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 """Provides an interface to start and stop Android emulator. | 5 """Provides an interface to start and stop Android emulator. |
| 6 | 6 |
| 7 Assumes system environment ANDROID_NDK_ROOT has been set. | 7 Assumes system environment ANDROID_NDK_ROOT has been set. |
| 8 | 8 |
| 9 Emulator: The class provides the methods to launch/shutdown the emulator with | 9 Emulator: The class provides the methods to launch/shutdown the emulator with |
| 10 the android virtual device named 'avd_armeabi' . | 10 the android virtual device named 'avd_armeabi' . |
| 11 """ | 11 """ |
| 12 | 12 |
| 13 import logging | 13 import logging |
| 14 import os | 14 import os |
| 15 import shutil |
| 15 import signal | 16 import signal |
| 16 import subprocess | 17 import subprocess |
| 18 import sys |
| 17 import time | 19 import time |
| 18 | 20 |
| 21 import time_profile |
| 19 # TODO(craigdh): Move these pylib dependencies to pylib/utils/. | 22 # TODO(craigdh): Move these pylib dependencies to pylib/utils/. |
| 20 from pylib import android_commands | 23 from pylib import android_commands |
| 21 from pylib import cmd_helper | 24 from pylib import cmd_helper |
| 22 from pylib import constants | 25 from pylib import constants |
| 23 from pylib import pexpect | 26 from pylib import pexpect |
| 24 from pylib.utils import time_profile | |
| 25 | 27 |
| 26 import errors | 28 import errors |
| 27 import run_command | 29 import run_command |
| 28 | 30 |
| 29 # SD card size | 31 # SD card size |
| 30 SDCARD_SIZE = '512M' | 32 SDCARD_SIZE = '512M' |
| 31 | 33 |
| 32 # Template used to generate config.ini files for the emulator | 34 # Template used to generate config.ini files for the emulator |
| 33 CONFIG_TEMPLATE = """avd.ini.encoding=ISO-8859-1 | 35 CONFIG_TEMPLATE = """avd.ini.encoding=ISO-8859-1 |
| 34 hw.dPad=no | 36 hw.dPad=no |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 89 There are odd 'sticky' cases where there can be no emulator process | 91 There are odd 'sticky' cases where there can be no emulator process |
| 90 running but a device slot is taken. A little bot trouble and and | 92 running but a device slot is taken. A little bot trouble and and |
| 91 we're out of room forever. | 93 we're out of room forever. |
| 92 """ | 94 """ |
| 93 emulators = android_commands.GetAttachedDevices(hardware=False) | 95 emulators = android_commands.GetAttachedDevices(hardware=False) |
| 94 if not emulators: | 96 if not emulators: |
| 95 return | 97 return |
| 96 for emu_name in emulators: | 98 for emu_name in emulators: |
| 97 cmd_helper.RunCmd(['adb', '-s', emu_name, 'emu', 'kill']) | 99 cmd_helper.RunCmd(['adb', '-s', emu_name, 'emu', 'kill']) |
| 98 logging.info('Emulator killing is async; give a few seconds for all to die.') | 100 logging.info('Emulator killing is async; give a few seconds for all to die.') |
| 99 for _ in range(5): | 101 for i in range(5): |
| 100 if not android_commands.GetAttachedDevices(hardware=False): | 102 if not android_commands.GetAttachedDevices(hardware=False): |
| 101 return | 103 return |
| 102 time.sleep(1) | 104 time.sleep(1) |
| 103 | 105 |
| 104 | 106 |
| 105 def DeleteAllTempAVDs(): | 107 def DeleteAllTempAVDs(): |
| 106 """Delete all temporary AVDs which are created for tests. | 108 """Delete all temporary AVDs which are created for tests. |
| 107 | 109 |
| 108 If the test exits abnormally and some temporary AVDs created when testing may | 110 If the test exits abnormally and some temporary AVDs created when testing may |
| 109 be left in the system. Clean these AVDs. | 111 be left in the system. Clean these AVDs. |
| (...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 220 android_sdk_root = os.path.join(constants.EMULATOR_SDK_ROOT, 'sdk') | 222 android_sdk_root = os.path.join(constants.EMULATOR_SDK_ROOT, 'sdk') |
| 221 self.emulator = os.path.join(android_sdk_root, 'tools', 'emulator') | 223 self.emulator = os.path.join(android_sdk_root, 'tools', 'emulator') |
| 222 self.android = os.path.join(android_sdk_root, 'tools', 'android') | 224 self.android = os.path.join(android_sdk_root, 'tools', 'android') |
| 223 self.popen = None | 225 self.popen = None |
| 224 self.device = None | 226 self.device = None |
| 225 self.abi = abi | 227 self.abi = abi |
| 226 self.avd_name = avd_name | 228 self.avd_name = avd_name |
| 227 self.api_level = api_level | 229 self.api_level = api_level |
| 228 self._CreateAVD() | 230 self._CreateAVD() |
| 229 | 231 |
| 230 @staticmethod | 232 def _DeviceName(self): |
| 231 def _DeviceName(): | |
| 232 """Return our device name.""" | 233 """Return our device name.""" |
| 233 port = _GetAvailablePort() | 234 port = _GetAvailablePort() |
| 234 return ('emulator-%d' % port, port) | 235 return ('emulator-%d' % port, port) |
| 235 | 236 |
| 236 def _CreateAVD(self): | 237 def _CreateAVD(self): |
| 237 """Creates an AVD with the given name. | 238 """Creates an AVD with the given name. |
| 238 | 239 |
| 239 Return avd_name. | 240 Return avd_name. |
| 240 """ | 241 """ |
| 241 | 242 |
| (...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 341 # For x86 emulator --enable-kvm will fail early, avoiding accidental | 342 # For x86 emulator --enable-kvm will fail early, avoiding accidental |
| 342 # runs in a slow mode (i.e. without hardware virtualization support). | 343 # runs in a slow mode (i.e. without hardware virtualization support). |
| 343 '--enable-kvm', | 344 '--enable-kvm', |
| 344 ]) | 345 ]) |
| 345 | 346 |
| 346 logging.info('Emulator launch command: %s', ' '.join(emulator_command)) | 347 logging.info('Emulator launch command: %s', ' '.join(emulator_command)) |
| 347 self.popen = subprocess.Popen(args=emulator_command, | 348 self.popen = subprocess.Popen(args=emulator_command, |
| 348 stderr=subprocess.STDOUT) | 349 stderr=subprocess.STDOUT) |
| 349 self._InstallKillHandler() | 350 self._InstallKillHandler() |
| 350 | 351 |
| 351 @staticmethod | 352 def _AggressiveImageCleanup(self): |
| 352 def _AggressiveImageCleanup(): | |
| 353 """Aggressive cleanup of emulator images. | 353 """Aggressive cleanup of emulator images. |
| 354 | 354 |
| 355 Experimentally it looks like our current emulator use on the bot | 355 Experimentally it looks like our current emulator use on the bot |
| 356 leaves image files around in /tmp/android-$USER. If a "random" | 356 leaves image files around in /tmp/android-$USER. If a "random" |
| 357 name gets reused, we choke with a 'File exists' error. | 357 name gets reused, we choke with a 'File exists' error. |
| 358 TODO(jrg): is there a less hacky way to accomplish the same goal? | 358 TODO(jrg): is there a less hacky way to accomplish the same goal? |
| 359 """ | 359 """ |
| 360 logging.info('Aggressive Image Cleanup') | 360 logging.info('Aggressive Image Cleanup') |
| 361 emulator_imagedir = '/tmp/android-%s' % os.environ['USER'] | 361 emulator_imagedir = '/tmp/android-%s' % os.environ['USER'] |
| 362 if not os.path.exists(emulator_imagedir): | 362 if not os.path.exists(emulator_imagedir): |
| (...skipping 16 matching lines...) Expand all Loading... |
| 379 number_of_waits = 2 # Make sure we can wfd twice | 379 number_of_waits = 2 # Make sure we can wfd twice |
| 380 adb_cmd = "adb -s %s %s" % (self.device, 'wait-for-device') | 380 adb_cmd = "adb -s %s %s" % (self.device, 'wait-for-device') |
| 381 while seconds_waited < self._LAUNCH_TIMEOUT: | 381 while seconds_waited < self._LAUNCH_TIMEOUT: |
| 382 try: | 382 try: |
| 383 run_command.RunCommand(adb_cmd, | 383 run_command.RunCommand(adb_cmd, |
| 384 timeout_time=self._WAITFORDEVICE_TIMEOUT, | 384 timeout_time=self._WAITFORDEVICE_TIMEOUT, |
| 385 retry_count=1) | 385 retry_count=1) |
| 386 number_of_waits -= 1 | 386 number_of_waits -= 1 |
| 387 if not number_of_waits: | 387 if not number_of_waits: |
| 388 break | 388 break |
| 389 except errors.WaitForResponseTimedOutError: | 389 except errors.WaitForResponseTimedOutError as e: |
| 390 seconds_waited += self._WAITFORDEVICE_TIMEOUT | 390 seconds_waited += self._WAITFORDEVICE_TIMEOUT |
| 391 adb_cmd = "adb -s %s %s" % (self.device, 'kill-server') | 391 adb_cmd = "adb -s %s %s" % (self.device, 'kill-server') |
| 392 run_command.RunCommand(adb_cmd) | 392 run_command.RunCommand(adb_cmd) |
| 393 self.popen.poll() | 393 self.popen.poll() |
| 394 if self.popen.returncode != None: | 394 if self.popen.returncode != None: |
| 395 raise EmulatorLaunchException('EMULATOR DIED') | 395 raise EmulatorLaunchException('EMULATOR DIED') |
| 396 if seconds_waited >= self._LAUNCH_TIMEOUT: | 396 if seconds_waited >= self._LAUNCH_TIMEOUT: |
| 397 raise EmulatorLaunchException('TIMEOUT with wait-for-device') | 397 raise EmulatorLaunchException('TIMEOUT with wait-for-device') |
| 398 logging.info('Seconds waited on wait-for-device: %d', seconds_waited) | 398 logging.info('Seconds waited on wait-for-device: %d', seconds_waited) |
| 399 if wait_for_boot: | 399 if wait_for_boot: |
| 400 # Now that we checked for obvious problems, wait for a boot complete. | 400 # Now that we checked for obvious problems, wait for a boot complete. |
| 401 # Waiting for the package manager is sometimes problematic. | 401 # Waiting for the package manager is sometimes problematic. |
| 402 a = android_commands.AndroidCommands(self.device) | 402 a = android_commands.AndroidCommands(self.device) |
| 403 a.WaitForSystemBootCompleted(self._WAITFORBOOT_TIMEOUT) | 403 a.WaitForSystemBootCompleted(self._WAITFORBOOT_TIMEOUT) |
| 404 | 404 |
| 405 def Shutdown(self): | 405 def Shutdown(self): |
| 406 """Shuts down the process started by launch.""" | 406 """Shuts down the process started by launch.""" |
| 407 self._DeleteAVD() | 407 self._DeleteAVD() |
| 408 if self.popen: | 408 if self.popen: |
| 409 self.popen.poll() | 409 self.popen.poll() |
| 410 if self.popen.returncode == None: | 410 if self.popen.returncode == None: |
| 411 self.popen.kill() | 411 self.popen.kill() |
| 412 self.popen = None | 412 self.popen = None |
| 413 | 413 |
| 414 def _ShutdownOnSignal(self, _signum, _frame): | 414 def _ShutdownOnSignal(self, signum, frame): |
| 415 logging.critical('emulator _ShutdownOnSignal') | 415 logging.critical('emulator _ShutdownOnSignal') |
| 416 for sig in self._SIGNALS: | 416 for sig in self._SIGNALS: |
| 417 signal.signal(sig, signal.SIG_DFL) | 417 signal.signal(sig, signal.SIG_DFL) |
| 418 self.Shutdown() | 418 self.Shutdown() |
| 419 raise KeyboardInterrupt # print a stack | 419 raise KeyboardInterrupt # print a stack |
| 420 | 420 |
| 421 def _InstallKillHandler(self): | 421 def _InstallKillHandler(self): |
| 422 """Install a handler to kill the emulator when we exit unexpectedly.""" | 422 """Install a handler to kill the emulator when we exit unexpectedly.""" |
| 423 for sig in self._SIGNALS: | 423 for sig in self._SIGNALS: |
| 424 signal.signal(sig, self._ShutdownOnSignal) | 424 signal.signal(sig, self._ShutdownOnSignal) |
| OLD | NEW |