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 gzip
     20 import logging
     21 import os
     22 import socket
     23 import subprocess
     24 import tempfile
     25 import threading
     26 import time
     27 import traceback
     28 
     29 from vts.runners.host import asserts
     30 from vts.runners.host import errors
     31 from vts.runners.host import keys
     32 from vts.runners.host import logger as vts_logger
     33 from vts.runners.host import signals
     34 from vts.runners.host import utils
     35 from vts.runners.host.tcp_client import vts_tcp_client
     36 from vts.utils.python.controllers import adb
     37 from vts.utils.python.controllers import event_dispatcher
     38 from vts.utils.python.controllers import fastboot
     39 from vts.utils.python.controllers import customflasher
     40 from vts.utils.python.controllers import sl4a_client
     41 from vts.utils.python.mirror import mirror_tracker
     42 
     43 VTS_CONTROLLER_CONFIG_NAME = "AndroidDevice"
     44 VTS_CONTROLLER_REFERENCE_NAME = "android_devices"
     45 
     46 ANDROID_DEVICE_PICK_ALL_TOKEN = "*"
     47 # Key name for adb logcat extra params in config file.
     48 ANDROID_DEVICE_ADB_LOGCAT_PARAM_KEY = "adb_logcat_param"
     49 ANDROID_DEVICE_EMPTY_CONFIG_MSG = "Configuration is empty, abort!"
     50 ANDROID_DEVICE_NOT_LIST_CONFIG_MSG = "Configuration should be a list, abort!"
     51 PORT_RETRY_COUNT = 3
     52 SL4A_APK_NAME = "com.googlecode.android_scripting"
     53 
     54 ANDROID_PRODUCT_TYPE_UNKNOWN = "unknown"
     55 
     56 # Target-side directory where the VTS binaries are uploaded
     57 DEFAULT_AGENT_BASE_DIR = "/data/local/tmp"
     58 # Time for which the current is put on sleep when the client is unable to
     59 # make a connection.
     60 THREAD_SLEEP_TIME = 1
     61 # Max number of attempts that the client can make to connect to the agent
     62 MAX_AGENT_CONNECT_RETRIES = 10
     63 
     64 
     65 class AndroidDeviceError(signals.ControllerError):
     66     pass
     67 
     68 
     69 def create(configs, start_services=True):
     70     """Creates AndroidDevice controller objects.
     71 
     72     Args:
     73         configs: A list of dicts, each representing a configuration for an
     74                  Android device.
     75         start_services: boolean, controls whether services will be started.
     76 
     77     Returns:
     78         A list of AndroidDevice objects.
     79     """
     80     if not configs:
     81         raise AndroidDeviceError(ANDROID_DEVICE_EMPTY_CONFIG_MSG)
     82     elif configs == ANDROID_DEVICE_PICK_ALL_TOKEN:
     83         ads = get_all_instances()
     84     elif not isinstance(configs, list):
     85         raise AndroidDeviceError(ANDROID_DEVICE_NOT_LIST_CONFIG_MSG)
     86     elif isinstance(configs[0], str):
     87         # Configs is a list of serials.
     88         ads = get_instances(configs)
     89     else:
     90         # Configs is a list of dicts.
     91         ads = get_instances_with_configs(configs)
     92     connected_ads = list_adb_devices()
     93     for ad in ads:
     94         if ad.serial not in connected_ads:
     95             raise DoesNotExistError(("Android device %s is specified in config"
     96                                      " but is not attached.") % ad.serial)
     97     if start_services:
     98         _startServicesOnAds(ads)
     99     return ads
    100 
    101 
    102 def destroy(ads):
    103     """Cleans up AndroidDevice objects.
    104 
    105     Args:
    106         ads: A list of AndroidDevice objects.
    107     """
    108     for ad in ads:
    109         try:
    110             ad.cleanUp()
    111         except:
    112             ad.log.exception("Failed to clean up properly.")
    113 
    114 
    115 def _startServicesOnAds(ads):
    116     """Starts long running services on multiple AndroidDevice objects.
    117 
    118     If any one AndroidDevice object fails to start services, cleans up all
    119     existing AndroidDevice objects and their services.
    120 
    121     Args:
    122         ads: A list of AndroidDevice objects whose services to start.
    123     """
    124     running_ads = []
    125     for ad in ads:
    126         running_ads.append(ad)
    127         try:
    128             ad.startServices()
    129         except:
    130             ad.log.exception("Failed to start some services, abort!")
    131             destroy(running_ads)
    132             raise
    133 
    134 
    135 def _parse_device_list(device_list_str, key):
    136     """Parses a byte string representing a list of devices. The string is
    137     generated by calling either adb or fastboot.
    138 
    139     Args:
    140         device_list_str: Output of adb or fastboot.
    141         key: The token that signifies a device in device_list_str.
    142 
    143     Returns:
    144         A list of android device serial numbers.
    145     """
    146     clean_lines = str(device_list_str, 'utf-8').strip().split('\n')
    147     results = []
    148     for line in clean_lines:
    149         tokens = line.strip().split('\t')
    150         if len(tokens) == 2 and tokens[1] == key:
    151             results.append(tokens[0])
    152     return results
    153 
    154 
    155 def list_adb_devices():
    156     """List all target devices connected to the host and detected by adb.
    157 
    158     Returns:
    159         A list of android device serials. Empty if there's none.
    160     """
    161     out = adb.AdbProxy().devices()
    162     return _parse_device_list(out, "device")
    163 
    164 
    165 def list_fastboot_devices():
    166     """List all android devices connected to the computer that are in in
    167     fastboot mode. These are detected by fastboot.
    168 
    169     Returns:
    170         A list of android device serials. Empty if there's none.
    171     """
    172     out = fastboot.FastbootProxy().devices()
    173     return _parse_device_list(out, "fastboot")
    174 
    175 
    176 def get_instances(serials):
    177     """Create AndroidDevice instances from a list of serials.
    178 
    179     Args:
    180         serials: A list of android device serials.
    181 
    182     Returns:
    183         A list of AndroidDevice objects.
    184     """
    185     results = []
    186     for s in serials:
    187         results.append(AndroidDevice(s))
    188     return results
    189 
    190 
    191 def get_instances_with_configs(configs):
    192     """Create AndroidDevice instances from a list of json configs.
    193 
    194     Each config should have the required key-value pair "serial".
    195 
    196     Args:
    197         configs: A list of dicts each representing the configuration of one
    198             android device.
    199 
    200     Returns:
    201         A list of AndroidDevice objects.
    202     """
    203     results = []
    204     for c in configs:
    205         try:
    206             serial = c.pop(keys.ConfigKeys.IKEY_SERIAL)
    207         except KeyError:
    208             raise AndroidDeviceError(('Required value %s is missing in '
    209                                       'AndroidDevice config %s.') %
    210                                      (keys.ConfigKeys.IKEY_SERIAL, c))
    211         try:
    212             product_type = c.pop(keys.ConfigKeys.IKEY_PRODUCT_TYPE)
    213         except KeyError:
    214             logging.error('Required value %s is missing in '
    215                           'AndroidDevice config %s.',
    216                           keys.ConfigKeys.IKEY_PRODUCT_TYPE, c)
    217             product_type = ANDROID_PRODUCT_TYPE_UNKNOWN
    218 
    219         ad = AndroidDevice(serial, product_type)
    220         ad.loadConfig(c)
    221         results.append(ad)
    222     return results
    223 
    224 
    225 def get_all_instances(include_fastboot=False):
    226     """Create AndroidDevice instances for all attached android devices.
    227 
    228     Args:
    229         include_fastboot: Whether to include devices in bootloader mode or not.
    230 
    231     Returns:
    232         A list of AndroidDevice objects each representing an android device
    233         attached to the computer.
    234     """
    235     if include_fastboot:
    236         serial_list = list_adb_devices() + list_fastboot_devices()
    237         return get_instances(serial_list)
    238     return get_instances(list_adb_devices())
    239 
    240 
    241 def filter_devices(ads, func):
    242     """Finds the AndroidDevice instances from a list that match certain
    243     conditions.
    244 
    245     Args:
    246         ads: A list of AndroidDevice instances.
    247         func: A function that takes an AndroidDevice object and returns True
    248             if the device satisfies the filter condition.
    249 
    250     Returns:
    251         A list of AndroidDevice instances that satisfy the filter condition.
    252     """
    253     results = []
    254     for ad in ads:
    255         if func(ad):
    256             results.append(ad)
    257     return results
    258 
    259 
    260 def get_device(ads, **kwargs):
    261     """Finds a unique AndroidDevice instance from a list that has specific
    262     attributes of certain values.
    263 
    264     Example:
    265         get_device(android_devices, label="foo", phone_number="1234567890")
    266         get_device(android_devices, model="angler")
    267 
    268     Args:
    269         ads: A list of AndroidDevice instances.
    270         kwargs: keyword arguments used to filter AndroidDevice instances.
    271 
    272     Returns:
    273         The target AndroidDevice instance.
    274 
    275     Raises:
    276         AndroidDeviceError is raised if none or more than one device is
    277         matched.
    278     """
    279 
    280     def _get_device_filter(ad):
    281         for k, v in kwargs.items():
    282             if not hasattr(ad, k):
    283                 return False
    284             elif getattr(ad, k) != v:
    285                 return False
    286         return True
    287 
    288     filtered = filter_devices(ads, _get_device_filter)
    289     if not filtered:
    290         raise AndroidDeviceError(("Could not find a target device that matches"
    291                                   " condition: %s.") % kwargs)
    292     elif len(filtered) == 1:
    293         return filtered[0]
    294     else:
    295         serials = [ad.serial for ad in filtered]
    296         raise AndroidDeviceError("More than one device matched: %s" % serials)
    297 
    298 
    299 def takeBugReports(ads, test_name, begin_time):
    300     """Takes bug reports on a list of android devices.
    301 
    302     If you want to take a bug report, call this function with a list of
    303     android_device objects in on_fail. But reports will be taken on all the
    304     devices in the list concurrently. Bug report takes a relative long
    305     time to take, so use this cautiously.
    306 
    307     Args:
    308         ads: A list of AndroidDevice instances.
    309         test_name: Name of the test case that triggered this bug report.
    310         begin_time: Logline format timestamp taken when the test started.
    311     """
    312     begin_time = vts_logger.normalizeLogLineTimestamp(begin_time)
    313 
    314     def take_br(test_name, begin_time, ad):
    315         ad.takeBugReport(test_name, begin_time)
    316 
    317     args = [(test_name, begin_time, ad) for ad in ads]
    318     utils.concurrent_exec(take_br, args)
    319 
    320 
    321 class AndroidDevice(object):
    322     """Class representing an android device.
    323 
    324     Each object of this class represents one Android device. The object holds
    325     handles to adb, fastboot, and various RPC clients.
    326 
    327     Attributes:
    328         serial: A string that's the serial number of the Android device.
    329         device_command_port: int, the port number used on the Android device
    330                 for adb port forwarding (for command-response sessions).
    331         device_callback_port: int, the port number used on the Android device
    332                 for adb port reverse forwarding (for callback sessions).
    333                 Set -1 if callback is not needed (e.g., when this class is used
    334                 as an adb library).
    335         log: A logger project with a device-specific prefix for each line -
    336              [AndroidDevice|<serial>]
    337         log_path: A string that is the path where all logs collected on this
    338                   android device should be stored.
    339         adb_logcat_process: A process that collects the adb logcat.
    340         adb_logcat_file_path: A string that's the full path to the adb logcat
    341                               file collected, if any.
    342         vts_agent_process: A process that runs the HAL agent.
    343         adb: An AdbProxy object used for interacting with the device via adb.
    344         fastboot: A FastbootProxy object used for interacting with the device
    345                   via fastboot.
    346         customflasher: A CustomFlasherProxy object used for interacting with
    347                        the device via user defined flashing binary.
    348         enable_vts_agent: bool, whether VTS agent is used.
    349         enable_sl4a: bool, whether SL4A is used.
    350         enable_sl4a_ed: bool, whether SL4A Event Dispatcher is used.
    351         host_command_port: the host-side port for runner to agent sessions
    352                            (to send commands and receive responses).
    353         host_callback_port: the host-side port for agent to runner sessions
    354                             (to get callbacks from agent).
    355         hal: HalMirror, in charge of all communications with the HAL layer.
    356         lib: LibMirror, in charge of all communications with static and shared
    357              native libs.
    358         shell: ShellMirror, in charge of all communications with shell.
    359         _product_type: A string, the device product type (e.g., bullhead) if
    360                        known, ANDROID_PRODUCT_TYPE_UNKNOWN otherwise.
    361     """
    362 
    363     def __init__(self,
    364                  serial="",
    365                  product_type=ANDROID_PRODUCT_TYPE_UNKNOWN,
    366                  device_callback_port=5010):
    367         self.serial = serial
    368         self._product_type = product_type
    369         self.device_command_port = None
    370         self.device_callback_port = device_callback_port
    371         self.log = AndroidDeviceLoggerAdapter(logging.getLogger(),
    372                                               {"serial": self.serial})
    373         base_log_path = getattr(logging, "log_path", "/tmp/logs/")
    374         self.log_path = os.path.join(base_log_path, "AndroidDevice%s" % serial)
    375         self.adb_logcat_process = None
    376         self.adb_logcat_file_path = None
    377         self.vts_agent_process = None
    378         self.adb = adb.AdbProxy(serial)
    379         self.fastboot = fastboot.FastbootProxy(serial)
    380         self.customflasher = customflasher.CustomFlasherProxy(serial)
    381         if not self.isBootloaderMode:
    382             self.rootAdb()
    383         self.host_command_port = None
    384         self.host_callback_port = adb.get_available_host_port()
    385         if self.device_callback_port >= 0:
    386             self.adb.reverse_tcp_forward(self.device_callback_port,
    387                                          self.host_callback_port)
    388         self.hal = None
    389         self.lib = None
    390         self.shell = None
    391         self.sl4a_host_port = None
    392         # TODO: figure out a good way to detect which port is available
    393         # on the target side, instead of hard coding a port number.
    394         self.sl4a_target_port = 8082
    395 
    396     def __del__(self):
    397         self.cleanUp()
    398 
    399     def SetCustomFlasherPath(self, customflasher_path):
    400         """Sets customflasher path to use to flash the device.
    401 
    402         Args:
    403             customflasher_path: string, path to user-spcified flash binary.
    404         """
    405         self.customflasher.SetCustomBinaryPath(customflasher_path)
    406 
    407     def cleanUp(self):
    408         """Cleans up the AndroidDevice object and releases any resources it
    409         claimed.
    410         """
    411         self.stopServices()
    412         if self.host_command_port:
    413             self.adb.forward("--remove tcp:%s" % self.host_command_port)
    414             self.host_command_port = None
    415         if self.sl4a_host_port:
    416             self.adb.forward("--remove tcp:%s" % self.sl4a_host_port)
    417             self.sl4a_host_port = None
    418 
    419     @property
    420     def isBootloaderMode(self):
    421         """True if the device is in bootloader mode."""
    422         return self.serial in list_fastboot_devices()
    423 
    424     @property
    425     def isAdbRoot(self):
    426         """True if adb is running as root for this device."""
    427         id_str = self.adb.shell("id -un").strip().decode("utf-8")
    428         return id_str == "root"
    429 
    430     @property
    431     def verityEnabled(self):
    432         """True if verity is enabled for this device."""
    433         try:
    434             verified = self.getProp("partition.system.verified")
    435             if not verified:
    436                 return False
    437         except adb.AdbError:
    438             # If verity is disabled, there is no property 'partition.system.verified'
    439             return False
    440         return True
    441 
    442     @property
    443     def model(self):
    444         """The Android code name for the device."""
    445         # If device is in bootloader mode, get mode name from fastboot.
    446         if self.isBootloaderMode:
    447             out = self.fastboot.getvar("product").strip()
    448             # "out" is never empty because of the "total time" message fastboot
    449             # writes to stderr.
    450             lines = out.decode("utf-8").split('\n', 1)
    451             if lines:
    452                 tokens = lines[0].split(' ')
    453                 if len(tokens) > 1:
    454                     return tokens[1].lower()
    455             return None
    456         model = self.getProp("ro.build.product").lower()
    457         if model == "sprout":
    458             return model
    459         else:
    460             model = self.getProp("ro.product.name").lower()
    461             return model
    462 
    463     @property
    464     def first_api_level(self):
    465         """Gets the API level that the device was initially launched with."""
    466         return self.getProp("ro.product.first_api_level")
    467 
    468     @property
    469     def sdk_version(self):
    470         """Gets the SDK version that the device is running with."""
    471         return self.getProp("ro.build.version.sdk")
    472 
    473     def getLaunchApiLevel(self, strict=True):
    474         """Gets the API level that the device was initially launched with.
    475 
    476         This method reads ro.product.first_api_level from the device. If the
    477         value is 0, it then reads ro.build.version.sdk.
    478 
    479         Args:
    480             strict: A boolean, whether to fail the test if the property is
    481                     not an integer or not defined.
    482 
    483         Returns:
    484             An integer, the API level.
    485             0 if the property is not an integer or not defined.
    486         """
    487         level_str = self.first_api_level
    488         try:
    489             level = int(level_str)
    490         except ValueError:
    491             error_msg = "Cannot parse first_api_level: %s" % level_str
    492             if strict:
    493                 asserts.fail(error_msg)
    494             logging.error(error_msg)
    495             return 0
    496 
    497         if level != 0:
    498             return level
    499 
    500         level_str = self.sdk_version
    501         try:
    502             return int(level_str)
    503         except ValueError:
    504             error_msg = "Cannot parse version.sdk: %s" % level_str
    505             if strict:
    506                 asserts.fail(error_msg)
    507             logging.error(error_msg)
    508             return 0
    509 
    510     @property
    511     def vndk_version(self):
    512         """Gets the VNDK version that the vendor partition is using."""
    513         return self.getProp("ro.vndk.version")
    514 
    515     @property
    516     def vndk_lite(self):
    517         """Checks whether the vendor partition requests lite VNDK
    518         enforcement.
    519 
    520         Returns:
    521             bool, True for lite vndk enforcement.
    522         """
    523         vndk_lite_str = self.getProp("ro.vndk.lite")
    524         if vndk_lite_str is None:
    525             logging.debug('ro.vndk.lite: %s' % vndk_lite_str)
    526             return False
    527         return vndk_lite_str.lower() == "true"
    528 
    529     @property
    530     def cpu_abi(self):
    531         """CPU ABI (Application Binary Interface) of the device."""
    532         out = self.getProp("ro.product.cpu.abi")
    533         if not out:
    534             return "unknown"
    535 
    536         cpu_abi = out.lower()
    537         return cpu_abi
    538 
    539     def getCpuAbiList(self, bitness=""):
    540         """Gets list of supported ABIs from property.
    541 
    542         Args:
    543             bitness: 32 or 64. If the argument is not specified, this method
    544                      returns both 32 and 64-bit ABIs.
    545 
    546         Returns:
    547             A list of strings, the supported ABIs.
    548         """
    549         out = self.getProp("ro.product.cpu.abilist" + str(bitness))
    550         return out.lower().split(",") if out else []
    551 
    552     @property
    553     def is64Bit(self):
    554         """True if device is 64 bit."""
    555         out = self.adb.shell('uname -m')
    556         return "64" in out
    557 
    558     @property
    559     def total_memory(self):
    560         """Total memory on device.
    561 
    562         Returns:
    563             long, total memory in bytes. -1 if cannot get memory information.
    564         """
    565         total_memory_command = 'cat /proc/meminfo | grep MemTotal'
    566         out = self.adb.shell(total_memory_command)
    567         value_unit = out.split(':')[-1].strip().split(' ')
    568 
    569         if len(value_unit) != 2:
    570             logging.error('Cannot get memory information. %s', out)
    571             return -1
    572 
    573         value, unit = value_unit
    574 
    575         try:
    576             value = int(value)
    577         except ValueError:
    578             logging.error('Unrecognized total memory value: %s', value_unit)
    579             return -1
    580 
    581         unit = unit.lower()
    582         if unit == 'kb':
    583             value *= 1024
    584         elif unit == 'mb':
    585             value *= 1024 * 1024
    586         elif unit == 'b':
    587             pass
    588         else:
    589             logging.error('Unrecognized total memory unit: %s', value_unit)
    590             return -1
    591 
    592         return value
    593 
    594     @property
    595     def libPaths(self):
    596         """List of strings representing the paths to the native library directories."""
    597         paths_32 = ["/system/lib", "/vendor/lib"]
    598         if self.is64Bit:
    599             paths_64 = ["/system/lib64", "/vendor/lib64"]
    600             paths_64.extend(paths_32)
    601             return paths_64
    602         return paths_32
    603 
    604     @property
    605     def isAdbLogcatOn(self):
    606         """Whether there is an ongoing adb logcat collection.
    607         """
    608         if self.adb_logcat_process:
    609             return True
    610         return False
    611 
    612     @property
    613     def mac_address(self):
    614         """The MAC address of the device.
    615         """
    616         try:
    617             command = 'su root cat /sys/class/net/wlan0/address'
    618             response = self.adb.shell(command)
    619             return response.strip()
    620         except adb.AdbError as e:
    621             logging.exception(e)
    622             return "unknown"
    623 
    624     @property
    625     def sim_state(self):
    626         """The SIM state of the device.
    627         """
    628         return self.getProp('gsm.sim.state')
    629 
    630     @property
    631     def sim_operator(self):
    632         """The SIM operator of the device.
    633         """
    634         return self.getProp('gsm.operator.alpha')
    635 
    636     def getKernelConfig(self, config_name):
    637         """Gets kernel config from the device.
    638 
    639         Args:
    640             config_name: A string, the name of the configuration.
    641 
    642         Returns:
    643             "y" or "m" if the config is set.
    644             "" if the config is not set.
    645             None if fails to read config.
    646         """
    647         line_prefix = config_name + "="
    648         with tempfile.NamedTemporaryFile(delete=False) as temp_file:
    649             config_path = temp_file.name
    650         try:
    651             logging.debug("Pull config.gz to %s", config_path)
    652             self.adb.pull("/proc/config.gz", config_path)
    653             with gzip.GzipFile(config_path, "rb") as config_file:
    654                 for line in config_file:
    655                     if line.strip().startswith(line_prefix):
    656                         logging.debug("Found config: %s", line)
    657                         return line.strip()[len(line_prefix):]
    658             logging.debug("%s is not set.", config_name)
    659             return ""
    660         except (adb.AdbError, IOError) as e:
    661             logging.exception("Cannot read kernel config.", e)
    662             return None
    663         finally:
    664             os.remove(config_path)
    665 
    666     def getBinderBitness(self):
    667         """Returns the value of BINDER_IPC_32BIT in kernel config.
    668 
    669         Returns:
    670             32 or 64, binder bitness of the device.
    671             None if fails to read config.
    672         """
    673         config_value = self.getKernelConfig("CONFIG_ANDROID_BINDER_IPC_32BIT")
    674         if config_value is None:
    675             return None
    676         elif config_value:
    677             return 32
    678         else:
    679             return 64
    680 
    681     def loadConfig(self, config):
    682         """Add attributes to the AndroidDevice object based on json config.
    683 
    684         Args:
    685             config: A dictionary representing the configs.
    686 
    687         Raises:
    688             AndroidDeviceError is raised if the config is trying to overwrite
    689             an existing attribute.
    690         """
    691         for k, v in config.items():
    692             if hasattr(self, k):
    693                 raise AndroidDeviceError(
    694                     "Attempting to set existing attribute %s on %s" %
    695                     (k, self.serial))
    696             setattr(self, k, v)
    697 
    698     def rootAdb(self):
    699         """Changes adb to root mode for this device."""
    700         if not self.isAdbRoot:
    701             try:
    702                 self.adb.root()
    703                 self.adb.wait_for_device()
    704                 self.adb.remount()
    705                 self.adb.wait_for_device()
    706             except adb.AdbError as e:
    707                 # adb wait-for-device is not always possible in the lab
    708                 # continue with an assumption it's done by the harness.
    709                 logging.exception(e)
    710 
    711     def startAdbLogcat(self):
    712         """Starts a standing adb logcat collection in separate subprocesses and
    713         save the logcat in a file.
    714         """
    715         if self.isAdbLogcatOn:
    716             raise AndroidDeviceError(("Android device %s already has an adb "
    717                                       "logcat thread going on. Cannot start "
    718                                       "another one.") % self.serial)
    719         f_name = "adblog_%s_%s.txt" % (self.model, self.serial)
    720         utils.create_dir(self.log_path)
    721         logcat_file_path = os.path.join(self.log_path, f_name)
    722         try:
    723             extra_params = self.adb_logcat_param
    724         except AttributeError:
    725             extra_params = "-b all"
    726         cmd = "adb -s %s logcat -v threadtime %s >> %s" % (self.serial,
    727                                                            extra_params,
    728                                                            logcat_file_path)
    729         self.adb_logcat_process = utils.start_standing_subprocess(cmd)
    730         self.adb_logcat_file_path = logcat_file_path
    731 
    732     def stopAdbLogcat(self):
    733         """Stops the adb logcat collection subprocess.
    734         """
    735         if not self.isAdbLogcatOn:
    736             raise AndroidDeviceError(
    737                 "Android device %s does not have an ongoing adb logcat collection."
    738                 % self.serial)
    739         try:
    740             utils.stop_standing_subprocess(self.adb_logcat_process)
    741         except utils.VTSUtilsError as e:
    742             logging.error("Cannot stop adb logcat. %s", e)
    743         self.adb_logcat_process = None
    744 
    745     def takeBugReport(self, test_name, begin_time):
    746         """Takes a bug report on the device and stores it in a file.
    747 
    748         Args:
    749             test_name: Name of the test case that triggered this bug report.
    750             begin_time: Logline format timestamp taken when the test started.
    751         """
    752         br_path = os.path.join(self.log_path, "BugReports")
    753         utils.create_dir(br_path)
    754         base_name = ",%s,%s.txt" % (begin_time, self.serial)
    755         test_name_len = utils.MAX_FILENAME_LEN - len(base_name)
    756         out_name = test_name[:test_name_len] + base_name
    757         full_out_path = os.path.join(br_path, out_name.replace(' ', '\ '))
    758         self.log.info("Taking bugreport for %s on %s", test_name, self.serial)
    759         self.adb.bugreport(" > %s" % full_out_path)
    760         self.log.info("Bugreport for %s taken at %s", test_name, full_out_path)
    761 
    762     def waitForBootCompletion(self, timeout=900):
    763         """Waits for Android framework to broadcast ACTION_BOOT_COMPLETED.
    764 
    765         Args:
    766             timeout: int, seconds to wait for boot completion. Default is
    767                      15 minutes.
    768 
    769         Returns:
    770             bool, True if boot completed. False if any error or timeout
    771         """
    772         start = time.time()
    773         try:
    774             self.adb.wait_for_device(timeout=timeout)
    775         except adb.AdbError as e:
    776             # adb wait-for-device is not always possible in the lab
    777             logging.exception(e)
    778             return False
    779 
    780         while not self.isBootCompleted():
    781             if time.time() - start >= timeout:
    782                 logging.error("Timeout while waiting for boot completion.")
    783                 return False
    784             time.sleep(1)
    785 
    786         return True
    787 
    788     # Deprecated. Use isBootCompleted instead
    789     def hasBooted(self):
    790         """Checks whether the device has booted.
    791 
    792         Returns:
    793             True if booted, False otherwise.
    794         """
    795         return self.isBootCompleted()
    796 
    797     def isBootCompleted(self):
    798         """Checks whether the device has booted.
    799 
    800         Returns:
    801             True if booted, False otherwise.
    802         """
    803         try:
    804             completed = self.getProp("sys.boot_completed")
    805             if completed == '1':
    806                 return True
    807         except adb.AdbError:
    808             # adb shell calls may fail during certain period of booting
    809             # process, which is normal. Ignoring these errors.
    810             pass
    811 
    812         return False
    813 
    814     def isFrameworkRunning(self, check_boot_completion=True):
    815         """Checks whether Android framework is started.
    816 
    817         This function will first check boot_completed prop. If boot_completed
    818         is 0, then return False meaning framework not started.
    819         Then this function will check whether system_server process is running.
    820         If yes, then return True meaning framework is started.
    821 
    822         The assumption here is if prop boot_completed is 0 then framework
    823         is stopped.
    824 
    825         There are still cases which can make this function return wrong
    826         result. For example, boot_completed is set to 0 manually without
    827         without stopping framework.
    828 
    829         Args:
    830             check_boot_completion: bool, whether to check boot completion
    831                                    before checking framework status. This is an
    832                                    important step for ensuring framework is
    833                                    started. Under most circumstances this value
    834                                    should be set to True.
    835                                    Default True.
    836 
    837         Returns:
    838             True if started, False otherwise.
    839         """
    840         # First, check whether boot has completed.
    841         if check_boot_completion and not self.isBootCompleted():
    842             return False
    843 
    844         cmd = 'ps -g system | grep system_server'
    845         res = self.adb.shell(cmd)
    846 
    847         return 'system_server' in res
    848 
    849     def startFramework(self,
    850                        wait_for_completion=True,
    851                        wait_for_completion_timeout=120):
    852         """Starts Android framework.
    853 
    854         By default this function will wait for framework starting process to
    855         finish before returning.
    856 
    857         Args:
    858             wait_for_completion: bool, whether to wait for framework to complete
    859                                  starting. Default: True
    860             wait_for_completion_timeout: timeout in seconds for waiting framework
    861                                  to start. Default: 2 minutes
    862 
    863         Returns:
    864             bool, True if framework start success. False otherwise.
    865         """
    866         logging.info("starting Android framework")
    867         self.adb.shell("start")
    868 
    869         if wait_for_completion:
    870             return self.waitForFrameworkStartComplete(wait_for_completion_timeout)
    871 
    872         return True
    873 
    874     def start(self):
    875         """Starts Android framework and waits for ACTION_BOOT_COMPLETED.
    876 
    877         Returns:
    878             bool, True if framework start success. False otherwise.
    879         """
    880         return self.startFramework()
    881 
    882     def stopFramework(self):
    883         """Stops Android framework.
    884 
    885         Method will block until stop is complete.
    886         """
    887         logging.info("stopping Android framework")
    888         self.adb.shell("stop")
    889         self.setProp("sys.boot_completed", 0)
    890         logging.info("Android framework stopped")
    891 
    892     def stop(self):
    893         """Stops Android framework.
    894 
    895         Method will block until stop is complete.
    896         """
    897         self.stopFramework()
    898 
    899     def waitForFrameworkStartComplete(self, timeout_secs=120):
    900         """Wait for Android framework to complete starting.
    901 
    902         Args:
    903             timeout_secs: int, seconds to wait for boot completion. Default is
    904                           2 minutes.
    905 
    906         Returns:
    907             bool, True if framework is started. False otherwise or timeout
    908         """
    909         start = time.time()
    910 
    911         # First, wait for boot completion and checks
    912         self.waitForBootCompletion(timeout_secs)
    913 
    914         while not self.isFrameworkRunning(check_boot_completion=False):
    915             if time.time() - start >= timeout_secs:
    916                 logging.error("Timeout while waiting for framework to start.")
    917                 return False
    918             time.sleep(1)
    919 
    920         return True
    921 
    922     def setProp(self, name, value):
    923         """Calls setprop shell command.
    924 
    925         Args:
    926             name: string, the name of a system property to set
    927             value: any type, value will be converted to string. Quotes in value
    928                    is not supported at this time; if value contains a quote,
    929                    this method will log an error and return.
    930 
    931         Raises:
    932             AdbError, if name contains invalid character
    933         """
    934         if name is None or value is None:
    935             logging.error("name or value of system property "
    936                           "should not be None. No property is set.")
    937             return
    938 
    939         value = str(value)
    940 
    941         if "'" in value or "\"" in value:
    942             logging.error("Quotes in value of system property "
    943                           "is not yet supported. No property is set.")
    944             return
    945 
    946         self.adb.shell("setprop %s \"%s\"" % (name, value))
    947 
    948     def getProp(self, name):
    949         """Calls getprop shell command.
    950 
    951         Args:
    952             name: string, the name of a system property to get
    953 
    954         Returns:
    955             string, value of the property. If name does not exist; an empty
    956             string will be returned. decode("utf-8") and strip() will be called
    957             on the output before returning; None will be returned if input
    958             name is None
    959 
    960         Raises:
    961             AdbError, if name contains invalid character
    962         """
    963         if name is None:
    964             logging.error("name of system property should not be None.")
    965             return None
    966 
    967         out = self.adb.shell("getprop %s" % name)
    968         return out.decode("utf-8").strip()
    969 
    970     def reboot(self, restart_services=True):
    971         """Reboots the device and wait for device to complete booting.
    972 
    973         This is probably going to print some error messages in console. Only
    974         use if there's no other option.
    975 
    976         Raises:
    977             AndroidDeviceError is raised if waiting for completion timed
    978             out.
    979         """
    980         if self.isBootloaderMode:
    981             self.fastboot.reboot()
    982             return
    983 
    984         if restart_services:
    985             has_adb_log = self.isAdbLogcatOn
    986             has_vts_agent = True if self.vts_agent_process else False
    987             if has_adb_log:
    988                 self.stopAdbLogcat()
    989             if has_vts_agent:
    990                 self.stopVtsAgent()
    991 
    992         self.adb.reboot()
    993         self.waitForBootCompletion()
    994         self.rootAdb()
    995 
    996         if restart_services:
    997             if has_adb_log:
    998                 self.startAdbLogcat()
    999             if has_vts_agent:
   1000                 self.startVtsAgent()
   1001 
   1002     def startServices(self):
   1003         """Starts long running services on the android device.
   1004 
   1005         1. Start adb logcat capture.
   1006         2. Start VtsAgent and create HalMirror unless disabled in config.
   1007         3. If enabled in config, start sl4a service and create sl4a clients.
   1008         """
   1009         self.enable_vts_agent = getattr(self, "enable_vts_agent", True)
   1010         self.enable_sl4a = getattr(self, "enable_sl4a", False)
   1011         self.enable_sl4a_ed = getattr(self, "enable_sl4a_ed", False)
   1012         try:
   1013             self.startAdbLogcat()
   1014         except:
   1015             self.log.exception("Failed to start adb logcat!")
   1016             raise
   1017         if self.enable_vts_agent:
   1018             self.startVtsAgent()
   1019             self.device_command_port = int(
   1020                 self.adb.shell("cat /data/local/tmp/vts_tcp_server_port"))
   1021             logging.info("device_command_port: %s", self.device_command_port)
   1022             if not self.host_command_port:
   1023                 self.host_command_port = adb.get_available_host_port()
   1024             self.adb.tcp_forward(self.host_command_port,
   1025                                  self.device_command_port)
   1026             self.hal = mirror_tracker.MirrorTracker(
   1027                 self.host_command_port, self.host_callback_port, True)
   1028             self.lib = mirror_tracker.MirrorTracker(self.host_command_port)
   1029             self.shell = mirror_tracker.MirrorTracker(
   1030                 host_command_port=self.host_command_port, adb=self.adb)
   1031         if self.enable_sl4a:
   1032             try:
   1033                 self.startSl4aClient(eself.enable_sl4a_ed)
   1034             except Exception as e:
   1035                 self.log.exception("Failed to start SL4A!")
   1036                 self.log.exception(e)
   1037                 raise
   1038 
   1039     def stopServices(self):
   1040         """Stops long running services on the android device.
   1041         """
   1042         if self.adb_logcat_process:
   1043             self.stopAdbLogcat()
   1044         if getattr(self, "enable_sl4a", False):
   1045             self._terminateAllSl4aSessions()
   1046             self.stopSl4a()
   1047         if getattr(self, "enable_vts_agent", True):
   1048             self.stopVtsAgent()
   1049         if self.hal:
   1050             self.hal.CleanUp()
   1051 
   1052     def startVtsAgent(self):
   1053         """Start HAL agent on the AndroidDevice.
   1054 
   1055         This function starts the target side native agent and is persisted
   1056         throughout the test run.
   1057         """
   1058         self.log.info("Starting VTS agent")
   1059         if self.vts_agent_process:
   1060             raise AndroidDeviceError(
   1061                 "HAL agent is already running on %s." % self.serial)
   1062 
   1063         cleanup_commands = [
   1064             "rm -f /data/local/tmp/vts_driver_*",
   1065             "rm -f /data/local/tmp/vts_agent_callback*"
   1066         ]
   1067         kill_commands = [
   1068             "killall vts_hal_agent32", "killall vts_hal_agent64",
   1069             "killall vts_hal_driver32", "killall vts_hal_driver64",
   1070             "killall vts_shell_driver32", "killall vts_shell_driver64"
   1071         ]
   1072         cleanup_commands.extend(kill_commands)
   1073         chmod_commands = [
   1074             "chmod 755 %s/32/vts_hal_agent32" % DEFAULT_AGENT_BASE_DIR,
   1075             "chmod 755 %s/64/vts_hal_agent64" % DEFAULT_AGENT_BASE_DIR,
   1076             "chmod 755 %s/32/vts_hal_driver32" % DEFAULT_AGENT_BASE_DIR,
   1077             "chmod 755 %s/64/vts_hal_driver64" % DEFAULT_AGENT_BASE_DIR,
   1078             "chmod 755 %s/32/vts_shell_driver32" % DEFAULT_AGENT_BASE_DIR,
   1079             "chmod 755 %s/64/vts_shell_driver64" % DEFAULT_AGENT_BASE_DIR
   1080         ]
   1081         cleanup_commands.extend(chmod_commands)
   1082         for cmd in cleanup_commands:
   1083             try:
   1084                 self.adb.shell(cmd)
   1085             except adb.AdbError as e:
   1086                 self.log.warning(
   1087                     "A command to setup the env to start the VTS Agent failed %s",
   1088                     e)
   1089         log_severity = getattr(self, keys.ConfigKeys.KEY_LOG_SEVERITY, "INFO")
   1090         bits = ['64', '32'] if self.is64Bit else ['32']
   1091         for bitness in bits:
   1092             vts_agent_log_path = os.path.join(self.log_path,
   1093                                               "vts_agent_" + bitness + ".log")
   1094             cmd = ('adb -s {s} shell LD_LIBRARY_PATH={path}/{bitness} '
   1095                    '{path}/{bitness}/vts_hal_agent{bitness} '
   1096                    '--hal_driver_path_32={path}/32/vts_hal_driver32 '
   1097                    '--hal_driver_path_64={path}/64/vts_hal_driver64 '
   1098                    '--spec_dir={path}/spec '
   1099                    '--shell_driver_path_32={path}/32/vts_shell_driver32 '
   1100                    '--shell_driver_path_64={path}/64/vts_shell_driver64 '
   1101                    '-l {severity} >> {log} 2>&1').format(
   1102                        s=self.serial,
   1103                        bitness=bitness,
   1104                        path=DEFAULT_AGENT_BASE_DIR,
   1105                        log=vts_agent_log_path,
   1106                        severity=log_severity)
   1107             try:
   1108                 self.vts_agent_process = utils.start_standing_subprocess(
   1109                     cmd, check_health_delay=1)
   1110                 break
   1111             except utils.VTSUtilsError as e:
   1112                 logging.exception(e)
   1113                 with open(vts_agent_log_path, 'r') as log_file:
   1114                     logging.error("VTS agent output:\n")
   1115                     logging.error(log_file.read())
   1116                 # one common cause is that 64-bit executable is not supported
   1117                 # in low API level devices.
   1118                 if bitness == '32':
   1119                     raise
   1120                 else:
   1121                     logging.error('retrying using a 32-bit binary.')
   1122 
   1123     def stopVtsAgent(self):
   1124         """Stop the HAL agent running on the AndroidDevice.
   1125         """
   1126         if not self.vts_agent_process:
   1127             return
   1128         try:
   1129             utils.stop_standing_subprocess(self.vts_agent_process)
   1130         except utils.VTSUtilsError as e:
   1131             logging.error("Cannot stop VTS agent. %s", e)
   1132         self.vts_agent_process = None
   1133 
   1134     @property
   1135     def product_type(self):
   1136         """Gets the product type name."""
   1137         return self._product_type
   1138 
   1139     # Code for using SL4A client
   1140     def startSl4aClient(self, handle_event=True):
   1141         """Create an sl4a connection to the device.
   1142 
   1143         Return the connection handler 'droid'. By default, another connection
   1144         on the same session is made for EventDispatcher, and the dispatcher is
   1145         returned to the caller as well.
   1146         If sl4a server is not started on the device, try to start it.
   1147 
   1148         Args:
   1149             handle_event: True if this droid session will need to handle
   1150                           events.
   1151         """
   1152         self._sl4a_sessions = {}
   1153         self._sl4a_event_dispatchers = {}
   1154 
   1155         for i in range(PORT_RETRY_COUNT):
   1156             try:
   1157                 if self.isRogueSl4aRunning():
   1158                     self.log.info("Stop rogue sl4a")
   1159                     self.stopSl4a()
   1160                     time.sleep(15)
   1161                 sl4a_client.start_sl4a(
   1162                     self.adb, device_side_port=self.sl4a_target_port)
   1163                 time.sleep(5)
   1164 
   1165                 self.setupSl4aPort()
   1166                 droid = self._createNewSl4aSession()
   1167                 if handle_event:
   1168                     ed = self._getSl4aEventDispatcher(droid)
   1169                     ed.start()
   1170                 break
   1171             except sl4a_client.Error as e:
   1172                 logging.exception("error: %s", e)
   1173 
   1174     def setupSl4aPort(self):
   1175         forward_success = False
   1176         last_error = None
   1177         for _ in range(PORT_RETRY_COUNT):
   1178             if not self.sl4a_host_port or not adb.is_port_available(
   1179                     self.sl4a_host_port):
   1180                 self.sl4a_host_port = adb.get_available_host_port()
   1181             logging.info("sl4a port host %s target %s", self.sl4a_host_port,
   1182                          self.sl4a_target_port)
   1183             try:
   1184                 self.adb.tcp_forward(self.sl4a_host_port,
   1185                                      self.sl4a_target_port)
   1186                 forward_success = True
   1187                 break
   1188             except adb.AdbError as e:
   1189                 last_error = e
   1190                 pass
   1191         if not forward_success:
   1192             self.log.error(last_error)
   1193             raise last_error
   1194 
   1195     def stopSl4a(self):
   1196         """Stops an SL4A apk on a target device."""
   1197         try:
   1198             self.adb.shell("am force-stop %s" % SL4A_APK_NAME)
   1199         except adb.AdbError as e:
   1200             self.log.warn("Fail to stop package %s: %s", SL4A_APK_NAME, e)
   1201 
   1202     def getPackagePid(self, package_name):
   1203         """Gets the pid for a given package. Returns None if not running.
   1204 
   1205         Args:
   1206             package_name: The name of the package.
   1207 
   1208         Returns:
   1209             The first pid found under a given package name. None if no process
   1210             was found running the package.
   1211 
   1212         Raises:
   1213             AndroidDeviceError if the output of the phone's process list was
   1214             in an unexpected format.
   1215         """
   1216         for cmd in ("ps -A", "ps"):
   1217             try:
   1218                 out = self.adb.shell('%s | grep "S %s"' % (cmd, package_name))
   1219                 if package_name not in out:
   1220                     continue
   1221                 try:
   1222                     pid = int(out.split()[1])
   1223                     self.log.info('apk %s has pid %s.', package_name, pid)
   1224                     return pid
   1225                 except (IndexError, ValueError) as e:
   1226                     # Possible ValueError from string to int cast.
   1227                     # Possible IndexError from split.
   1228                     self.log.warn('Command \"%s\" returned output line: '
   1229                                   '\"%s\".\nError: %s', cmd, out, e)
   1230             except Exception as e:
   1231                 self.log.warn(
   1232                     'Device fails to check if %s running with \"%s\"\n'
   1233                     'Exception %s', package_name, cmd, e)
   1234         self.log.debug("apk %s is not running", package_name)
   1235         return None
   1236 
   1237     def isRogueSl4aRunning(self):
   1238         """Returns true if SL4A was started by a process other than ACTS.
   1239 
   1240         If SL4A is started by a process other than ACTS, the port will be set to
   1241         something other than sl4a_client.DEFAULT_DEVICE_SIDE_PORT. This causes
   1242         SL4A to be up and running, but nearly impossible to talk to.
   1243         """
   1244         sl4a_pid = self.getPackagePid(SL4A_APK_NAME)
   1245         if sl4a_pid is not None:
   1246             sl4a_port_hex = '{0:02x}'.format(
   1247                 sl4a_client.DEFAULT_DEVICE_SIDE_PORT).upper()
   1248             port_is_open = (
   1249                 # Get the tcp info
   1250                 'cat /proc/%s/net/tcp | '
   1251                 # Remove the space padding
   1252                 'tr -s " " | '
   1253                 # Grab the 4th column (rem_address)
   1254                 'cut -d " " -f 4 | '
   1255                 # Grab the port from that address
   1256                 'cut -d ":" -f 2 | '
   1257                 # Find the port we are looking for
   1258                 'grep %s')
   1259             # If the resulting string from the command is empty, SL4A does not
   1260             # have a port open for ACTS to listen to.
   1261             return not bool(
   1262                 self.adb.shell(port_is_open % (sl4a_pid, sl4a_port_hex)))
   1263         return False
   1264 
   1265     @property
   1266     def droid(self):
   1267         """The default SL4A session to the device if exist, None otherwise."""
   1268         if not hasattr(self,
   1269                        "_sl4a_sessions") or len(self._sl4a_sessions) == 0:
   1270             return None
   1271         try:
   1272             session_id = sorted(self._sl4a_sessions)[0]
   1273             result = self._sl4a_sessions[session_id][0]
   1274             logging.info("key %s val %s", session_id, result)
   1275             return result
   1276         except IndexError as e:
   1277             logging.exception(e)
   1278             return None
   1279 
   1280     @property
   1281     def droids(self):
   1282         """A list of the active SL4A sessions on this device."""
   1283         if not hasattr(self,
   1284                        "_sl4a_sessions") or len(self._sl4a_sessions) == 0:
   1285             return None
   1286         keys = sorted(self._sl4a_sessions)
   1287         results = []
   1288         for key in keys:
   1289             results.append(self._sl4a_sessions[key][0])
   1290         return results
   1291 
   1292     @property
   1293     def ed(self):
   1294         """The default SL4A session to the device if exist, None otherwise."""
   1295         if (not hasattr(self, "_sl4a_event_dispatchers") or
   1296                 len(self._sl4a_event_dispatchers) == 0):
   1297             return None
   1298         logging.info("self._sl4a_event_dispatchers: %s",
   1299                      self._sl4a_event_dispatchers)
   1300         try:
   1301             session_id = sorted(self._sl4a_event_dispatchers)[0]
   1302             return self._sl4a_event_dispatchers[session_id]
   1303         except IndexError:
   1304             return None
   1305 
   1306     @property
   1307     def sl4a(self):
   1308         """The default SL4A session to the device if exist, None otherwise."""
   1309         try:
   1310             return self._sl4a_sessions[sorted(self._sl4a_sessions)[0]][0]
   1311         except IndexError:
   1312             return None
   1313 
   1314     @property
   1315     def sl4as(self):
   1316         """A list of the active SL4A sessions on this device.
   1317 
   1318         If multiple connections exist for the same session, only one connection
   1319         is listed.
   1320         """
   1321         keys = sorted(self._sl4a_sessions)
   1322         results = []
   1323         for key in keys:
   1324             results.append(self._sl4a_sessions[key][0])
   1325         return results
   1326 
   1327     def getVintfXml(self, use_lshal=True):
   1328         """Reads the vendor interface manifest Xml.
   1329 
   1330         Args:
   1331             use_hal: bool, set True to use lshal command and False to fetch
   1332                      manifest.xml directly.
   1333 
   1334         Returns:
   1335             Vendor interface manifest string.
   1336         """
   1337         if not use_lshal:
   1338             return None
   1339         try:
   1340             stdout = self.adb.shell('"lshal --init-vintf 2> /dev/null"')
   1341             return str(stdout)
   1342         except adb.AdbError as e:
   1343             return None
   1344 
   1345     def _getSl4aEventDispatcher(self, droid):
   1346         """Return an EventDispatcher for an sl4a session
   1347 
   1348         Args:
   1349             droid: Session to create EventDispatcher for.
   1350 
   1351         Returns:
   1352             ed: An EventDispatcher for specified session.
   1353         """
   1354         # TODO (angli): Move service-specific start/stop functions out of
   1355         # android_device, including VTS Agent, SL4A, and any other
   1356         # target-side services.
   1357         ed_key = self.serial + str(droid.uid)
   1358         if ed_key in self._sl4a_event_dispatchers:
   1359             if self._sl4a_event_dispatchers[ed_key] is None:
   1360                 raise AndroidDeviceError("EventDispatcher Key Empty")
   1361             self.log.debug("Returning existing key %s for event dispatcher!",
   1362                            ed_key)
   1363             return self._sl4a_event_dispatchers[ed_key]
   1364         event_droid = self._addNewConnectionToSl4aSession(droid.uid)
   1365         ed = event_dispatcher.EventDispatcher(event_droid)
   1366         self._sl4a_event_dispatchers[ed_key] = ed
   1367         return ed
   1368 
   1369     def _createNewSl4aSession(self):
   1370         """Start a new session in sl4a.
   1371 
   1372         Also caches the droid in a dict with its uid being the key.
   1373 
   1374         Returns:
   1375             An Android object used to communicate with sl4a on the android
   1376                 device.
   1377 
   1378         Raises:
   1379             sl4a_client.Error: Something is wrong with sl4a and it returned an
   1380             existing uid to a new session.
   1381         """
   1382         droid = sl4a_client.Sl4aClient(port=self.sl4a_host_port)
   1383         droid.open()
   1384         if droid.uid in self._sl4a_sessions:
   1385             raise sl4a_client.Error(
   1386                 "SL4A returned an existing uid for a new session. Abort.")
   1387         logging.debug("set sl4a_session[%s]", droid.uid)
   1388         self._sl4a_sessions[droid.uid] = [droid]
   1389         return droid
   1390 
   1391     def _addNewConnectionToSl4aSession(self, session_id):
   1392         """Create a new connection to an existing sl4a session.
   1393 
   1394         Args:
   1395             session_id: UID of the sl4a session to add connection to.
   1396 
   1397         Returns:
   1398             An Android object used to communicate with sl4a on the android
   1399                 device.
   1400 
   1401         Raises:
   1402             DoesNotExistError: Raised if the session it's trying to connect to
   1403             does not exist.
   1404         """
   1405         if session_id not in self._sl4a_sessions:
   1406             raise DoesNotExistError("Session %d doesn't exist." % session_id)
   1407         droid = sl4a_client.Sl4aClient(
   1408             port=self.sl4a_host_port, uid=session_id)
   1409         droid.open(cmd=sl4a_client.Sl4aCommand.CONTINUE)
   1410         return droid
   1411 
   1412     def _terminateSl4aSession(self, session_id):
   1413         """Terminate a session in sl4a.
   1414 
   1415         Send terminate signal to sl4a server; stop dispatcher associated with
   1416         the session. Clear corresponding droids and dispatchers from cache.
   1417 
   1418         Args:
   1419             session_id: UID of the sl4a session to terminate.
   1420         """
   1421         if self._sl4a_sessions and (session_id in self._sl4a_sessions):
   1422             for droid in self._sl4a_sessions[session_id]:
   1423                 droid.closeSl4aSession()
   1424                 droid.close()
   1425             del self._sl4a_sessions[session_id]
   1426         ed_key = self.serial + str(session_id)
   1427         if ed_key in self._sl4a_event_dispatchers:
   1428             self._sl4a_event_dispatchers[ed_key].clean_up()
   1429             del self._sl4a_event_dispatchers[ed_key]
   1430 
   1431     def _terminateAllSl4aSessions(self):
   1432         """Terminate all sl4a sessions on the AndroidDevice instance.
   1433 
   1434         Terminate all sessions and clear caches.
   1435         """
   1436         if hasattr(self, "_sl4a_sessions") and self._sl4a_sessions:
   1437             session_ids = list(self._sl4a_sessions.keys())
   1438             for session_id in session_ids:
   1439                 try:
   1440                     self._terminateSl4aSession(session_id)
   1441                 except:
   1442                     self.log.exception("Failed to terminate session %d.",
   1443                                        session_id)
   1444             if self.sl4a_host_port:
   1445                 self.adb.forward("--remove tcp:%d" % self.sl4a_host_port)
   1446                 self.sl4a_host_port = None
   1447 
   1448 
   1449 class AndroidDeviceLoggerAdapter(logging.LoggerAdapter):
   1450     """A wrapper class that attaches a prefix to all log lines from an
   1451     AndroidDevice object.
   1452     """
   1453 
   1454     def process(self, msg, kwargs):
   1455         """Process every log message written via the wrapped logger object.
   1456 
   1457         We are adding the prefix "[AndroidDevice|<serial>]" to all log lines.
   1458 
   1459         Args:
   1460             msg: string, the original log message.
   1461             kwargs: dict, the key value pairs that can be used to modify the
   1462                     original log message.
   1463         """
   1464         msg = "[AndroidDevice|%s] %s" % (self.extra["serial"], msg)
   1465         return (msg, kwargs)
   1466 
   1467     def warn(self, msg, *args, **kwargs):
   1468         """Function call warper for warn() to warning()."""
   1469         super(AndroidDeviceLoggerAdapter, self).warning(msg, *args, **kwargs)
   1470