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