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