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