| 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 | |
| 16 import signal | 15 import signal |
| 17 import subprocess | 16 import subprocess |
| 18 import sys | |
| 19 import time | 17 import time |
| 20 | 18 |
| 21 import time_profile | |
| 22 # TODO(craigdh): Move these pylib dependencies to pylib/utils/. | 19 # TODO(craigdh): Move these pylib dependencies to pylib/utils/. |
| 23 from pylib import android_commands | 20 from pylib import android_commands |
| 24 from pylib import cmd_helper | 21 from pylib import cmd_helper |
| 25 from pylib import constants | 22 from pylib import constants |
| 26 from pylib import pexpect | 23 from pylib import pexpect |
| 24 from pylib.utils import time_profile |
| 27 | 25 |
| 28 import errors | 26 import errors |
| 29 import run_command | 27 import run_command |
| 30 | 28 |
| 31 # SD card size | 29 # SD card size |
| 32 SDCARD_SIZE = '512M' | 30 SDCARD_SIZE = '512M' |
| 33 | 31 |
| 34 # Template used to generate config.ini files for the emulator | 32 # Template used to generate config.ini files for the emulator |
| 35 CONFIG_TEMPLATE = """avd.ini.encoding=ISO-8859-1 | 33 CONFIG_TEMPLATE = """avd.ini.encoding=ISO-8859-1 |
| 36 hw.dPad=no | 34 hw.dPad=no |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 91 There are odd 'sticky' cases where there can be no emulator process | 89 There are odd 'sticky' cases where there can be no emulator process |
| 92 running but a device slot is taken. A little bot trouble and and | 90 running but a device slot is taken. A little bot trouble and and |
| 93 we're out of room forever. | 91 we're out of room forever. |
| 94 """ | 92 """ |
| 95 emulators = android_commands.GetAttachedDevices(hardware=False) | 93 emulators = android_commands.GetAttachedDevices(hardware=False) |
| 96 if not emulators: | 94 if not emulators: |
| 97 return | 95 return |
| 98 for emu_name in emulators: | 96 for emu_name in emulators: |
| 99 cmd_helper.RunCmd(['adb', '-s', emu_name, 'emu', 'kill']) | 97 cmd_helper.RunCmd(['adb', '-s', emu_name, 'emu', 'kill']) |
| 100 logging.info('Emulator killing is async; give a few seconds for all to die.') | 98 logging.info('Emulator killing is async; give a few seconds for all to die.') |
| 101 for i in range(5): | 99 for _ in range(5): |
| 102 if not android_commands.GetAttachedDevices(hardware=False): | 100 if not android_commands.GetAttachedDevices(hardware=False): |
| 103 return | 101 return |
| 104 time.sleep(1) | 102 time.sleep(1) |
| 105 | 103 |
| 106 | 104 |
| 107 def DeleteAllTempAVDs(): | 105 def DeleteAllTempAVDs(): |
| 108 """Delete all temporary AVDs which are created for tests. | 106 """Delete all temporary AVDs which are created for tests. |
| 109 | 107 |
| 110 If the test exits abnormally and some temporary AVDs created when testing may | 108 If the test exits abnormally and some temporary AVDs created when testing may |
| 111 be left in the system. Clean these AVDs. | 109 be left in the system. Clean these AVDs. |
| (...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 222 android_sdk_root = os.path.join(constants.EMULATOR_SDK_ROOT, 'sdk') | 220 android_sdk_root = os.path.join(constants.EMULATOR_SDK_ROOT, 'sdk') |
| 223 self.emulator = os.path.join(android_sdk_root, 'tools', 'emulator') | 221 self.emulator = os.path.join(android_sdk_root, 'tools', 'emulator') |
| 224 self.android = os.path.join(android_sdk_root, 'tools', 'android') | 222 self.android = os.path.join(android_sdk_root, 'tools', 'android') |
| 225 self.popen = None | 223 self.popen = None |
| 226 self.device = None | 224 self.device = None |
| 227 self.abi = abi | 225 self.abi = abi |
| 228 self.avd_name = avd_name | 226 self.avd_name = avd_name |
| 229 self.api_level = api_level | 227 self.api_level = api_level |
| 230 self._CreateAVD() | 228 self._CreateAVD() |
| 231 | 229 |
| 232 def _DeviceName(self): | 230 @staticmethod |
| 231 def _DeviceName(): |
| 233 """Return our device name.""" | 232 """Return our device name.""" |
| 234 port = _GetAvailablePort() | 233 port = _GetAvailablePort() |
| 235 return ('emulator-%d' % port, port) | 234 return ('emulator-%d' % port, port) |
| 236 | 235 |
| 237 def _CreateAVD(self): | 236 def _CreateAVD(self): |
| 238 """Creates an AVD with the given name. | 237 """Creates an AVD with the given name. |
| 239 | 238 |
| 240 Return avd_name. | 239 Return avd_name. |
| 241 """ | 240 """ |
| 242 | 241 |
| (...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 342 # For x86 emulator --enable-kvm will fail early, avoiding accidental | 341 # For x86 emulator --enable-kvm will fail early, avoiding accidental |
| 343 # runs in a slow mode (i.e. without hardware virtualization support). | 342 # runs in a slow mode (i.e. without hardware virtualization support). |
| 344 '--enable-kvm', | 343 '--enable-kvm', |
| 345 ]) | 344 ]) |
| 346 | 345 |
| 347 logging.info('Emulator launch command: %s', ' '.join(emulator_command)) | 346 logging.info('Emulator launch command: %s', ' '.join(emulator_command)) |
| 348 self.popen = subprocess.Popen(args=emulator_command, | 347 self.popen = subprocess.Popen(args=emulator_command, |
| 349 stderr=subprocess.STDOUT) | 348 stderr=subprocess.STDOUT) |
| 350 self._InstallKillHandler() | 349 self._InstallKillHandler() |
| 351 | 350 |
| 352 def _AggressiveImageCleanup(self): | 351 @staticmethod |
| 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 as e: | 389 except errors.WaitForResponseTimedOutError: |
| 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 |