chromium/tools/cr/cr/actions/adb.py

# Copyright 2013 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""A module to hold adb specific action implementations."""

from __future__ import print_function

import re

import cr


class Adb(object):
  """Exposes the functionality of the adb tool to the rest of cr.

  This is intended as the only class in the cr that needs to understand the
  adb command line, and expose it in neutral form to the rest of the code.
  """

  # Tracks the set of killed target names, so we don't keep issuing kill
  # commands that are not going to have any effect.
  _kills = {}

  @classmethod
  def GetPids(cls, target):
    """Gets the set of running PIDs that match the specified target."""
    pids = []
    with target:
      output = cr.Host.Capture('{CR_ADB}', 'shell', 'ps')
    pattern = re.compile(r'\S+\s+(\d+)\s+.*{CR_PROCESS}')
    for line in output.split('\n'):
      match = re.match(pattern, line)
      if match:
        pids.append(match.group(1))
    return pids

  @classmethod
  def Run(cls, target, arguments):
    """Invoke a target binary on the device."""
    with target:
      cr.Host.Execute(
          '{CR_ADB}', 'shell', 'am', 'start',
          '-a', '{CR_ACTION}',
          '-n', '{CR_INTENT}',
          '{CR_RUN_ARGUMENTS}',
          *arguments
      )

  @classmethod
  def Kill(cls, target, _):
    """Kill all running processes for a target."""
    target_name = target.build_target
    if target_name in cls._kills:
      # already killed this target, do nothing
      return
    pids = cls.GetPids(target)
    if pids:
      with target:
        cr.Host.Execute('{CR_ADB}', 'shell', 'kill', *pids)
    elif target.verbose:
      print(target.Substitute('{CR_TARGET_NAME} not running'))
    cls._kills[target_name] = True

  @classmethod
  def Uninstall(cls, target, arguments):
    with target:
      cr.Host.Execute(
          '{CR_ADB}', 'uninstall',
          '{CR_PACKAGE}',
          *arguments
      )

  @classmethod
  def Install(cls, target, arguments):
    with target:
      cr.Host.Execute(
          '{CR_ADB}', 'install',
          '{CR_BINARY}',
          *arguments
      )

  @classmethod
  def Reinstall(cls, target, arguments):
    with target:
      cr.Host.Execute(
          '{CR_ADB}', 'install',
          '-r',
          '{CR_BINARY}',
          *arguments
      )

  @classmethod
  def AttachGdb(cls, target, arguments):
    with target:
      cr.Host.Execute(
          '{CR_ADB_GDB}',
          '--adb={CR_ADB}',
          '--symbol-dir=${CR_BUILD_DIR}/lib',
          '--program-name={CR_TARGET_NAME}',
          '--package-name={CR_PACKAGE}',
          *arguments
      )


class AdbRunner(cr.Runner):
  """An implementation of cr.Runner for the android platform."""

  @property
  def enabled(self):
    return cr.AndroidPlatform.GetInstance().is_active

  def Kill(self, targets, arguments):
    for target in targets:
      Adb.Kill(target, arguments)

  def Run(self, target, arguments):
    Adb.Run(target, arguments)

  def Test(self, target, arguments):
    with target:
      test_type = cr.context.Get('CR_TEST_TYPE')
      if test_type == cr.Target.INSTRUMENTATION_TEST:
        target_name_flag = '--test-apk'
      else:
        target_name_flag = '-s'
      cr.Host.Execute(
          '{CR_TEST_RUNNER}', test_type,
          target_name_flag, '{CR_TARGET_NAME}',
          '--{CR_TEST_MODE}',
          *arguments
      )


class AdbInstaller(cr.Installer):
  """An implementation of cr.Installer for the android platform."""

  @property
  def enabled(self):
    return cr.AndroidPlatform.GetInstance().is_active

  def Uninstall(self, targets, arguments):
    for target in targets:
      Adb.Uninstall(target, arguments)

  def Install(self, targets, arguments):
    for target in targets:
      Adb.Install(target, arguments)

  def Reinstall(self, targets, arguments):
    for target in targets:
      Adb.Reinstall(target, arguments)