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