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