Home | History | Annotate | Download | only in controllers
      1 #
      2 #   Copyright 2016 - The Android Open Source Project
      3 #
      4 #   Licensed under the Apache License, Version 2.0 (the "License");
      5 #   you may not use this file except in compliance with the License.
      6 #   You may obtain a copy of the License at
      7 #
      8 #       http://www.apache.org/licenses/LICENSE-2.0
      9 #
     10 #   Unless required by applicable law or agreed to in writing, software
     11 #   distributed under the License is distributed on an "AS IS" BASIS,
     12 #   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 #   See the License for the specific language governing permissions and
     14 #   limitations under the License.
     15 
     16 from builtins import str
     17 from builtins import open
     18 
     19 import logging
     20 import os
     21 import time
     22 import traceback
     23 import threading
     24 import socket
     25 
     26 from vts.runners.host import keys
     27 from vts.runners.host import logger as vts_logger
     28 from vts.runners.host import signals
     29 from vts.runners.host import utils
     30 from vts.utils.python.controllers import adb
     31 from vts.utils.python.controllers import event_dispatcher
     32 from vts.utils.python.controllers import fastboot
     33 from vts.utils.python.controllers import sl4a_client
     34 from vts.runners.host.tcp_client import vts_tcp_client
     35 from vts.utils.python.mirror import hal_mirror
     36 from vts.utils.python.mirror import shell_mirror
     37 from vts.utils.python.mirror import lib_mirror
     38 from vts.runners.host import errors
     39 import subprocess
     40 
     41 VTS_CONTROLLER_CONFIG_NAME = "AndroidDevice"
     42 VTS_CONTROLLER_REFERENCE_NAME = "android_devices"
     43 
     44 ANDROID_DEVICE_PICK_ALL_TOKEN = "*"
     45 # Key name for adb logcat extra params in config file.
     46 ANDROID_DEVICE_ADB_LOGCAT_PARAM_KEY = "adb_logcat_param"
     47 ANDROID_DEVICE_EMPTY_CONFIG_MSG = "Configuration is empty, abort!"
     48 ANDROID_DEVICE_NOT_LIST_CONFIG_MSG = "Configuration should be a list, abort!"
     49 
     50 ANDROID_PRODUCT_TYPE_UNKNOWN = "unknown"
     51 
     52 # Target-side directory where the VTS binaries are uploaded
     53 DEFAULT_AGENT_BASE_DIR = "/data/local/tmp"
     54 # Time for which the current is put on sleep when the client is unable to
     55 # make a connection.
     56 THREAD_SLEEP_TIME = 1
     57 # Max number of attempts that the client can make to connect to the agent
     58 MAX_AGENT_CONNECT_RETRIES = 10
     59 
     60 class AndroidDeviceError(signals.ControllerError):
     61     pass
     62 
     63 
     64 def create(configs, start_services=True):
     65     """Creates AndroidDevice controller objects.
     66 
     67     Args:
     68         configs: A list of dicts, each representing a configuration for an
     69                  Android device.
     70         start_services: boolean, controls whether services will be started.
     71 
     72     Returns:
     73         A list of AndroidDevice objects.
     74     """
     75     if not configs:
     76         raise AndroidDeviceError(ANDROID_DEVICE_EMPTY_CONFIG_MSG)
     77     elif configs == ANDROID_DEVICE_PICK_ALL_TOKEN:
     78         ads = get_all_instances()
     79     elif not isinstance(configs, list):
     80         raise AndroidDeviceError(ANDROID_DEVICE_NOT_LIST_CONFIG_MSG)
     81     elif isinstance(configs[0], str):
     82         # Configs is a list of serials.
     83         ads = get_instances(configs)
     84     else:
     85         # Configs is a list of dicts.
     86         ads = get_instances_with_configs(configs)
     87     connected_ads = list_adb_devices()
     88     for ad in ads:
     89         if ad.serial not in connected_ads:
     90             raise DoesNotExistError(("Android device %s is specified in config"
     91                                      " but is not attached.") % ad.serial)
     92     if start_services:
     93         _startServicesOnAds(ads)
     94     return ads
     95 
     96 
     97 def destroy(ads):
     98     """Cleans up AndroidDevice objects.
     99 
    100     Args:
    101         ads: A list of AndroidDevice objects.
    102     """
    103     for ad in ads:
    104         try:
    105             ad.cleanUp()
    106         except:
    107             ad.log.exception("Failed to clean up properly.")
    108 
    109 
    110 def _startServicesOnAds(ads):
    111     """Starts long running services on multiple AndroidDevice objects.
    112 
    113     If any one AndroidDevice object fails to start services, cleans up all
    114     existing AndroidDevice objects and their services.
    115 
    116     Args:
    117         ads: A list of AndroidDevice objects whose services to start.
    118     """
    119     running_ads = []
    120     for ad in ads:
    121         running_ads.append(ad)
    122         try:
    123             ad.startServices()
    124         except:
    125             ad.log.exception("Failed to start some services, abort!")
    126             destroy(running_ads)
    127             raise
    128 
    129 
    130 def _parse_device_list(device_list_str, key):
    131     """Parses a byte string representing a list of devices. The string is
    132     generated by calling either adb or fastboot.
    133 
    134     Args:
    135         device_list_str: Output of adb or fastboot.
    136         key: The token that signifies a device in device_list_str.
    137 
    138     Returns:
    139         A list of android device serial numbers.
    140     """
    141     clean_lines = str(device_list_str, 'utf-8').strip().split('\n')
    142     results = []
    143     for line in clean_lines:
    144         tokens = line.strip().split('\t')
    145         if len(tokens) == 2 and tokens[1] == key:
    146             results.append(tokens[0])
    147     return results
    148 
    149 
    150 def list_adb_devices():
    151     """List all target devices connected to the host and detected by adb.
    152 
    153     Returns:
    154         A list of android device serials. Empty if there's none.
    155     """
    156     out = adb.AdbProxy().devices()
    157     return _parse_device_list(out, "device")
    158 
    159 
    160 def list_fastboot_devices():
    161     """List all android devices connected to the computer that are in in
    162     fastboot mode. These are detected by fastboot.
    163 
    164     Returns:
    165         A list of android device serials. Empty if there's none.
    166     """
    167     out = fastboot.FastbootProxy().devices()
    168     return _parse_device_list(out, "fastboot")
    169 
    170 
    171 def get_instances(serials):
    172     """Create AndroidDevice instances from a list of serials.
    173 
    174     Args:
    175         serials: A list of android device serials.
    176 
    177     Returns:
    178         A list of AndroidDevice objects.
    179     """
    180     results = []
    181     for s in serials:
    182         results.append(AndroidDevice(s))
    183     return results
    184 
    185 
    186 def get_instances_with_configs(configs):
    187     """Create AndroidDevice instances from a list of json configs.
    188 
    189     Each config should have the required key-value pair "serial".
    190 
    191     Args:
    192         configs: A list of dicts each representing the configuration of one
    193             android device.
    194 
    195     Returns:
    196         A list of AndroidDevice objects.
    197     """
    198     results = []
    199     for c in configs:
    200         try:
    201             serial = c.pop(keys.ConfigKeys.IKEY_SERIAL)
    202         except KeyError:
    203             raise AndroidDeviceError(
    204                 ('Required value %s is missing in '
    205                  'AndroidDevice config %s.') % (keys.ConfigKeys.IKEY_SERIAL,
    206                                                 c))
    207         try:
    208             product_type = c.pop(keys.ConfigKeys.IKEY_PRODUCT_TYPE)
    209         except KeyError:
    210             logging.error(
    211                 'Required value %s is missing in '
    212                 'AndroidDevice config %s.',
    213                 keys.ConfigKeys.IKEY_PRODUCT_TYPE, c)
    214             product_type = ANDROID_PRODUCT_TYPE_UNKNOWN
    215 
    216         ad = AndroidDevice(serial, product_type)
    217         ad.loadConfig(c)
    218         results.append(ad)
    219     return results
    220 
    221 
    222 def get_all_instances(include_fastboot=False):
    223     """Create AndroidDevice instances for all attached android devices.
    224 
    225     Args:
    226         include_fastboot: Whether to include devices in bootloader mode or not.
    227 
    228     Returns:
    229         A list of AndroidDevice objects each representing an android device
    230         attached to the computer.
    231     """
    232     if include_fastboot:
    233         serial_list = list_adb_devices() + list_fastboot_devices()
    234         return get_instances(serial_list)
    235     return get_instances(list_adb_devices())
    236 
    237 
    238 def filter_devices(ads, func):
    239     """Finds the AndroidDevice instances from a list that match certain
    240     conditions.
    241 
    242     Args:
    243         ads: A list of AndroidDevice instances.
    244         func: A function that takes an AndroidDevice object and returns True
    245             if the device satisfies the filter condition.
    246 
    247     Returns:
    248         A list of AndroidDevice instances that satisfy the filter condition.
    249     """
    250     results = []
    251     for ad in ads:
    252         if func(ad):
    253             results.append(ad)
    254     return results
    255 
    256 
    257 def get_device(ads, **kwargs):
    258     """Finds a unique AndroidDevice instance from a list that has specific
    259     attributes of certain values.
    260 
    261     Example:
    262         get_device(android_devices, label="foo", phone_number="1234567890")
    263         get_device(android_devices, model="angler")
    264 
    265     Args:
    266         ads: A list of AndroidDevice instances.
    267         kwargs: keyword arguments used to filter AndroidDevice instances.
    268 
    269     Returns:
    270         The target AndroidDevice instance.
    271 
    272     Raises:
    273         AndroidDeviceError is raised if none or more than one device is
    274         matched.
    275     """
    276 
    277     def _get_device_filter(ad):
    278         for k, v in kwargs.items():
    279             if not hasattr(ad, k):
    280                 return False
    281             elif getattr(ad, k) != v:
    282                 return False
    283         return True
    284 
    285     filtered = filter_devices(ads, _get_device_filter)
    286     if not filtered:
    287         raise AndroidDeviceError(("Could not find a target device that matches"
    288                                   " condition: %s.") % kwargs)
    289     elif len(filtered) == 1:
    290         return filtered[0]
    291     else:
    292         serials = [ad.serial for ad in filtered]
    293         raise AndroidDeviceError("More than one device matched: %s" % serials)
    294 
    295 
    296 def takeBugReports(ads, test_name, begin_time):
    297     """Takes bug reports on a list of android devices.
    298 
    299     If you want to take a bug report, call this function with a list of
    300     android_device objects in on_fail. But reports will be taken on all the
    301     devices in the list concurrently. Bug report takes a relative long
    302     time to take, so use this cautiously.
    303 
    304     Args:
    305         ads: A list of AndroidDevice instances.
    306         test_name: Name of the test case that triggered this bug report.
    307         begin_time: Logline format timestamp taken when the test started.
    308     """
    309     begin_time = vts_logger.normalizeLogLineTimestamp(begin_time)
    310 
    311     def take_br(test_name, begin_time, ad):
    312         ad.takeBugReport(test_name, begin_time)
    313 
    314     args = [(test_name, begin_time, ad) for ad in ads]
    315     utils.concurrent_exec(take_br, args)
    316 
    317 
    318 class AndroidDevice(object):
    319     """Class representing an android device.
    320 
    321     Each object of this class represents one Android device. The object holds
    322     handles to adb, fastboot, and various RPC clients.
    323 
    324     Attributes:
    325         serial: A string that's the serial number of the Android device.
    326         device_command_port: int, the port number used on the Android device
    327                 for adb port forwarding (for command-response sessions).
    328         device_callback_port: int, the port number used on the Android device
    329                 for adb port reverse forwarding (for callback sessions).
    330         log: A logger project with a device-specific prefix for each line -
    331              [AndroidDevice|<serial>]
    332         log_path: A string that is the path where all logs collected on this
    333                   android device should be stored.
    334         adb_logcat_process: A process that collects the adb logcat.
    335         adb_logcat_file_path: A string that's the full path to the adb logcat
    336                               file collected, if any.
    337         vts_agent_process: A process that runs the HAL agent.
    338         adb: An AdbProxy object used for interacting with the device via adb.
    339         fastboot: A FastbootProxy object used for interacting with the device
    340                   via fastboot.
    341         host_command_port: the host-side port for runner to agent sessions
    342                            (to send commands and receive responses).
    343         host_callback_port: the host-side port for agent to runner sessions
    344                             (to get callbacks from agent).
    345         hal: HalMirror, in charge of all communications with the HAL layer.
    346         lib: LibMirror, in charge of all communications with static and shared
    347              native libs.
    348         shell: ShellMirror, in charge of all communications with shell.
    349         _product_type: A string, the device product type (e.g., bullhead) if
    350                        known, ANDROID_PRODUCT_TYPE_UNKNOWN otherwise.
    351     """
    352 
    353     def __init__(self, serial="", product_type=ANDROID_PRODUCT_TYPE_UNKNOWN,
    354                  device_callback_port=5010):
    355         self.serial = serial
    356         self._product_type = product_type
    357         self.device_command_port = None
    358         self.device_callback_port = device_callback_port
    359         self.log = AndroidDeviceLoggerAdapter(logging.getLogger(),
    360                                               {"serial": self.serial})
    361         base_log_path = getattr(logging, "log_path", "/tmp/logs/")
    362         self.log_path = os.path.join(base_log_path, "AndroidDevice%s" % serial)
    363         self.adb_logcat_process = None
    364         self.adb_logcat_file_path = None
    365         self.vts_agent_process = None
    366         self.adb = adb.AdbProxy(serial)
    367         self.fastboot = fastboot.FastbootProxy(serial)
    368         if not self.isBootloaderMode:
    369             self.rootAdb()
    370         self.host_command_port = None
    371         self.host_callback_port = adb.get_available_host_port()
    372         self.adb.reverse_tcp_forward(self.device_callback_port,
    373                                      self.host_callback_port)
    374         self.hal = None
    375         self.lib = None
    376         self.shell = None
    377         self.sl4a_host_port = None
    378         # TODO: figure out a good way to detect which port is available
    379         # on the target side, instead of hard coding a port number.
    380         self.sl4a_target_port = 8082
    381 
    382     def __del__(self):
    383         self.cleanUp()
    384 
    385     def cleanUp(self):
    386         """Cleans up the AndroidDevice object and releases any resources it
    387         claimed.
    388         """
    389         self.stopServices()
    390         if self.host_command_port:
    391             self.adb.forward("--remove tcp:%s" % self.host_command_port)
    392             self.host_command_port = None
    393         if self.sl4a_host_port:
    394             self.adb.forward("--remove tcp:%s" % self.sl4a_host_port)
    395             self.sl4a_host_port = None
    396 
    397     @property
    398     def isBootloaderMode(self):
    399         """True if the device is in bootloader mode."""
    400         return self.serial in list_fastboot_devices()
    401 
    402     @property
    403     def isAdbRoot(self):
    404         """True if adb is running as root for this device."""
    405         id_str = self.adb.shell("id -u").decode("utf-8")
    406         return "root" in id_str
    407 
    408     @property
    409     def verityEnabled(self):
    410         """True if verity is enabled for this device."""
    411         try:
    412             verified = self.getProp("partition.system.verified")
    413             if not verified:
    414                 return False
    415         except adb.AdbError:
    416             # If verity is disabled, there is no property 'partition.system.verified'
    417             return False
    418         return True
    419 
    420     @property
    421     def model(self):
    422         """The Android code name for the device."""
    423         # If device is in bootloader mode, get mode name from fastboot.
    424         if self.isBootloaderMode:
    425             out = self.fastboot.getvar("product").strip()
    426             # "out" is never empty because of the "total time" message fastboot
    427             # writes to stderr.
    428             lines = out.decode("utf-8").split('\n', 1)
    429             if lines:
    430                 tokens = lines[0].split(' ')
    431                 if len(tokens) > 1:
    432                     return tokens[1].lower()
    433             return None
    434         model = self.getProp("ro.build.product").lower()
    435         if model == "sprout":
    436             return model
    437         else:
    438             model = self.getProp("ro.product.name").lower()
    439             return model
    440 
    441     @property
    442     def cpu_abi(self):
    443         """CPU ABI (Application Binary Interface) of the device."""
    444         out = self.getProp("ro.product.cpu.abi")
    445         if not out:
    446             return "unknown"
    447 
    448         cpu_abi = out.lower()
    449         return cpu_abi
    450 
    451     @property
    452     def is64Bit(self):
    453         """True if device is 64 bit."""
    454         out = self.adb.shell('uname -m')
    455         return "64" in out
    456 
    457     @property
    458     def libPaths(self):
    459         """List of strings representing the paths to the native library directories."""
    460         paths_32 = ["/system/lib", "/vendor/lib"]
    461         if self.is64Bit:
    462             paths_64 = ["/system/lib64", "/vendor/lib64"]
    463             paths_64.extend(paths_32)
    464             return paths_64
    465         return paths_32
    466 
    467     @property
    468     def isAdbLogcatOn(self):
    469         """Whether there is an ongoing adb logcat collection.
    470         """
    471         if self.adb_logcat_process:
    472             return True
    473         return False
    474 
    475     def loadConfig(self, config):
    476         """Add attributes to the AndroidDevice object based on json config.
    477 
    478         Args:
    479             config: A dictionary representing the configs.
    480 
    481         Raises:
    482             AndroidDeviceError is raised if the config is trying to overwrite
    483             an existing attribute.
    484         """
    485         for k, v in config.items():
    486             if hasattr(self, k):
    487                 raise AndroidDeviceError(
    488                     "Attempting to set existing attribute %s on %s" %
    489                     (k, self.serial))
    490             setattr(self, k, v)
    491 
    492     def rootAdb(self):
    493         """Changes adb to root mode for this device."""
    494         if not self.isAdbRoot:
    495             try:
    496                 self.adb.root()
    497                 self.adb.wait_for_device()
    498                 self.adb.remount()
    499                 self.adb.wait_for_device()
    500             except adb.AdbError as e:
    501                 # adb wait-for-device is not always possible in the lab
    502                 # continue with an assumption it's done by the harness.
    503                 logging.exception(e)
    504 
    505     def startAdbLogcat(self):
    506         """Starts a standing adb logcat collection in separate subprocesses and
    507         save the logcat in a file.
    508         """
    509         if self.isAdbLogcatOn:
    510             raise AndroidDeviceError(("Android device %s already has an adb "
    511                                       "logcat thread going on. Cannot start "
    512                                       "another one.") % self.serial)
    513         f_name = "adblog,%s,%s.txt" % (self.model, self.serial)
    514         utils.create_dir(self.log_path)
    515         logcat_file_path = os.path.join(self.log_path, f_name)
    516         try:
    517             extra_params = self.adb_logcat_param
    518         except AttributeError:
    519             extra_params = "-b all"
    520         cmd = "adb -s %s logcat -v threadtime %s >> %s" % (
    521             self.serial, extra_params, logcat_file_path)
    522         self.adb_logcat_process = utils.start_standing_subprocess(cmd)
    523         self.adb_logcat_file_path = logcat_file_path
    524 
    525     def stopAdbLogcat(self):
    526         """Stops the adb logcat collection subprocess.
    527         """
    528         if not self.isAdbLogcatOn:
    529             raise AndroidDeviceError(
    530                 "Android device %s does not have an ongoing adb logcat collection."
    531                 % self.serial)
    532         utils.stop_standing_subprocess(self.adb_logcat_process)
    533         self.adb_logcat_process = None
    534 
    535     def takeBugReport(self, test_name, begin_time):
    536         """Takes a bug report on the device and stores it in a file.
    537 
    538         Args:
    539             test_name: Name of the test case that triggered this bug report.
    540             begin_time: Logline format timestamp taken when the test started.
    541         """
    542         br_path = os.path.join(self.log_path, "BugReports")
    543         utils.create_dir(br_path)
    544         base_name = ",%s,%s.txt" % (begin_time, self.serial)
    545         test_name_len = utils.MAX_FILENAME_LEN - len(base_name)
    546         out_name = test_name[:test_name_len] + base_name
    547         full_out_path = os.path.join(br_path, out_name.replace(' ', '\ '))
    548         self.log.info("Taking bugreport for %s on %s", test_name, self.serial)
    549         self.adb.bugreport(" > %s" % full_out_path)
    550         self.log.info("Bugreport for %s taken at %s", test_name, full_out_path)
    551 
    552     @utils.timeout(15 * 60)
    553     def waitForBootCompletion(self):
    554         """Waits for Android framework to broadcast ACTION_BOOT_COMPLETED.
    555 
    556         This function times out after 15 minutes.
    557         """
    558         try:
    559             self.adb.wait_for_device()
    560         except adb.AdbError as e:
    561             # adb wait-for-device is not always possible in the lab
    562             logging.exception(e)
    563         while not self.hasBooted():
    564             time.sleep(5)
    565 
    566     def hasBooted(self):
    567         """Checks whether the device has booted.
    568 
    569         Returns:
    570             True if booted, False otherwise.
    571         """
    572         try:
    573             completed = self.getProp("sys.boot_completed")
    574             if completed == '1':
    575                 return True
    576         except adb.AdbError:
    577             # adb shell calls may fail during certain period of booting
    578             # process, which is normal. Ignoring these errors.
    579             return False
    580 
    581     def start(self):
    582         """Starts Android runtime and waits for ACTION_BOOT_COMPLETED."""
    583         logging.info("starting Android Runtime")
    584         self.adb.shell("start")
    585         self.waitForBootCompletion()
    586         logging.info("Android Runtime started")
    587 
    588     def stop(self):
    589         """Stops Android runtime."""
    590         logging.info("stopping Android Runtime")
    591         self.adb.shell("stop")
    592         self.setProp("sys.boot_completed", 0)
    593         logging.info("Android Runtime stopped")
    594 
    595     def setProp(self, name, value):
    596         """Calls setprop shell command.
    597 
    598         Args:
    599             name: string, the name of a system property to set
    600             value: any type, value will be converted to string. Quotes in value
    601                    is not supported at this time; if value contains a quote,
    602                    this method will log an error and return.
    603 
    604         Raises:
    605             AdbError, if name contains invalid character
    606         """
    607         if name is None or value is None:
    608             logging.error("name or value of system property "
    609                           "should not be None. No property is set.")
    610             return
    611 
    612         value = str(value)
    613 
    614         if "'" in value or "\"" in value:
    615             logging.error("Quotes in value of system property "
    616                           "is not yet supported. No property is set.")
    617             return
    618 
    619         self.adb.shell("setprop %s \"%s\"" % (name, value))
    620 
    621     def getProp(self, name):
    622         """Calls getprop shell command.
    623 
    624         Args:
    625             name: string, the name of a system property to get
    626 
    627         Returns:
    628             string, value of the property. If name does not exist; an empty
    629             string will be returned. decode("utf-8") and strip() will be called
    630             on the output before returning; None will be returned if input
    631             name is None
    632 
    633         Raises:
    634             AdbError, if name contains invalid character
    635         """
    636         if name is None:
    637             logging.error("name of system property should not be None.")
    638             return None
    639 
    640         out = self.adb.shell("getprop %s" % name)
    641         return out.decode("utf-8").strip()
    642 
    643     def reboot(self, restart_services=True):
    644         """Reboots the device and wait for device to complete booting.
    645 
    646         This is probably going to print some error messages in console. Only
    647         use if there's no other option.
    648 
    649         Raises:
    650             AndroidDeviceError is raised if waiting for completion timed
    651             out.
    652         """
    653         if self.isBootloaderMode:
    654             self.fastboot.reboot()
    655             return
    656 
    657         if restart_services:
    658             has_adb_log = self.isAdbLogcatOn
    659             has_vts_agent = True if self.vts_agent_process else False
    660             if has_adb_log:
    661                 self.stopAdbLogcat()
    662             if has_vts_agent:
    663                 self.stopVtsAgent()
    664 
    665         self.adb.reboot()
    666         self.waitForBootCompletion()
    667         self.rootAdb()
    668 
    669         if restart_services:
    670             if has_adb_log:
    671                 self.startAdbLogcat()
    672             if has_vts_agent:
    673                 self.startVtsAgent()
    674 
    675     def startServices(self):
    676         """Starts long running services on the android device.
    677 
    678         1. Start adb logcat capture.
    679         2. Start VtsAgent and create HalMirror unless disabled in config.
    680         3. If enabled in config, start sl4a service and create sl4a clients.
    681         """
    682         enable_vts_agent = getattr(self, "enable_vts_agent", True)
    683         enable_sl4a = getattr(self, "enable_sl4a", False)
    684         try:
    685             self.startAdbLogcat()
    686         except:
    687             self.log.exception("Failed to start adb logcat!")
    688             raise
    689         if enable_vts_agent:
    690             self.startVtsAgent()
    691             self.device_command_port = int(
    692                 self.adb.shell("cat /data/local/tmp/vts_tcp_server_port"))
    693             logging.info("device_command_port: %s", self.device_command_port)
    694             if not self.host_command_port:
    695                 self.host_command_port = adb.get_available_host_port()
    696             self.adb.tcp_forward(self.host_command_port, self.device_command_port)
    697             self.hal = hal_mirror.HalMirror(self.host_command_port,
    698                                             self.host_callback_port)
    699             self.lib = lib_mirror.LibMirror(self.host_command_port)
    700             self.shell = shell_mirror.ShellMirror(self.host_command_port)
    701         if enable_sl4a:
    702             self.startSl4aClient()
    703 
    704     def stopServices(self):
    705         """Stops long running services on the android device.
    706         """
    707         if self.adb_logcat_process:
    708             self.stopAdbLogcat()
    709         self.stopVtsAgent()
    710         if self.hal:
    711             self.hal.CleanUp()
    712 
    713     def startVtsAgent(self):
    714         """Start HAL agent on the AndroidDevice.
    715 
    716         This function starts the target side native agent and is persisted
    717         throughout the test run.
    718         """
    719         self.log.info("Starting VTS agent")
    720         if self.vts_agent_process:
    721             raise AndroidDeviceError("HAL agent is already running on %s." %
    722                                      self.serial)
    723 
    724         cleanup_commands = [
    725             "rm -f /data/local/tmp/vts_driver_*",
    726             "rm -f /data/local/tmp/vts_agent_callback*"
    727         ]
    728         kill_commands = ["killall vts_hal_agent32", "killall vts_hal_agent64",
    729                          "killall fuzzer32", "killall fuzzer64",
    730                          "killall vts_shell_driver32",
    731                          "killall vts_shell_driver64"]
    732         cleanup_commands.extend(kill_commands)
    733         chmod_commands = [
    734             "chmod 755 %s/32/vts_hal_agent32" % DEFAULT_AGENT_BASE_DIR,
    735             "chmod 755 %s/64/vts_hal_agent64" % DEFAULT_AGENT_BASE_DIR,
    736             "chmod 755 %s/32/fuzzer32" % DEFAULT_AGENT_BASE_DIR,
    737             "chmod 755 %s/64/fuzzer64" % DEFAULT_AGENT_BASE_DIR,
    738             "chmod 755 %s/32/vts_shell_driver32" % DEFAULT_AGENT_BASE_DIR,
    739             "chmod 755 %s/64/vts_shell_driver64" % DEFAULT_AGENT_BASE_DIR
    740         ]
    741         cleanup_commands.extend(chmod_commands)
    742         for cmd in cleanup_commands:
    743             try:
    744                 self.adb.shell(cmd)
    745             except adb.AdbError as e:
    746                 self.log.warning(
    747                     "A command to setup the env to start the VTS Agent failed %s",
    748                     e)
    749 
    750         bits = ['64', '32'] if self.is64Bit else ['32']
    751         for bitness in bits:
    752             vts_agent_log_path = os.path.join(self.log_path,
    753                      "vts_agent_" + bitness + ".log")
    754             cmd = (
    755                 'adb -s {s} shell LD_LIBRARY_PATH={path}/{bitness} '
    756                 '{path}/{bitness}/vts_hal_agent{bitness}'
    757                 ' {path}/32/fuzzer32 {path}/64/fuzzer64 {path}/spec'
    758                 ' {path}/32/vts_shell_driver32 {path}/64/vts_shell_driver64 >> {log} 2>&1'
    759             ).format(s=self.serial,
    760                      bitness=bitness,
    761                      path=DEFAULT_AGENT_BASE_DIR,
    762                      log=vts_agent_log_path)
    763             try:
    764                 self.vts_agent_process = utils.start_standing_subprocess(
    765                     cmd, check_health_delay=1)
    766                 break
    767             except utils.VTSUtilsError as e:
    768                 logging.exception(e)
    769                 with open(vts_agent_log_path, 'r') as log_file:
    770                     logging.error("VTS agent output:\n")
    771                     logging.error(log_file.read())
    772                 # one common cause is that 64-bit executable is not supported
    773                 # in low API level devices.
    774                 if bitness == '32':
    775                     raise
    776                 else:
    777                     logging.error('retrying using a 32-bit binary.')
    778 
    779     def stopVtsAgent(self):
    780         """Stop the HAL agent running on the AndroidDevice.
    781         """
    782         if self.vts_agent_process:
    783             utils.stop_standing_subprocess(self.vts_agent_process)
    784             self.vts_agent_process = None
    785 
    786     @property
    787     def product_type(self):
    788         """Gets the product type name."""
    789         return self._product_type
    790 
    791     # Code for using SL4A client
    792     def startSl4aClient(self, handle_event=True):
    793         """Create an sl4a connection to the device.
    794 
    795         Return the connection handler 'droid'. By default, another connection
    796         on the same session is made for EventDispatcher, and the dispatcher is
    797         returned to the caller as well.
    798         If sl4a server is not started on the device, try to start it.
    799 
    800         Args:
    801             handle_event: True if this droid session will need to handle
    802                           events.
    803         """
    804         self._sl4a_sessions = {}
    805         self._sl4a_event_dispatchers = {}
    806         if not self.sl4a_host_port or not adb.is_port_available(self.sl4a_host_port):
    807             self.sl4a_host_port = adb.get_available_host_port()
    808         self.adb.tcp_forward(self.sl4a_host_port, self.sl4a_target_port)
    809         try:
    810             droid = self._createNewSl4aSession()
    811         except sl4a_client.Error:
    812             sl4a_client.start_sl4a(self.adb)
    813             droid = self._createNewSl4aSession()
    814         self.sl4a = droid
    815         if handle_event:
    816             ed = self._getSl4aEventDispatcher(droid)
    817         self.sl4a_event = ed
    818 
    819     def getVintfXml(self):
    820         """Return vendor interface manifest string."""
    821         # TODO: (b/36137939) use vintf instead of lshal.
    822         try:
    823             stdout = self.adb.shell('"lshal --init-vintf 2> /dev/null"')
    824             return str(stdout)
    825         except adb.AdbError as e:
    826             return None
    827 
    828     def _getSl4aEventDispatcher(self, droid):
    829         """Return an EventDispatcher for an sl4a session
    830 
    831         Args:
    832             droid: Session to create EventDispatcher for.
    833 
    834         Returns:
    835             ed: An EventDispatcher for specified session.
    836         """
    837         # TODO (angli): Move service-specific start/stop functions out of
    838         # android_device, including VTS Agent, SL4A, and any other
    839         # target-side services.
    840         ed_key = self.serial + str(droid.uid)
    841         if ed_key in self._sl4a_event_dispatchers:
    842             if self._sl4a_event_dispatchers[ed_key] is None:
    843                 raise AndroidDeviceError("EventDispatcher Key Empty")
    844             self.log.debug("Returning existing key %s for event dispatcher!",
    845                            ed_key)
    846             return self._sl4a_event_dispatchers[ed_key]
    847         event_droid = self._addNewConnectionToSl4aSession(droid.uid)
    848         ed = event_dispatcher.EventDispatcher(event_droid)
    849         self._sl4a_event_dispatchers[ed_key] = ed
    850         return ed
    851 
    852     def _createNewSl4aSession(self):
    853         """Start a new session in sl4a.
    854 
    855         Also caches the droid in a dict with its uid being the key.
    856 
    857         Returns:
    858             An Android object used to communicate with sl4a on the android
    859                 device.
    860 
    861         Raises:
    862             sl4a_client.Error: Something is wrong with sl4a and it returned an
    863             existing uid to a new session.
    864         """
    865         droid = sl4a_client.Sl4aClient(port=self.sl4a_host_port)
    866         droid.open()
    867         if droid.uid in self._sl4a_sessions:
    868             raise sl4a_client.Error(
    869                 "SL4A returned an existing uid for a new session. Abort.")
    870         self._sl4a_sessions[droid.uid] = [droid]
    871         return droid
    872 
    873     def _addNewConnectionToSl4aSession(self, session_id):
    874         """Create a new connection to an existing sl4a session.
    875 
    876         Args:
    877             session_id: UID of the sl4a session to add connection to.
    878 
    879         Returns:
    880             An Android object used to communicate with sl4a on the android
    881                 device.
    882 
    883         Raises:
    884             DoesNotExistError: Raised if the session it's trying to connect to
    885             does not exist.
    886         """
    887         if session_id not in self._sl4a_sessions:
    888             raise DoesNotExistError("Session %d doesn't exist." % session_id)
    889         droid = sl4a_client.Sl4aClient(port=self.sl4a_host_port, uid=session_id)
    890         droid.open(cmd=sl4a_client.Sl4aCommand.CONTINUE)
    891         return droid
    892 
    893     def _terminateSl4aSession(self, session_id):
    894         """Terminate a session in sl4a.
    895 
    896         Send terminate signal to sl4a server; stop dispatcher associated with
    897         the session. Clear corresponding droids and dispatchers from cache.
    898 
    899         Args:
    900             session_id: UID of the sl4a session to terminate.
    901         """
    902         if self._sl4a_sessions and (session_id in self._sl4a_sessions):
    903             for droid in self._sl4a_sessions[session_id]:
    904                 droid.closeSl4aSession()
    905                 droid.close()
    906             del self._sl4a_sessions[session_id]
    907         ed_key = self.serial + str(session_id)
    908         if ed_key in self._sl4a_event_dispatchers:
    909             self._sl4a_event_dispatchers[ed_key].clean_up()
    910             del self._sl4a_event_dispatchers[ed_key]
    911 
    912     def _terminateAllSl4aSessions(self):
    913         """Terminate all sl4a sessions on the AndroidDevice instance.
    914 
    915         Terminate all sessions and clear caches.
    916         """
    917         if self._sl4a_sessions:
    918             session_ids = list(self._sl4a_sessions.keys())
    919             for session_id in session_ids:
    920                 try:
    921                     self._terminateSl4aSession(session_id)
    922                 except:
    923                     self.log.exception("Failed to terminate session %d.",
    924                                        session_id)
    925             if self.sl4a_host_port:
    926                 self.adb.forward("--remove tcp:%d" % self.sl4a_host_port)
    927                 self.sl4a_host_port = None
    928 
    929 
    930 class AndroidDeviceLoggerAdapter(logging.LoggerAdapter):
    931     """A wrapper class that attaches a prefix to all log lines from an
    932     AndroidDevice object.
    933     """
    934 
    935     def process(self, msg, kwargs):
    936         """Process every log message written via the wrapped logger object.
    937 
    938         We are adding the prefix "[AndroidDevice|<serial>]" to all log lines.
    939 
    940         Args:
    941             msg: string, the original log message.
    942             kwargs: dict, the key value pairs that can be used to modify the
    943                     original log message.
    944         """
    945         msg = "[AndroidDevice|%s] %s" % (self.extra["serial"], msg)
    946         return (msg, kwargs)
    947