1#!/usr/bin/env python3
2#
3#   Copyright 2016 - The Android Open Source Project
4#
5#   Licensed under the Apache License, Version 2.0 (the "License");
6#   you may not use this file except in compliance with the License.
7#   You may obtain a copy of the License at
8#
9#       http://www.apache.org/licenses/LICENSE-2.0
10#
11#   Unless required by applicable law or agreed to in writing, software
12#   distributed under the License is distributed on an "AS IS" BASIS,
13#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14#   See the License for the specific language governing permissions and
15#   limitations under the License.
16
17import collections
18import logging
19import math
20import os
21import re
22import socket
23import time
24from builtins import open
25from builtins import str
26from datetime import datetime
27
28from acts import context
29from acts import logger as acts_logger
30from acts import tracelogger
31from acts import utils
32from acts.controllers import adb
33from acts.controllers import fastboot
34from acts.controllers.android_lib import errors
35from acts.controllers.android_lib import events as android_events
36from acts.controllers.android_lib import logcat
37from acts.controllers.android_lib import services
38from acts.controllers.sl4a_lib import sl4a_manager
39from acts.controllers.utils_lib.ssh import connection
40from acts.controllers.utils_lib.ssh import settings
41from acts.event import event_bus
42from acts.libs.proc import job
43
44ACTS_CONTROLLER_CONFIG_NAME = "AndroidDevice"
45ACTS_CONTROLLER_REFERENCE_NAME = "android_devices"
46
47ANDROID_DEVICE_PICK_ALL_TOKEN = "*"
48# Key name for adb logcat extra params in config file.
49ANDROID_DEVICE_ADB_LOGCAT_PARAM_KEY = "adb_logcat_param"
50ANDROID_DEVICE_EMPTY_CONFIG_MSG = "Configuration is empty, abort!"
51ANDROID_DEVICE_NOT_LIST_CONFIG_MSG = "Configuration should be a list, abort!"
52CRASH_REPORT_PATHS = ("/data/tombstones/", "/data/vendor/ramdump/",
53                      "/data/ramdump/", "/data/vendor/ssrdump",
54                      "/data/vendor/ramdump/bluetooth", "/data/vendor/log/cbd")
55CRASH_REPORT_SKIPS = ("RAMDUMP_RESERVED", "RAMDUMP_STATUS", "RAMDUMP_OUTPUT",
56                      "bluetooth")
57DEFAULT_QXDM_LOG_PATH = "/data/vendor/radio/diag_logs"
58DEFAULT_SDM_LOG_PATH = "/data/vendor/slog/"
59BUG_REPORT_TIMEOUT = 1800
60PULL_TIMEOUT = 300
61PORT_RETRY_COUNT = 3
62IPERF_TIMEOUT = 60
63SL4A_APK_NAME = "com.googlecode.android_scripting"
64WAIT_FOR_DEVICE_TIMEOUT = 180
65ENCRYPTION_WINDOW = "CryptKeeper"
66DEFAULT_DEVICE_PASSWORD = "1111"
67RELEASE_ID_REGEXES = [re.compile(r'\w+\.\d+\.\d+'), re.compile(r'N\w+')]
68
69
70def create(configs):
71    """Creates AndroidDevice controller objects.
72
73    Args:
74        configs: A list of dicts, each representing a configuration for an
75                 Android device.
76
77    Returns:
78        A list of AndroidDevice objects.
79    """
80    if not configs:
81        raise errors.AndroidDeviceConfigError(ANDROID_DEVICE_EMPTY_CONFIG_MSG)
82    elif configs == ANDROID_DEVICE_PICK_ALL_TOKEN:
83        ads = get_all_instances()
84    elif not isinstance(configs, list):
85        raise errors.AndroidDeviceConfigError(
86            ANDROID_DEVICE_NOT_LIST_CONFIG_MSG)
87    elif isinstance(configs[0], str):
88        # Configs is a list of serials.
89        ads = get_instances(configs)
90    else:
91        # Configs is a list of dicts.
92        ads = get_instances_with_configs(configs)
93
94    ads[0].log.info('The primary device under test is "%s".' % ads[0].serial)
95
96    for ad in ads:
97        if not ad.is_connected():
98            raise errors.AndroidDeviceError(
99                ("Android device %s is specified in config"
100                 " but is not attached.") % ad.serial,
101                serial=ad.serial)
102    _start_services_on_ads(ads)
103    for ad in ads:
104        if ad.droid:
105            utils.set_location_service(ad, False)
106            utils.sync_device_time(ad)
107    return ads
108
109
110def destroy(ads):
111    """Cleans up AndroidDevice objects.
112
113    Args:
114        ads: A list of AndroidDevice objects.
115    """
116    for ad in ads:
117        try:
118            ad.clean_up()
119        except:
120            ad.log.exception("Failed to clean up properly.")
121
122
123def get_info(ads):
124    """Get information on a list of AndroidDevice objects.
125
126    Args:
127        ads: A list of AndroidDevice objects.
128
129    Returns:
130        A list of dict, each representing info for an AndroidDevice objects.
131    """
132    device_info = []
133    for ad in ads:
134        info = {"serial": ad.serial, "model": ad.model}
135        info.update(ad.build_info)
136        device_info.append(info)
137    return device_info
138
139
140def _start_services_on_ads(ads):
141    """Starts long running services on multiple AndroidDevice objects.
142
143    If any one AndroidDevice object fails to start services, cleans up all
144    existing AndroidDevice objects and their services.
145
146    Args:
147        ads: A list of AndroidDevice objects whose services to start.
148    """
149    running_ads = []
150    for ad in ads:
151        running_ads.append(ad)
152        try:
153            ad.start_services()
154        except:
155            ad.log.exception('Failed to start some services, abort!')
156            destroy(running_ads)
157            raise
158
159
160def _parse_device_list(device_list_str, key):
161    """Parses a byte string representing a list of devices. The string is
162    generated by calling either adb or fastboot.
163
164    Args:
165        device_list_str: Output of adb or fastboot.
166        key: The token that signifies a device in device_list_str.
167
168    Returns:
169        A list of android device serial numbers.
170    """
171    return re.findall(r"(\S+)\t%s" % key, device_list_str)
172
173
174def list_adb_devices():
175    """List all android devices connected to the computer that are detected by
176    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 get_instances(serials):
197    """Create AndroidDevice instances from a list of serials.
198
199    Args:
200        serials: A list of android device serials.
201
202    Returns:
203        A list of AndroidDevice objects.
204    """
205    results = []
206    for s in serials:
207        results.append(AndroidDevice(s))
208    return results
209
210
211def get_instances_with_configs(configs):
212    """Create AndroidDevice instances from a list of json configs.
213
214    Each config should have the required key-value pair "serial".
215
216    Args:
217        configs: A list of dicts each representing the configuration of one
218            android device.
219
220    Returns:
221        A list of AndroidDevice objects.
222    """
223    results = []
224    for c in configs:
225        try:
226            serial = c.pop('serial')
227        except KeyError:
228            raise errors.AndroidDeviceConfigError(
229                "Required value 'serial' is missing in AndroidDevice config %s."
230                % c)
231        ssh_config = c.pop('ssh_config', None)
232        ssh_connection = None
233        if ssh_config is not None:
234            ssh_settings = settings.from_config(ssh_config)
235            ssh_connection = connection.SshConnection(ssh_settings)
236        ad = AndroidDevice(serial, ssh_connection=ssh_connection)
237        ad.load_config(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 ValueError(
308            "Could not find a target device that matches condition: %s." %
309            kwargs)
310    elif len(filtered) == 1:
311        return filtered[0]
312    else:
313        serials = [ad.serial for ad in filtered]
314        raise ValueError("More than one device matched: %s" % serials)
315
316
317def take_bug_reports(ads, test_name, begin_time):
318    """Takes bug reports on a list of android devices.
319
320    If you want to take a bug report, call this function with a list of
321    android_device objects in on_fail. But reports will be taken on all the
322    devices in the list concurrently. Bug report takes a relative long
323    time to take, so use this cautiously.
324
325    Args:
326        ads: A list of AndroidDevice instances.
327        test_name: Name of the test case that triggered this bug report.
328        begin_time: Logline format timestamp taken when the test started.
329    """
330
331    def take_br(test_name, begin_time, ad):
332        ad.take_bug_report(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:
339    """Class representing an android device.
340
341    Each object of this class represents one Android device in ACTS, including
342    handles to adb, fastboot, and sl4a clients. In addition to direct adb
343    commands, this object also uses adb port forwarding to talk to the Android
344    device.
345
346    Attributes:
347        serial: A string that's the serial number of the Android device.
348        log_path: A string that is the path where all logs collected on this
349                  android device should be stored.
350        log: A logger adapted from root logger with added token specific to an
351             AndroidDevice instance.
352        adb_logcat_process: A process that collects the adb logcat.
353        adb: An AdbProxy object used for interacting with the device via adb.
354        fastboot: A FastbootProxy object used for interacting with the device
355                  via fastboot.
356    """
357
358    def __init__(self, serial='', ssh_connection=None):
359        self.serial = serial
360        # logging.log_path only exists when this is used in an ACTS test run.
361        log_path_base = getattr(logging, 'log_path', '/tmp/logs')
362        self.log_dir = 'AndroidDevice%s' % serial
363        self.log_path = os.path.join(log_path_base, self.log_dir)
364        self.log = tracelogger.TraceLogger(
365            AndroidDeviceLoggerAdapter(logging.getLogger(),
366                                       {'serial': serial}))
367        self._event_dispatchers = {}
368        self._services = []
369        self.register_service(services.AdbLogcatService(self))
370        self.register_service(services.Sl4aService(self))
371        self.adb_logcat_process = None
372        self.adb = adb.AdbProxy(serial, ssh_connection=ssh_connection)
373        self.fastboot = fastboot.FastbootProxy(
374            serial, ssh_connection=ssh_connection)
375        if not self.is_bootloader:
376            self.root_adb()
377        self._ssh_connection = ssh_connection
378        self.skip_sl4a = False
379        self.crash_report = None
380        self.data_accounting = collections.defaultdict(int)
381        self._sl4a_manager = sl4a_manager.Sl4aManager(self.adb)
382        self.last_logcat_timestamp = None
383        # Device info cache.
384        self._user_added_device_info = {}
385        self._sdk_api_level = None
386
387    def clean_up(self):
388        """Cleans up the AndroidDevice object and releases any resources it
389        claimed.
390        """
391        self.stop_services()
392        for service in self._services:
393            service.unregister()
394        self._services.clear()
395        if self._ssh_connection:
396            self._ssh_connection.close()
397
398    def register_service(self, service):
399        """Registers the service on the device. """
400        service.register()
401        self._services.append(service)
402
403    # TODO(angli): This function shall be refactored to accommodate all services
404    # and not have hard coded switch for SL4A when b/29157104 is done.
405    def start_services(self, skip_setup_wizard=True):
406        """Starts long running services on the android device.
407
408        1. Start adb logcat capture.
409        2. Start SL4A if not skipped.
410
411        Args:
412            skip_setup_wizard: Whether or not to skip the setup wizard.
413        """
414        if skip_setup_wizard:
415            self.exit_setup_wizard()
416
417        event_bus.post(android_events.AndroidStartServicesEvent(self))
418
419    def stop_services(self):
420        """Stops long running services on the android device.
421
422        Stop adb logcat and terminate sl4a sessions if exist.
423        """
424        event_bus.post(
425            android_events.AndroidStopServicesEvent(self), ignore_errors=True)
426
427    def is_connected(self):
428        out = self.adb.devices()
429        devices = _parse_device_list(out, "device")
430        return self.serial in devices
431
432    @property
433    def build_info(self):
434        """Get the build info of this Android device, including build id and
435        build type.
436
437        This is not available if the device is in bootloader mode.
438
439        Returns:
440            A dict with the build info of this Android device, or None if the
441            device is in bootloader mode.
442        """
443        if self.is_bootloader:
444            self.log.error("Device is in fastboot mode, could not get build "
445                           "info.")
446            return
447
448        build_id = self.adb.getprop("ro.build.id")
449        incremental_build_id = self.adb.getprop("ro.build.version.incremental")
450        valid_build_id = False
451        for regex in RELEASE_ID_REGEXES:
452            if re.match(regex, build_id):
453                valid_build_id = True
454                break
455        if not valid_build_id:
456            build_id = incremental_build_id
457
458        info = {
459            "build_id": build_id,
460            "incremental_build_id": incremental_build_id,
461            "build_type": self.adb.getprop("ro.build.type")
462        }
463        return info
464
465    @property
466    def device_info(self):
467        """Information to be pulled into controller info.
468
469        The latest serial, model, and build_info are included. Additional info
470        can be added via `add_device_info`.
471        """
472        info = {
473            'serial': self.serial,
474            'model': self.model,
475            'build_info': self.build_info,
476            'user_added_info': self._user_added_device_info,
477            'flavor': self.flavor
478        }
479        return info
480
481    def sdk_api_level(self):
482        if self._sdk_api_level is not None:
483            return self._sdk_api_level
484        if self.is_bootloader:
485            self.log.error(
486                'Device is in fastboot mode. Cannot get build info.')
487            return
488        self._sdk_api_level = int(
489            self.adb.shell('getprop ro.build.version.sdk'))
490        return self._sdk_api_level
491
492    @property
493    def is_bootloader(self):
494        """True if the device is in bootloader mode.
495        """
496        return self.serial in list_fastboot_devices()
497
498    @property
499    def is_adb_root(self):
500        """True if adb is running as root for this device.
501        """
502        try:
503            return "0" == self.adb.shell("id -u")
504        except adb.AdbError:
505            # Wait a bit and retry to work around adb flakiness for this cmd.
506            time.sleep(0.2)
507            return "0" == self.adb.shell("id -u")
508
509    @property
510    def model(self):
511        """The Android code name for the device."""
512        # If device is in bootloader mode, get mode name from fastboot.
513        if self.is_bootloader:
514            out = self.fastboot.getvar("product").strip()
515            # "out" is never empty because of the "total time" message fastboot
516            # writes to stderr.
517            lines = out.split('\n', 1)
518            if lines:
519                tokens = lines[0].split(' ')
520                if len(tokens) > 1:
521                    return tokens[1].lower()
522            return None
523        model = self.adb.getprop("ro.build.product").lower()
524        if model == "sprout":
525            return model
526        else:
527            return self.adb.getprop("ro.product.name").lower()
528
529    @property
530    def flavor(self):
531        """Returns the specific flavor of Android build the device is using."""
532        return self.adb.getprop("ro.build.flavor").lower()
533
534    @property
535    def droid(self):
536        """Returns the RPC Service of the first Sl4aSession created."""
537        if len(self._sl4a_manager.sessions) > 0:
538            session_id = sorted(self._sl4a_manager.sessions.keys())[0]
539            return self._sl4a_manager.sessions[session_id].rpc_client
540        else:
541            return None
542
543    @property
544    def ed(self):
545        """Returns the event dispatcher of the first Sl4aSession created."""
546        if len(self._sl4a_manager.sessions) > 0:
547            session_id = sorted(self._sl4a_manager.sessions.keys())[0]
548            return self._sl4a_manager.sessions[
549                session_id].get_event_dispatcher()
550        else:
551            return None
552
553    @property
554    def sl4a_sessions(self):
555        """Returns a dictionary of session ids to sessions."""
556        return list(self._sl4a_manager.sessions)
557
558    @property
559    def is_adb_logcat_on(self):
560        """Whether there is an ongoing adb logcat collection.
561        """
562        if self.adb_logcat_process:
563            if self.adb_logcat_process.is_running():
564                return True
565            else:
566                # if skip_sl4a is true, there is no sl4a session
567                # if logcat died due to device reboot and sl4a session has
568                # not restarted there is no droid.
569                if self.droid:
570                    self.droid.logI('Logcat died')
571                self.log.info("Logcat to %s died", self.log_path)
572                return False
573        return False
574
575    @property
576    def device_log_path(self):
577        """Returns the directory for all Android device logs for the current
578        test context and serial.
579        """
580        return context.get_current_context().get_full_output_path(self.serial)
581
582    def update_sdk_api_level(self):
583        self._sdk_api_level = None
584        self.sdk_api_level()
585
586    def load_config(self, config):
587        """Add attributes to the AndroidDevice object based on json config.
588
589        Args:
590            config: A dictionary representing the configs.
591
592        Raises:
593            AndroidDeviceError is raised if the config is trying to overwrite
594            an existing attribute.
595        """
596        for k, v in config.items():
597            # skip_sl4a value can be reset from config file
598            if hasattr(self, k) and k != "skip_sl4a":
599                raise errors.AndroidDeviceError(
600                    "Attempting to set existing attribute %s on %s" %
601                    (k, self.serial),
602                    serial=self.serial)
603            setattr(self, k, v)
604
605    def root_adb(self):
606        """Change adb to root mode for this device if allowed.
607
608        If executed on a production build, adb will not be switched to root
609        mode per security restrictions.
610        """
611        self.adb.root()
612        self.adb.wait_for_device()
613
614    def get_droid(self, handle_event=True):
615        """Create an sl4a connection to the device.
616
617        Return the connection handler 'droid'. By default, another connection
618        on the same session is made for EventDispatcher, and the dispatcher is
619        returned to the caller as well.
620        If sl4a server is not started on the device, try to start it.
621
622        Args:
623            handle_event: True if this droid session will need to handle
624                events.
625
626        Returns:
627            droid: Android object used to communicate with sl4a on the android
628                device.
629            ed: An optional EventDispatcher to organize events for this droid.
630
631        Examples:
632            Don't need event handling:
633            >>> ad = AndroidDevice()
634            >>> droid = ad.get_droid(False)
635
636            Need event handling:
637            >>> ad = AndroidDevice()
638            >>> droid, ed = ad.get_droid()
639        """
640        session = self._sl4a_manager.create_session()
641        droid = session.rpc_client
642        if handle_event:
643            ed = session.get_event_dispatcher()
644            return droid, ed
645        return droid
646
647    def get_package_pid(self, package_name):
648        """Gets the pid for a given package. Returns None if not running.
649        Args:
650            package_name: The name of the package.
651        Returns:
652            The first pid found under a given package name. None if no process
653            was found running the package.
654        Raises:
655            AndroidDeviceError if the output of the phone's process list was
656            in an unexpected format.
657        """
658        for cmd in ("ps -A", "ps"):
659            try:
660                out = self.adb.shell(
661                    '%s | grep "S %s"' % (cmd, package_name),
662                    ignore_status=True)
663                if package_name not in out:
664                    continue
665                try:
666                    pid = int(out.split()[1])
667                    self.log.info('apk %s has pid %s.', package_name, pid)
668                    return pid
669                except (IndexError, ValueError) as e:
670                    # Possible ValueError from string to int cast.
671                    # Possible IndexError from split.
672                    self.log.warn(
673                        'Command \"%s\" returned output line: '
674                        '\"%s\".\nError: %s', cmd, out, e)
675            except Exception as e:
676                self.log.warn(
677                    'Device fails to check if %s running with \"%s\"\n'
678                    'Exception %s', package_name, cmd, e)
679        self.log.debug("apk %s is not running", package_name)
680        return None
681
682    def get_dispatcher(self, droid):
683        """Return an EventDispatcher for an sl4a session
684
685        Args:
686            droid: Session to create EventDispatcher for.
687
688        Returns:
689            ed: An EventDispatcher for specified session.
690        """
691        return self._sl4a_manager.sessions[droid.uid].get_event_dispatcher()
692
693    def _is_timestamp_in_range(self, target, log_begin_time, log_end_time):
694        low = acts_logger.logline_timestamp_comparator(log_begin_time,
695                                                       target) <= 0
696        high = acts_logger.logline_timestamp_comparator(log_end_time,
697                                                        target) >= 0
698        return low and high
699
700    def cat_adb_log(self,
701                    tag,
702                    begin_time,
703                    end_time=None,
704                    dest_path="AdbLogExcerpts"):
705        """Takes an excerpt of the adb logcat log from a certain time point to
706        current time.
707
708        Args:
709            tag: An identifier of the time period, usually the name of a test.
710            begin_time: Epoch time of the beginning of the time period.
711            end_time: Epoch time of the ending of the time period, default None
712            dest_path: Destination path of the excerpt file.
713        """
714        log_begin_time = acts_logger.epoch_to_log_line_timestamp(begin_time)
715        if end_time is None:
716            log_end_time = acts_logger.get_log_line_timestamp()
717        else:
718            log_end_time = acts_logger.epoch_to_log_line_timestamp(end_time)
719        self.log.debug("Extracting adb log from logcat.")
720        logcat_path = os.path.join(self.device_log_path,
721                                   'adblog_%s_debug.txt' % self.serial)
722        if not os.path.exists(logcat_path):
723            self.log.warning("Logcat file %s does not exist." % logcat_path)
724            return
725        adb_excerpt_dir = os.path.join(self.log_path, dest_path)
726        os.makedirs(adb_excerpt_dir, exist_ok=True)
727        out_name = '%s,%s.txt' % (acts_logger.normalize_log_line_timestamp(
728            log_begin_time), self.serial)
729        tag_len = utils.MAX_FILENAME_LEN - len(out_name)
730        out_name = '%s,%s' % (tag[:tag_len], out_name)
731        adb_excerpt_path = os.path.join(adb_excerpt_dir, out_name)
732        with open(adb_excerpt_path, 'w', encoding='utf-8') as out:
733            in_file = logcat_path
734            with open(in_file, 'r', encoding='utf-8', errors='replace') as f:
735                while True:
736                    line = None
737                    try:
738                        line = f.readline()
739                        if not line:
740                            break
741                    except:
742                        continue
743                    line_time = line[:acts_logger.log_line_timestamp_len]
744                    if not acts_logger.is_valid_logline_timestamp(line_time):
745                        continue
746                    if self._is_timestamp_in_range(line_time, log_begin_time,
747                                                   log_end_time):
748                        if not line.endswith('\n'):
749                            line += '\n'
750                        out.write(line)
751        return adb_excerpt_path
752
753    def search_logcat(self, matching_string, begin_time=None):
754        """Search logcat message with given string.
755
756        Args:
757            matching_string: matching_string to search.
758
759        Returns:
760            A list of dictionaries with full log message, time stamp string
761            and time object. For example:
762            [{"log_message": "05-03 17:39:29.898   968  1001 D"
763                              "ActivityManager: Sending BOOT_COMPLETE user #0",
764              "time_stamp": "2017-05-03 17:39:29.898",
765              "datetime_obj": datetime object}]
766        """
767        logcat_path = os.path.join(self.device_log_path,
768                                   'adblog_%s_debug.txt' % self.serial)
769        if not os.path.exists(logcat_path):
770            self.log.warning("Logcat file %s does not exist." % logcat_path)
771            return
772        output = job.run(
773            "grep '%s' %s" % (matching_string, logcat_path),
774            ignore_status=True)
775        if not output.stdout or output.exit_status != 0:
776            return []
777        if begin_time:
778            log_begin_time = acts_logger.epoch_to_log_line_timestamp(
779                begin_time)
780            begin_time = datetime.strptime(log_begin_time,
781                                           "%Y-%m-%d %H:%M:%S.%f")
782        result = []
783        logs = re.findall(r'(\S+\s\S+)(.*)', output.stdout)
784        for log in logs:
785            time_stamp = log[0]
786            time_obj = datetime.strptime(time_stamp, "%Y-%m-%d %H:%M:%S.%f")
787            if begin_time and time_obj < begin_time:
788                continue
789            result.append({
790                "log_message": "".join(log),
791                "time_stamp": time_stamp,
792                "datetime_obj": time_obj
793            })
794        return result
795
796    def start_adb_logcat(self):
797        """Starts a standing adb logcat collection in separate subprocesses and
798        save the logcat in a file.
799        """
800        if self.is_adb_logcat_on:
801            self.log.warn(
802                'Android device %s already has a running adb logcat thread. ' %
803                self.serial)
804            return
805        # Disable adb log spam filter. Have to stop and clear settings first
806        # because 'start' doesn't support --clear option before Android N.
807        self.adb.shell("logpersist.stop --clear")
808        self.adb.shell("logpersist.start")
809        if hasattr(self, 'adb_logcat_param'):
810            extra_params = self.adb_logcat_param
811        else:
812            extra_params = "-b all"
813
814        self.adb_logcat_process = logcat.create_logcat_keepalive_process(
815            self.serial, self.log_dir, extra_params)
816        self.adb_logcat_process.start()
817
818    def stop_adb_logcat(self):
819        """Stops the adb logcat collection subprocess.
820        """
821        if not self.is_adb_logcat_on:
822            self.log.warn(
823                'Android device %s does not have an ongoing adb logcat ' %
824                self.serial)
825            return
826        # Set the last timestamp to the current timestamp. This may cause
827        # a race condition that allows the same line to be logged twice,
828        # but it does not pose a problem for our logging purposes.
829        self.adb_logcat_process.stop()
830        self.adb_logcat_process = None
831
832    def get_apk_uid(self, apk_name):
833        """Get the uid of the given apk.
834
835        Args:
836        apk_name: Name of the package, e.g., com.android.phone.
837
838        Returns:
839        Linux UID for the apk.
840        """
841        output = self.adb.shell(
842            "dumpsys package %s | grep userId=" % apk_name, ignore_status=True)
843        result = re.search(r"userId=(\d+)", output)
844        if result:
845            return result.group(1)
846        else:
847            None
848
849    def is_apk_installed(self, package_name):
850        """Check if the given apk is already installed.
851
852        Args:
853        package_name: Name of the package, e.g., com.android.phone.
854
855        Returns:
856        True if package is installed. False otherwise.
857        """
858
859        try:
860            return bool(
861                self.adb.shell(
862                    'pm list packages | grep -w "package:%s"' % package_name))
863
864        except Exception as err:
865            self.log.error(
866                'Could not determine if %s is installed. '
867                'Received error:\n%s', package_name, err)
868            return False
869
870    def is_sl4a_installed(self):
871        return self.is_apk_installed(SL4A_APK_NAME)
872
873    def is_apk_running(self, package_name):
874        """Check if the given apk is running.
875
876        Args:
877            package_name: Name of the package, e.g., com.android.phone.
878
879        Returns:
880        True if package is installed. False otherwise.
881        """
882        for cmd in ("ps -A", "ps"):
883            try:
884                out = self.adb.shell(
885                    '%s | grep "S %s"' % (cmd, package_name),
886                    ignore_status=True)
887                if package_name in out:
888                    self.log.info("apk %s is running", package_name)
889                    return True
890            except Exception as e:
891                self.log.warn(
892                    "Device fails to check is %s running by %s "
893                    "Exception %s", package_name, cmd, e)
894                continue
895        self.log.debug("apk %s is not running", package_name)
896        return False
897
898    def is_sl4a_running(self):
899        return self.is_apk_running(SL4A_APK_NAME)
900
901    def force_stop_apk(self, package_name):
902        """Force stop the given apk.
903
904        Args:
905        package_name: Name of the package, e.g., com.android.phone.
906
907        Returns:
908        True if package is installed. False otherwise.
909        """
910        try:
911            self.adb.shell(
912                'am force-stop %s' % package_name, ignore_status=True)
913        except Exception as e:
914            self.log.warn("Fail to stop package %s: %s", package_name, e)
915
916    def stop_sl4a(self):
917        # TODO(markdr): Move this into sl4a_manager.
918        return self.force_stop_apk(SL4A_APK_NAME)
919
920    def start_sl4a(self):
921        self._sl4a_manager.start_sl4a_service()
922
923    def take_bug_report(self, test_name, begin_time):
924        """Takes a bug report on the device and stores it in a file.
925
926        Args:
927            test_name: Name of the test case that triggered this bug report.
928            begin_time: Epoch time when the test started.
929        """
930        self.adb.wait_for_device(timeout=WAIT_FOR_DEVICE_TIMEOUT)
931        new_br = True
932        try:
933            stdout = self.adb.shell("bugreportz -v")
934            # This check is necessary for builds before N, where adb shell's ret
935            # code and stderr are not propagated properly.
936            if "not found" in stdout:
937                new_br = False
938        except adb.AdbError:
939            new_br = False
940        br_path = self.device_log_path
941        os.makedirs(br_path, exist_ok=True)
942        time_stamp = acts_logger.normalize_log_line_timestamp(
943            acts_logger.epoch_to_log_line_timestamp(begin_time))
944        out_name = "AndroidDevice%s_%s" % (
945            self.serial, time_stamp.replace(" ", "_").replace(":", "-"))
946        out_name = "%s.zip" % out_name if new_br else "%s.txt" % out_name
947        full_out_path = os.path.join(br_path, out_name)
948        # in case device restarted, wait for adb interface to return
949        self.wait_for_boot_completion()
950        self.log.info("Taking bugreport for %s.", test_name)
951        if new_br:
952            out = self.adb.shell("bugreportz", timeout=BUG_REPORT_TIMEOUT)
953            if not out.startswith("OK"):
954                raise errors.AndroidDeviceError(
955                    'Failed to take bugreport on %s: %s' % (self.serial, out),
956                    serial=self.serial)
957            br_out_path = out.split(':')[1].strip().split()[0]
958            self.adb.pull("%s %s" % (br_out_path, full_out_path))
959        else:
960            self.adb.bugreport(
961                " > {}".format(full_out_path), timeout=BUG_REPORT_TIMEOUT)
962        self.log.info("Bugreport for %s taken at %s.", test_name,
963                      full_out_path)
964        self.adb.wait_for_device(timeout=WAIT_FOR_DEVICE_TIMEOUT)
965
966    def get_file_names(self,
967                       directory,
968                       begin_time=None,
969                       skip_files=[],
970                       match_string=None):
971        """Get files names with provided directory."""
972        cmd = "find %s -type f" % directory
973        if begin_time:
974            current_time = utils.get_current_epoch_time()
975            seconds = int(math.ceil((current_time - begin_time) / 1000.0))
976            cmd = "%s -mtime -%ss" % (cmd, seconds)
977        if match_string:
978            cmd = "%s -iname %s" % (cmd, match_string)
979        for skip_file in skip_files:
980            cmd = "%s ! -iname %s" % (cmd, skip_file)
981        out = self.adb.shell(cmd, ignore_status=True)
982        if not out or "No such" in out or "Permission denied" in out or \
983            "Not a directory" in out:
984            return []
985        files = out.split("\n")
986        self.log.debug("Find files in directory %s: %s", directory, files)
987        return files
988
989    @property
990    def external_storage_path(self):
991        """
992        The $EXTERNAL_STORAGE path on the device. Most commonly set to '/sdcard'
993        """
994        return self.adb.shell('echo $EXTERNAL_STORAGE')
995
996    def pull_files(self, device_paths, host_path=None):
997        """Pull files from devices.
998
999        Args:
1000            device_paths: List of paths on the device to pull from.
1001            host_path: Destination path
1002        """
1003        if isinstance(device_paths, str):
1004            device_paths = [device_paths]
1005        if not host_path:
1006            host_path = self.log_path
1007        for device_path in device_paths:
1008            self.log.info(
1009                'Pull from device: %s -> %s' % (device_path, host_path))
1010            self.adb.pull(
1011                "%s %s" % (device_path, host_path), timeout=PULL_TIMEOUT)
1012
1013    def check_crash_report(self,
1014                           test_name=None,
1015                           begin_time=None,
1016                           log_crash_report=False):
1017        """check crash report on the device."""
1018        crash_reports = []
1019        for crash_path in CRASH_REPORT_PATHS:
1020            try:
1021                cmd = 'cd %s' % crash_path
1022                self.adb.shell(cmd)
1023            except Exception as e:
1024                self.log.debug("received exception %s", e)
1025                continue
1026            crashes = self.get_file_names(
1027                crash_path,
1028                skip_files=CRASH_REPORT_SKIPS,
1029                begin_time=begin_time)
1030            if crash_path == "/data/tombstones/" and crashes:
1031                tombstones = crashes[:]
1032                for tombstone in tombstones:
1033                    if self.adb.shell(
1034                            'cat %s | grep "crash_dump failed to dump process"'
1035                            % tombstone):
1036                        crashes.remove(tombstone)
1037            if crashes:
1038                crash_reports.extend(crashes)
1039        if crash_reports and log_crash_report:
1040            test_name = test_name or time.strftime("%Y-%m-%d-%Y-%H-%M-%S")
1041            crash_log_path = os.path.join(self.log_path, test_name,
1042                                          "Crashes_%s" % self.serial)
1043            os.makedirs(crash_log_path, exist_ok=True)
1044            self.pull_files(crash_reports, crash_log_path)
1045        return crash_reports
1046
1047    def get_qxdm_logs(self, test_name="", begin_time=None):
1048        """Get qxdm logs."""
1049        # Sleep 10 seconds for the buffered log to be written in qxdm log file
1050        time.sleep(10)
1051        log_path = getattr(self, "qxdm_log_path", DEFAULT_QXDM_LOG_PATH)
1052        qxdm_logs = self.get_file_names(
1053            log_path, begin_time=begin_time, match_string="*.qmdl")
1054        if qxdm_logs:
1055            qxdm_log_path = os.path.join(self.device_log_path,
1056                                         "QXDM_%s" % self.serial)
1057            os.makedirs(qxdm_log_path, exist_ok=True)
1058            self.log.info("Pull QXDM Log %s to %s", qxdm_logs, qxdm_log_path)
1059            self.pull_files(qxdm_logs, qxdm_log_path)
1060            self.adb.pull(
1061                "/firmware/image/qdsp6m.qdb %s" % qxdm_log_path,
1062                timeout=PULL_TIMEOUT,
1063                ignore_status=True)
1064        else:
1065            self.log.error("Didn't find QXDM logs in %s." % log_path)
1066        if "Verizon" in self.adb.getprop("gsm.sim.operator.alpha"):
1067            omadm_log_path = os.path.join(self.device_log_path,
1068                                          "OMADM_%s" % self.serial)
1069            os.makedirs(omadm_log_path, exist_ok=True)
1070            self.log.info("Pull OMADM Log")
1071            self.adb.pull(
1072                "/data/data/com.android.omadm.service/files/dm/log/ %s" %
1073                omadm_log_path,
1074                timeout=PULL_TIMEOUT,
1075                ignore_status=True)
1076
1077    def get_sdm_logs(self, test_name="", begin_time=None):
1078        """Get sdm logs."""
1079        # Sleep 10 seconds for the buffered log to be written in sdm log file
1080        time.sleep(10)
1081        log_path = getattr(self, "sdm_log_path", DEFAULT_SDM_LOG_PATH)
1082        sdm_logs = self.get_file_names(
1083            log_path, begin_time=begin_time, match_string="*.sdm*")
1084        if sdm_logs:
1085            sdm_log_path = os.path.join(self.device_log_path,
1086                                        "SDM_%s" % self.serial)
1087            os.makedirs(sdm_log_path, exist_ok=True)
1088            self.log.info("Pull SDM Log %s to %s", sdm_logs, sdm_log_path)
1089            self.pull_files(sdm_logs, sdm_log_path)
1090        else:
1091            self.log.error("Didn't find SDM logs in %s." % log_path)
1092        if "Verizon" in self.adb.getprop("gsm.sim.operator.alpha"):
1093            omadm_log_path = os.path.join(self.device_log_path,
1094                                          "OMADM_%s" % self.serial)
1095            os.makedirs(omadm_log_path, exist_ok=True)
1096            self.log.info("Pull OMADM Log")
1097            self.adb.pull(
1098                "/data/data/com.android.omadm.service/files/dm/log/ %s" %
1099                omadm_log_path,
1100                timeout=PULL_TIMEOUT,
1101                ignore_status=True)
1102
1103    def start_new_session(self, max_connections=None, server_port=None):
1104        """Start a new session in sl4a.
1105
1106        Also caches the droid in a dict with its uid being the key.
1107
1108        Returns:
1109            An Android object used to communicate with sl4a on the android
1110                device.
1111
1112        Raises:
1113            Sl4aException: Something is wrong with sl4a and it returned an
1114            existing uid to a new session.
1115        """
1116        session = self._sl4a_manager.create_session(
1117            max_connections=max_connections, server_port=server_port)
1118
1119        self._sl4a_manager.sessions[session.uid] = session
1120        return session.rpc_client
1121
1122    def terminate_all_sessions(self):
1123        """Terminate all sl4a sessions on the AndroidDevice instance.
1124
1125        Terminate all sessions and clear caches.
1126        """
1127        self._sl4a_manager.terminate_all_sessions()
1128
1129    def run_iperf_client_nb(self,
1130                            server_host,
1131                            extra_args="",
1132                            timeout=IPERF_TIMEOUT,
1133                            log_file_path=None):
1134        """Start iperf client on the device asynchronously.
1135
1136        Return status as true if iperf client start successfully.
1137        And data flow information as results.
1138
1139        Args:
1140            server_host: Address of the iperf server.
1141            extra_args: A string representing extra arguments for iperf client,
1142                e.g. "-i 1 -t 30".
1143            log_file_path: The complete file path to log the results.
1144
1145        """
1146        cmd = "iperf3 -c {} {}".format(server_host, extra_args)
1147        if log_file_path:
1148            cmd += " --logfile {} &".format(log_file_path)
1149        self.adb.shell_nb(cmd)
1150
1151    def run_iperf_client(self,
1152                         server_host,
1153                         extra_args="",
1154                         timeout=IPERF_TIMEOUT):
1155        """Start iperf client on the device.
1156
1157        Return status as true if iperf client start successfully.
1158        And data flow information as results.
1159
1160        Args:
1161            server_host: Address of the iperf server.
1162            extra_args: A string representing extra arguments for iperf client,
1163                e.g. "-i 1 -t 30".
1164
1165        Returns:
1166            status: true if iperf client start successfully.
1167            results: results have data flow information
1168        """
1169        out = self.adb.shell(
1170            "iperf3 -c {} {}".format(server_host, extra_args), timeout=timeout)
1171        clean_out = out.split('\n')
1172        if "error" in clean_out[0].lower():
1173            return False, clean_out
1174        return True, clean_out
1175
1176    def run_iperf_server(self, extra_args=""):
1177        """Start iperf server on the device
1178
1179        Return status as true if iperf server started successfully.
1180
1181        Args:
1182            extra_args: A string representing extra arguments for iperf server.
1183
1184        Returns:
1185            status: true if iperf server started successfully.
1186            results: results have output of command
1187        """
1188        out = self.adb.shell("iperf3 -s {}".format(extra_args))
1189        clean_out = out.split('\n')
1190        if "error" in clean_out[0].lower():
1191            return False, clean_out
1192        return True, clean_out
1193
1194    def wait_for_boot_completion(self):
1195        """Waits for Android framework to broadcast ACTION_BOOT_COMPLETED.
1196
1197        This function times out after 15 minutes.
1198        """
1199        timeout_start = time.time()
1200        timeout = 15 * 60
1201
1202        self.adb.wait_for_device(timeout=WAIT_FOR_DEVICE_TIMEOUT)
1203        while time.time() < timeout_start + timeout:
1204            try:
1205                completed = self.adb.getprop("sys.boot_completed")
1206                if completed == '1':
1207                    return
1208            except adb.AdbError:
1209                # adb shell calls may fail during certain period of booting
1210                # process, which is normal. Ignoring these errors.
1211                pass
1212            time.sleep(5)
1213        raise errors.AndroidDeviceError(
1214            'Device %s booting process timed out.' % self.serial,
1215            serial=self.serial)
1216
1217    def reboot(self, stop_at_lock_screen=False):
1218        """Reboots the device.
1219
1220        Terminate all sl4a sessions, reboot the device, wait for device to
1221        complete booting, and restart an sl4a session if restart_sl4a is True.
1222
1223        Args:
1224            stop_at_lock_screen: whether to unlock after reboot. Set to False
1225                if want to bring the device to reboot up to password locking
1226                phase. Sl4a checking need the device unlocked after rebooting.
1227        """
1228        if self.is_bootloader:
1229            self.fastboot.reboot()
1230            return
1231        self.stop_services()
1232        self.log.info("Rebooting")
1233        self.adb.reboot()
1234
1235        timeout_start = time.time()
1236        timeout = 2 * 60
1237        # b/111791239: Newer versions of android sometimes return early after
1238        # `adb reboot` is called. This means subsequent calls may make it to
1239        # the device before the reboot goes through, return false positives for
1240        # getprops such as sys.boot_completed.
1241        while time.time() < timeout_start + timeout:
1242            try:
1243                self.adb.get_state()
1244                time.sleep(.1)
1245            except adb.AdbError:
1246                # get_state will raise an error if the device is not found. We
1247                # want the device to be missing to prove the device has kicked
1248                # off the reboot.
1249                break
1250        self.wait_for_boot_completion()
1251        self.root_adb()
1252        skip_sl4a = self.skip_sl4a
1253        self.skip_sl4a = self.skip_sl4a or stop_at_lock_screen
1254        self.start_services()
1255        self.skip_sl4a = skip_sl4a
1256
1257    def restart_runtime(self):
1258        """Restarts android runtime.
1259
1260        Terminate all sl4a sessions, restarts runtime, wait for framework
1261        complete restart, and restart an sl4a session if restart_sl4a is True.
1262        """
1263        self.stop_services()
1264        self.log.info("Restarting android runtime")
1265        self.adb.shell("stop")
1266        # Reset the boot completed flag before we restart the framework
1267        # to correctly detect when the framework has fully come up.
1268        self.adb.shell("setprop sys.boot_completed 0")
1269        self.adb.shell("start")
1270        self.wait_for_boot_completion()
1271        self.root_adb()
1272
1273        self.start_services()
1274
1275    def get_ipv4_address(self, interface='wlan0', timeout=5):
1276        for timer in range(0, timeout):
1277            try:
1278                ip_string = self.adb.shell('ifconfig %s|grep inet' % interface)
1279                break
1280            except adb.AdbError as e:
1281                if timer + 1 == timeout:
1282                    self.log.warning(
1283                        'Unable to find IP address for %s.' % interface)
1284                    return None
1285                else:
1286                    time.sleep(1)
1287        result = re.search('addr:(.*) Bcast', ip_string)
1288        if result != None:
1289            ip_address = result.group(1)
1290            try:
1291                socket.inet_aton(ip_address)
1292                return ip_address
1293            except socket.error:
1294                return None
1295        else:
1296            return None
1297
1298    def get_ipv4_gateway(self, timeout=5):
1299        for timer in range(0, timeout):
1300            try:
1301                gateway_string = self.adb.shell(
1302                    'dumpsys wifi | grep mDhcpResults')
1303                break
1304            except adb.AdbError as e:
1305                if timer + 1 == timeout:
1306                    self.log.warning('Unable to find gateway')
1307                    return None
1308                else:
1309                    time.sleep(1)
1310        result = re.search('Gateway (.*) DNS servers', gateway_string)
1311        if result != None:
1312            ipv4_gateway = result.group(1)
1313            try:
1314                socket.inet_aton(ipv4_gateway)
1315                return ipv4_gateway
1316            except socket.error:
1317                return None
1318        else:
1319            return None
1320
1321    def send_keycode(self, keycode):
1322        self.adb.shell("input keyevent KEYCODE_%s" % keycode)
1323
1324    def get_my_current_focus_window(self):
1325        """Get the current focus window on screen"""
1326        output = self.adb.shell(
1327            'dumpsys window windows | grep -E mCurrentFocus',
1328            ignore_status=True)
1329        if not output or "not found" in output or "Can't find" in output or (
1330                "mCurrentFocus=null" in output):
1331            result = ''
1332        else:
1333            result = output.split(' ')[-1].strip("}")
1334        self.log.debug("Current focus window is %s", result)
1335        return result
1336
1337    def get_my_current_focus_app(self):
1338        """Get the current focus application"""
1339        dumpsys_cmd = [
1340            'dumpsys window | grep -E mFocusedApp',
1341            'dumpsys window windows | grep -E mFocusedApp'
1342        ]
1343        for cmd in dumpsys_cmd:
1344            output = self.adb.shell(cmd, ignore_status=True)
1345            if not output or "not found" in output or "Can't find" in output or (
1346                    "mFocusedApp=null" in output):
1347                result = ''
1348            else:
1349                result = output.split(' ')[-2]
1350                break
1351        self.log.debug("Current focus app is %s", result)
1352        return result
1353
1354    def is_window_ready(self, window_name=None):
1355        current_window = self.get_my_current_focus_window()
1356        if window_name:
1357            return window_name in current_window
1358        return current_window and ENCRYPTION_WINDOW not in current_window
1359
1360    def wait_for_window_ready(self,
1361                              window_name=None,
1362                              check_interval=5,
1363                              check_duration=60):
1364        elapsed_time = 0
1365        while elapsed_time < check_duration:
1366            if self.is_window_ready(window_name=window_name):
1367                return True
1368            time.sleep(check_interval)
1369            elapsed_time += check_interval
1370        self.log.info("Current focus window is %s",
1371                      self.get_my_current_focus_window())
1372        return False
1373
1374    def is_user_setup_complete(self):
1375        return "1" in self.adb.shell("settings get secure user_setup_complete")
1376
1377    def is_screen_awake(self):
1378        """Check if device screen is in sleep mode"""
1379        return "Awake" in self.adb.shell("dumpsys power | grep mWakefulness=")
1380
1381    def is_screen_emergency_dialer(self):
1382        """Check if device screen is in emergency dialer mode"""
1383        return "EmergencyDialer" in self.get_my_current_focus_window()
1384
1385    def is_screen_in_call_activity(self):
1386        """Check if device screen is in in-call activity notification"""
1387        return "InCallActivity" in self.get_my_current_focus_window()
1388
1389    def is_setupwizard_on(self):
1390        """Check if device screen is in emergency dialer mode"""
1391        return "setupwizard" in self.get_my_current_focus_app()
1392
1393    def is_screen_lock_enabled(self):
1394        """Check if screen lock is enabled"""
1395        cmd = ("sqlite3 /data/system/locksettings.db .dump"
1396               " | grep lockscreen.password_type | grep -v alternate")
1397        out = self.adb.shell(cmd, ignore_status=True)
1398        if "unable to open" in out:
1399            self.root_adb()
1400            out = self.adb.shell(cmd, ignore_status=True)
1401        if ",0,'0'" not in out and out != "":
1402            self.log.info("Screen lock is enabled")
1403            return True
1404        return False
1405
1406    def is_waiting_for_unlock_pin(self):
1407        """Check if device is waiting for unlock pin to boot up"""
1408        current_window = self.get_my_current_focus_window()
1409        current_app = self.get_my_current_focus_app()
1410        if ENCRYPTION_WINDOW in current_window:
1411            self.log.info("Device is in CrpytKeeper window")
1412            return True
1413        if "StatusBar" in current_window and (
1414            (not current_app) or "FallbackHome" in current_app):
1415            self.log.info("Device is locked")
1416            return True
1417        return False
1418
1419    def ensure_screen_on(self):
1420        """Ensure device screen is powered on"""
1421        if self.is_screen_lock_enabled():
1422            for _ in range(2):
1423                self.unlock_screen()
1424                time.sleep(1)
1425                if self.is_waiting_for_unlock_pin():
1426                    self.unlock_screen(password=DEFAULT_DEVICE_PASSWORD)
1427                    time.sleep(1)
1428                if not self.is_waiting_for_unlock_pin(
1429                ) and self.wait_for_window_ready():
1430                    return True
1431            return False
1432        else:
1433            self.wakeup_screen()
1434            return True
1435
1436    def wakeup_screen(self):
1437        if not self.is_screen_awake():
1438            self.log.info("Screen is not awake, wake it up")
1439            self.send_keycode("WAKEUP")
1440
1441    def go_to_sleep(self):
1442        if self.is_screen_awake():
1443            self.send_keycode("SLEEP")
1444
1445    def send_keycode_number_pad(self, number):
1446        self.send_keycode("NUMPAD_%s" % number)
1447
1448    def unlock_screen(self, password=None):
1449        self.log.info("Unlocking with %s", password or "swipe up")
1450        # Bring device to SLEEP so that unlock process can start fresh
1451        self.send_keycode("SLEEP")
1452        time.sleep(1)
1453        self.send_keycode("WAKEUP")
1454        if ENCRYPTION_WINDOW not in self.get_my_current_focus_app():
1455            self.send_keycode("MENU")
1456        if password:
1457            self.send_keycode("DEL")
1458            for number in password:
1459                self.send_keycode_number_pad(number)
1460            self.send_keycode("ENTER")
1461            self.send_keycode("BACK")
1462
1463    def exit_setup_wizard(self):
1464        # Handling Android TV's setupwizard is ignored for now.
1465        if 'feature:com.google.android.tv.installed' in self.adb.shell(
1466                'pm list features'):
1467            return
1468        if not self.is_user_setup_complete() or self.is_setupwizard_on():
1469            # b/116709539 need this to prevent reboot after skip setup wizard
1470            self.adb.shell(
1471                "am start -a com.android.setupwizard.EXIT", ignore_status=True)
1472            self.adb.shell(
1473                "pm disable %s" % self.get_setupwizard_package_name())
1474        # Wait up to 5 seconds for user_setup_complete to be updated
1475        end_time = time.time() + 5
1476        while time.time() < end_time:
1477            if self.is_user_setup_complete() or not self.is_setupwizard_on():
1478                return
1479
1480        # If fail to exit setup wizard, set local.prop and reboot
1481        if not self.is_user_setup_complete() and self.is_setupwizard_on():
1482            self.adb.shell("echo ro.test_harness=1 > /data/local.prop")
1483            self.adb.shell("chmod 644 /data/local.prop")
1484            self.reboot(stop_at_lock_screen=True)
1485
1486    def get_setupwizard_package_name(self):
1487        """Finds setupwizard package/.activity
1488
1489        Bypass setupwizard or setupwraith depending on device.
1490
1491         Returns:
1492            packageName/.ActivityName
1493        """
1494        packages_to_skip = "'setupwizard|setupwraith'"
1495        android_package_name = "com.google.android"
1496        package = self.adb.shell(
1497            "pm list packages -f | grep -E {} | grep {}".format(
1498                packages_to_skip, android_package_name))
1499        wizard_package = package.split('=')[1]
1500        activity = package.split('=')[0].split('/')[-2]
1501        self.log.info("%s/.%sActivity" % (wizard_package, activity))
1502        return "%s/.%sActivity" % (wizard_package, activity)
1503
1504    def push_system_file(self, src_file_path, dst_file_path, push_timeout=300):
1505        """Pushes a file onto the read-only file system.
1506
1507        For speed, the device is left in root mode after this call, and leaves
1508        verity disabled. To re-enable verity, call ensure_verity_enabled().
1509
1510        Args:
1511            src_file_path: The path to the system app to install.
1512            dst_file_path: The destination of the file.
1513            push_timeout: How long to wait for the push to finish.
1514        Returns:
1515            Whether or not the install was successful.
1516        """
1517        self.adb.ensure_root()
1518        try:
1519            self.ensure_verity_disabled()
1520            self.adb.remount()
1521            out = self.adb.push(
1522                '%s %s' % (src_file_path, dst_file_path), timeout=push_timeout)
1523            if 'error' in out:
1524                self.log.error('Unable to push system file %s to %s due to %s',
1525                               src_file_path, dst_file_path, out)
1526                return False
1527            return True
1528        except Exception as e:
1529            self.log.error('Unable to push system file %s to %s due to %s',
1530                           src_file_path, dst_file_path, e)
1531            return False
1532
1533    def ensure_verity_enabled(self):
1534        """Ensures that verity is enabled.
1535
1536        If verity is not enabled, this call will reboot the phone. Note that
1537        this only works on debuggable builds.
1538        """
1539        user = self.adb.get_user_id()
1540        # The below properties will only exist if verity has been enabled.
1541        system_verity = self.adb.getprop('partition.system.verified')
1542        vendor_verity = self.adb.getprop('partition.vendor.verified')
1543        if not system_verity or not vendor_verity:
1544            self.adb.ensure_root()
1545            self.adb.enable_verity()
1546            self.reboot()
1547            self.adb.ensure_user(user)
1548
1549    def ensure_verity_disabled(self):
1550        """Ensures that verity is disabled.
1551
1552        If verity is enabled, this call will reboot the phone.
1553        """
1554        user = self.adb.get_user_id()
1555        # The below properties will only exist if verity has been enabled.
1556        system_verity = self.adb.getprop('partition.system.verified')
1557        vendor_verity = self.adb.getprop('partition.vendor.verified')
1558        if system_verity or vendor_verity:
1559            self.adb.ensure_root()
1560            self.adb.disable_verity()
1561            self.reboot()
1562            self.adb.ensure_user(user)
1563
1564
1565class AndroidDeviceLoggerAdapter(logging.LoggerAdapter):
1566    def process(self, msg, kwargs):
1567        msg = "[AndroidDevice|%s] %s" % (self.extra["serial"], msg)
1568        return (msg, kwargs)
1569