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