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