| Index: build/android/pylib/device/device_utils.py
|
| diff --git a/build/android/pylib/device/device_utils.py b/build/android/pylib/device/device_utils.py
|
| index 57164ed13f93fef65a2e93e9f066f795035a3ff1..fccdd619d02322e10763424405f78aa477e82c6a 100644
|
| --- a/build/android/pylib/device/device_utils.py
|
| +++ b/build/android/pylib/device/device_utils.py
|
| @@ -8,21 +8,15 @@ Eventually, this will be based on adb_wrapper.
|
| """
|
| # pylint: disable=W0613
|
|
|
| -import multiprocessing
|
| -import os
|
| import pipes
|
| import sys
|
| -import tempfile
|
| import time
|
| -import zipfile
|
|
|
| import pylib.android_commands
|
| from pylib.device import adb_wrapper
|
| from pylib.device import decorators
|
| from pylib.device import device_errors
|
| -from pylib.device.commands import install_commands
|
| from pylib.utils import apk_helper
|
| -from pylib.utils import host_utils
|
| from pylib.utils import parallelizer
|
|
|
| _DEFAULT_TIMEOUT = 30
|
| @@ -67,23 +61,17 @@ class DeviceUtils(object):
|
| operation should be retried on failure if no explicit
|
| value is provided.
|
| """
|
| - self.adb = None
|
| self.old_interface = None
|
| if isinstance(device, basestring):
|
| - self.adb = adb_wrapper.AdbWrapper(device)
|
| self.old_interface = pylib.android_commands.AndroidCommands(device)
|
| elif isinstance(device, adb_wrapper.AdbWrapper):
|
| - self.adb = device
|
| self.old_interface = pylib.android_commands.AndroidCommands(str(device))
|
| elif isinstance(device, pylib.android_commands.AndroidCommands):
|
| - self.adb = adb_wrapper.AdbWrapper(device.GetDevice())
|
| self.old_interface = device
|
| elif not device:
|
| - self.adb = adb_wrapper.AdbWrapper('')
|
| self.old_interface = pylib.android_commands.AndroidCommands()
|
| else:
|
| raise ValueError('Unsupported type passed for argument "device"')
|
| - self._commands_installed = False
|
| self._default_timeout = default_timeout
|
| self._default_retries = default_retries
|
| assert(hasattr(self, decorators.DEFAULT_TIMEOUT_ATTR))
|
| @@ -103,9 +91,6 @@ class DeviceUtils(object):
|
| Raises:
|
| CommandTimeoutError on timeout.
|
| """
|
| - return self._IsOnlineImpl()
|
| -
|
| - def _IsOnlineImpl(self):
|
| return self.old_interface.IsOnline()
|
|
|
| @decorators.WithTimeoutAndRetriesFromInstance()
|
| @@ -126,6 +111,17 @@ class DeviceUtils(object):
|
| return self._HasRootImpl()
|
|
|
| def _HasRootImpl(self):
|
| + """Implementation of HasRoot.
|
| +
|
| + This is split from HasRoot to allow other DeviceUtils methods to call
|
| + HasRoot without spawning a new timeout thread.
|
| +
|
| + Returns:
|
| + Same as for |HasRoot|.
|
| +
|
| + Raises:
|
| + Same as for |HasRoot|.
|
| + """
|
| return self.old_interface.IsRootEnabled()
|
|
|
| @decorators.WithTimeoutAndRetriesFromInstance()
|
| @@ -160,9 +156,6 @@ class DeviceUtils(object):
|
| CommandTimeoutError on timeout.
|
| DeviceUnreachableError on missing device.
|
| """
|
| - return self._GetExternalStoragePathImpl()
|
| -
|
| - def _GetExternalStoragePathImpl(self):
|
| try:
|
| return self.old_interface.GetExternalStorage()
|
| except AssertionError as e:
|
| @@ -190,6 +183,21 @@ class DeviceUtils(object):
|
| self._WaitUntilFullyBootedImpl(wifi=wifi, timeout=timeout)
|
|
|
| def _WaitUntilFullyBootedImpl(self, wifi=False, timeout=None):
|
| + """Implementation of WaitUntilFullyBooted.
|
| +
|
| + This is split from WaitUntilFullyBooted to allow other DeviceUtils methods
|
| + to call WaitUntilFullyBooted without spawning a new timeout thread.
|
| +
|
| + TODO(jbudorick) Remove the timeout parameter once this is no longer
|
| + implemented via AndroidCommands.
|
| +
|
| + Args:
|
| + wifi: Same as for |WaitUntilFullyBooted|.
|
| + timeout: timeout in seconds
|
| +
|
| + Raises:
|
| + Same as for |WaitUntilFullyBooted|.
|
| + """
|
| if timeout is None:
|
| timeout = self._default_timeout
|
| self.old_interface.WaitForSystemBootCompleted(timeout)
|
| @@ -273,8 +281,8 @@ class DeviceUtils(object):
|
| str(e), device=str(self)), None, sys.exc_info()[2]
|
|
|
| @decorators.WithTimeoutAndRetriesFromInstance()
|
| - def RunShellCommand(self, cmd, check_return=False, as_root=False, cwd=None,
|
| - env=None, timeout=None, retries=None):
|
| + def RunShellCommand(self, cmd, check_return=False, as_root=False,
|
| + timeout=None, retries=None):
|
| """Run an ADB shell command.
|
|
|
| TODO(jbudorick) Switch the default value of check_return to True after
|
| @@ -286,8 +294,6 @@ class DeviceUtils(object):
|
| be checked.
|
| as_root: A boolean indicating whether the shell command should be run
|
| with root privileges.
|
| - cwd: The device directory in which the command should be run.
|
| - env: The environment variables with which the command should be run.
|
| timeout: timeout in seconds
|
| retries: number of retries
|
|
|
| @@ -299,23 +305,35 @@ class DeviceUtils(object):
|
| CommandTimeoutError on timeout.
|
| DeviceUnreachableError on missing device.
|
| """
|
| - return self._RunShellCommandImpl(
|
| - cmd, check_return=check_return, as_root=as_root, cwd=cwd, env=env,
|
| - timeout=timeout)
|
| + return self._RunShellCommandImpl(cmd, check_return=check_return,
|
| + as_root=as_root, timeout=timeout)
|
|
|
| def _RunShellCommandImpl(self, cmd, check_return=False, as_root=False,
|
| - cwd=None, env=None, timeout=None):
|
| - # TODO(jbudorick): Remove the timeout parameter once this is no longer
|
| - # backed by AndroidCommands.
|
| + timeout=None):
|
| + """Implementation of RunShellCommand.
|
| +
|
| + This is split from RunShellCommand to allow other DeviceUtils methods to
|
| + call RunShellCommand without spawning a new timeout thread.
|
| +
|
| + TODO(jbudorick) Remove the timeout parameter once this is no longer
|
| + implemented via AndroidCommands.
|
| +
|
| + Args:
|
| + cmd: Same as for |RunShellCommand|.
|
| + check_return: Same as for |RunShellCommand|.
|
| + as_root: Same as for |RunShellCommand|.
|
| + timeout: timeout in seconds
|
| +
|
| + Raises:
|
| + Same as for |RunShellCommand|.
|
| +
|
| + Returns:
|
| + Same as for |RunShellCommand|.
|
| + """
|
| if isinstance(cmd, list):
|
| cmd = ' '.join(cmd)
|
| - if as_root and not self._HasRootImpl():
|
| + if as_root and not self.HasRoot():
|
| cmd = 'su -c %s' % cmd
|
| - if env:
|
| - cmd = '%s %s' % (
|
| - ' '.join('%s=%s' % (k, v) for k, v in env.iteritems()), cmd)
|
| - if cwd:
|
| - cmd = 'cd %s && %s' % (cwd, cmd)
|
| if check_return:
|
| code, output = self.old_interface.GetShellCommandStatusAndOutput(
|
| cmd, timeout_time=timeout)
|
| @@ -483,15 +501,15 @@ class DeviceUtils(object):
|
| @decorators.WithTimeoutAndRetriesDefaults(
|
| PUSH_CHANGED_FILES_DEFAULT_TIMEOUT,
|
| PUSH_CHANGED_FILES_DEFAULT_RETRIES)
|
| - def PushChangedFiles(self, host_device_tuples, timeout=None,
|
| + def PushChangedFiles(self, host_path, device_path, timeout=None,
|
| retries=None):
|
| """Push files to the device, skipping files that don't need updating.
|
|
|
| Args:
|
| - host_device_tuples: A list of (host_path, device_path) tuples, where
|
| - |host_path| is an absolute path of a file or directory on the host
|
| - that should be minimially pushed to the device, and |device_path| is
|
| - an absolute path of the destination on the device.
|
| + host_path: A string containing the absolute path to the file or directory
|
| + on the host that should be minimally pushed to the device.
|
| + device_path: A string containing the absolute path of the destination on
|
| + the device.
|
| timeout: timeout in seconds
|
| retries: number of retries
|
|
|
| @@ -500,125 +518,7 @@ class DeviceUtils(object):
|
| CommandTimeoutError on timeout.
|
| DeviceUnreachableError on missing device.
|
| """
|
| -
|
| - files = []
|
| - for h, d in host_device_tuples:
|
| - if os.path.isdir(h):
|
| - self._RunShellCommandImpl(['mkdir', '-p', '"%s"' % d],
|
| - check_return=True)
|
| - files += self.old_interface.GetFilesChanged(h, d)
|
| -
|
| - if not files:
|
| - return
|
| -
|
| - size = sum(host_utils.GetRecursiveDiskUsage(h) for h, _ in files)
|
| - file_count = len(files)
|
| - dir_size = sum(host_utils.GetRecursiveDiskUsage(h)
|
| - for h, _ in host_device_tuples)
|
| - dir_file_count = 0
|
| - for h, _ in host_device_tuples:
|
| - if os.path.isdir(h):
|
| - dir_file_count += sum(len(f) for _r, _d, f in os.walk(h))
|
| - else:
|
| - dir_file_count += 1
|
| -
|
| - push_duration = self._ApproximateDuration(
|
| - file_count, file_count, size, False)
|
| - dir_push_duration = self._ApproximateDuration(
|
| - len(host_device_tuples), dir_file_count, dir_size, False)
|
| - zip_duration = self._ApproximateDuration(1, 1, size, True)
|
| -
|
| - if dir_push_duration < push_duration and dir_push_duration < zip_duration:
|
| - self._PushChangedFilesIndividually(host_device_tuples)
|
| - elif push_duration < zip_duration:
|
| - self._PushChangedFilesIndividually(files)
|
| - else:
|
| - self._PushChangedFilesZipped(files)
|
| - self._RunShellCommandImpl(
|
| - ['chmod', '-R', '777'] + [d for _, d in host_device_tuples],
|
| - as_root=True)
|
| -
|
| - @staticmethod
|
| - def _ApproximateDuration(adb_calls, file_count, byte_count, is_zipping):
|
| - # We approximate the time to push a set of files to a device as:
|
| - # t = c1 * a + c2 * f + c3 + b / c4 + b / (c5 * c6), where
|
| - # t: total time (sec)
|
| - # c1: adb call time delay (sec)
|
| - # a: number of times adb is called (unitless)
|
| - # c2: push time delay (sec)
|
| - # f: number of files pushed via adb (unitless)
|
| - # c3: zip time delay (sec)
|
| - # c4: zip rate (bytes/sec)
|
| - # b: total number of bytes (bytes)
|
| - # c5: transfer rate (bytes/sec)
|
| - # c6: compression ratio (unitless)
|
| -
|
| - # All of these are approximations.
|
| - ADB_CALL_PENALTY = 0.1 # seconds
|
| - ADB_PUSH_PENALTY = 0.01 # seconds
|
| - ZIP_PENALTY = 2.0 # seconds
|
| - ZIP_RATE = 10000000.0 # bytes / second
|
| - TRANSFER_RATE = 2000000.0 # bytes / second
|
| - COMPRESSION_RATIO = 2.0 # unitless
|
| -
|
| - adb_call_time = ADB_CALL_PENALTY * adb_calls
|
| - adb_push_setup_time = ADB_PUSH_PENALTY * file_count
|
| - if is_zipping:
|
| - zip_time = ZIP_PENALTY + byte_count / ZIP_RATE
|
| - transfer_time = byte_count / (TRANSFER_RATE * COMPRESSION_RATIO)
|
| - else:
|
| - zip_time = 0
|
| - transfer_time = byte_count / TRANSFER_RATE
|
| - return (adb_call_time + adb_push_setup_time + zip_time + transfer_time)
|
| -
|
| - def _PushChangedFilesIndividually(self, files):
|
| - for h, d in files:
|
| - self.adb.Push(h, d)
|
| -
|
| - def _PushChangedFilesZipped(self, files):
|
| - if not files:
|
| - return
|
| -
|
| - self._InstallCommands()
|
| -
|
| - with tempfile.NamedTemporaryFile(suffix='.zip') as zip_file:
|
| - zip_proc = multiprocessing.Process(
|
| - target=DeviceUtils._CreateDeviceZip,
|
| - args=(zip_file.name, files))
|
| - zip_proc.start()
|
| - zip_proc.join()
|
| -
|
| - zip_on_device = '%s/tmp.zip' % self._GetExternalStoragePathImpl()
|
| - try:
|
| - self.adb.Push(zip_file.name, zip_on_device)
|
| - self._RunShellCommandImpl(
|
| - ['unzip', zip_on_device],
|
| - as_root=True, check_return=True,
|
| - env={'PATH': '$PATH:%s' % install_commands.BIN_DIR})
|
| - finally:
|
| - if zip_proc.is_alive():
|
| - zip_proc.terminate()
|
| - if self._IsOnlineImpl():
|
| - self._RunShellCommandImpl(['rm', zip_on_device])
|
| -
|
| - def _InstallCommands(self):
|
| - if not self._commands_installed and not install_commands.Installed(self):
|
| - install_commands.InstallCommands(self)
|
| - self._commands_installed = True
|
| -
|
| - @staticmethod
|
| - def _CreateDeviceZip(zip_path, host_device_tuples):
|
| - with zipfile.ZipFile(zip_path, 'w') as zip_file:
|
| - for host_path, device_path in host_device_tuples:
|
| - if os.path.isfile(host_path):
|
| - zip_file.write(host_path, device_path, zipfile.ZIP_DEFLATED)
|
| - else:
|
| - for hd, _, files in os.walk(host_path):
|
| - dd = '%s/%s' % (device_path, os.path.relpath(host_path, hd))
|
| - zip_file.write(hd, dd, zipfile.ZIP_STORED)
|
| - for f in files:
|
| - zip_file.write(os.path.join(hd, f), '%s/%s' % (dd, f),
|
| - zipfile.ZIP_DEFLATED)
|
| + self.old_interface.PushIfNeeded(host_path, device_path)
|
|
|
| @decorators.WithTimeoutAndRetriesFromInstance()
|
| def FileExists(self, device_path, timeout=None, retries=None):
|
| @@ -637,6 +537,23 @@ class DeviceUtils(object):
|
| CommandTimeoutError on timeout.
|
| DeviceUnreachableError on missing device.
|
| """
|
| + return self._FileExistsImpl(device_path)
|
| +
|
| + def _FileExistsImpl(self, device_path):
|
| + """Implementation of FileExists.
|
| +
|
| + This is split from FileExists to allow other DeviceUtils methods to call
|
| + FileExists without spawning a new timeout thread.
|
| +
|
| + Args:
|
| + device_path: Same as for |FileExists|.
|
| +
|
| + Returns:
|
| + True if the file exists on the device, False otherwise.
|
| +
|
| + Raises:
|
| + Same as for |FileExists|.
|
| + """
|
| return self.old_interface.FileExistsOnDevice(device_path)
|
|
|
| @decorators.WithTimeoutAndRetriesFromInstance()
|
| @@ -681,7 +598,7 @@ class DeviceUtils(object):
|
| CommandTimeoutError on timeout.
|
| DeviceUnreachableError on missing device.
|
| """
|
| - # TODO(jbudorick) Evaluate whether we want to return a list of lines after
|
| + # TODO(jbudorick) Evaluate whether we awant to return a list of lines after
|
| # the implementation switch, and if file not found should raise exception.
|
| if as_root:
|
| if not self.old_interface.CanAccessProtectedFileContents():
|
| @@ -838,6 +755,21 @@ class DeviceUtils(object):
|
| return self._GetPidsImpl(process_name)
|
|
|
| def _GetPidsImpl(self, process_name):
|
| + """Implementation of GetPids.
|
| +
|
| + This is split from GetPids to allow other DeviceUtils methods to call
|
| + GetPids without spawning a new timeout thread.
|
| +
|
| + Args:
|
| + process_name: A string containing the process name to get the PIDs for.
|
| +
|
| + Returns:
|
| + A dict mapping process name to PID for each process that contained the
|
| + provided |process_name|.
|
| +
|
| + Raises:
|
| + DeviceUnreachableError on missing device.
|
| + """
|
| procs_pids = {}
|
| for line in self._RunShellCommandImpl('ps'):
|
| try:
|
|
|