1 # 2 # Copyright 2016 - The Android Open Source Project 3 # 4 # Licensed under the Apache License, Version 2.0 (the "License"); 5 # you may not use this file except in compliance with the License. 6 # You may obtain a copy of the License at 7 # 8 # http://www.apache.org/licenses/LICENSE-2.0 9 # 10 # Unless required by applicable law or agreed to in writing, software 11 # distributed under the License is distributed on an "AS IS" BASIS, 12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 # See the License for the specific language governing permissions and 14 # limitations under the License. 15 16 from builtins import str 17 from builtins import open 18 19 import gzip 20 import logging 21 import os 22 import socket 23 import subprocess 24 import tempfile 25 import threading 26 import time 27 import traceback 28 29 from vts.runners.host import asserts 30 from vts.runners.host import errors 31 from vts.runners.host import keys 32 from vts.runners.host import logger as vts_logger 33 from vts.runners.host import signals 34 from vts.runners.host import utils 35 from vts.runners.host.tcp_client import vts_tcp_client 36 from vts.utils.python.controllers import adb 37 from vts.utils.python.controllers import event_dispatcher 38 from vts.utils.python.controllers import fastboot 39 from vts.utils.python.controllers import customflasher 40 from vts.utils.python.controllers import sl4a_client 41 from vts.utils.python.mirror import mirror_tracker 42 43 VTS_CONTROLLER_CONFIG_NAME = "AndroidDevice" 44 VTS_CONTROLLER_REFERENCE_NAME = "android_devices" 45 46 ANDROID_DEVICE_PICK_ALL_TOKEN = "*" 47 # Key name for adb logcat extra params in config file. 48 ANDROID_DEVICE_ADB_LOGCAT_PARAM_KEY = "adb_logcat_param" 49 ANDROID_DEVICE_EMPTY_CONFIG_MSG = "Configuration is empty, abort!" 50 ANDROID_DEVICE_NOT_LIST_CONFIG_MSG = "Configuration should be a list, abort!" 51 PORT_RETRY_COUNT = 3 52 SL4A_APK_NAME = "com.googlecode.android_scripting" 53 54 ANDROID_PRODUCT_TYPE_UNKNOWN = "unknown" 55 56 # Target-side directory where the VTS binaries are uploaded 57 DEFAULT_AGENT_BASE_DIR = "/data/local/tmp" 58 # Time for which the current is put on sleep when the client is unable to 59 # make a connection. 60 THREAD_SLEEP_TIME = 1 61 # Max number of attempts that the client can make to connect to the agent 62 MAX_AGENT_CONNECT_RETRIES = 10 63 64 65 class AndroidDeviceError(signals.ControllerError): 66 pass 67 68 69 def create(configs, start_services=True): 70 """Creates AndroidDevice controller objects. 71 72 Args: 73 configs: A list of dicts, each representing a configuration for an 74 Android device. 75 start_services: boolean, controls whether services will be started. 76 77 Returns: 78 A list of AndroidDevice objects. 79 """ 80 if not configs: 81 raise AndroidDeviceError(ANDROID_DEVICE_EMPTY_CONFIG_MSG) 82 elif configs == ANDROID_DEVICE_PICK_ALL_TOKEN: 83 ads = get_all_instances() 84 elif not isinstance(configs, list): 85 raise AndroidDeviceError(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 connected_ads = list_adb_devices() 93 for ad in ads: 94 if ad.serial not in connected_ads: 95 raise DoesNotExistError(("Android device %s is specified in config" 96 " but is not attached.") % ad.serial) 97 if start_services: 98 _startServicesOnAds(ads) 99 return ads 100 101 102 def destroy(ads): 103 """Cleans up AndroidDevice objects. 104 105 Args: 106 ads: A list of AndroidDevice objects. 107 """ 108 for ad in ads: 109 try: 110 ad.cleanUp() 111 except: 112 ad.log.exception("Failed to clean up properly.") 113 114 115 def _startServicesOnAds(ads): 116 """Starts long running services on multiple AndroidDevice objects. 117 118 If any one AndroidDevice object fails to start services, cleans up all 119 existing AndroidDevice objects and their services. 120 121 Args: 122 ads: A list of AndroidDevice objects whose services to start. 123 """ 124 running_ads = [] 125 for ad in ads: 126 running_ads.append(ad) 127 try: 128 ad.startServices() 129 except: 130 ad.log.exception("Failed to start some services, abort!") 131 destroy(running_ads) 132 raise 133 134 135 def _parse_device_list(device_list_str, key): 136 """Parses a byte string representing a list of devices. The string is 137 generated by calling either adb or fastboot. 138 139 Args: 140 device_list_str: Output of adb or fastboot. 141 key: The token that signifies a device in device_list_str. 142 143 Returns: 144 A list of android device serial numbers. 145 """ 146 clean_lines = str(device_list_str, 'utf-8').strip().split('\n') 147 results = [] 148 for line in clean_lines: 149 tokens = line.strip().split('\t') 150 if len(tokens) == 2 and tokens[1] == key: 151 results.append(tokens[0]) 152 return results 153 154 155 def list_adb_devices(): 156 """List all target devices connected to the host and detected by adb. 157 158 Returns: 159 A list of android device serials. Empty if there's none. 160 """ 161 out = adb.AdbProxy().devices() 162 return _parse_device_list(out, "device") 163 164 165 def list_fastboot_devices(): 166 """List all android devices connected to the computer that are in in 167 fastboot mode. These are detected by fastboot. 168 169 Returns: 170 A list of android device serials. Empty if there's none. 171 """ 172 out = fastboot.FastbootProxy().devices() 173 return _parse_device_list(out, "fastboot") 174 175 176 def get_instances(serials): 177 """Create AndroidDevice instances from a list of serials. 178 179 Args: 180 serials: A list of android device serials. 181 182 Returns: 183 A list of AndroidDevice objects. 184 """ 185 results = [] 186 for s in serials: 187 results.append(AndroidDevice(s)) 188 return results 189 190 191 def get_instances_with_configs(configs): 192 """Create AndroidDevice instances from a list of json configs. 193 194 Each config should have the required key-value pair "serial". 195 196 Args: 197 configs: A list of dicts each representing the configuration of one 198 android device. 199 200 Returns: 201 A list of AndroidDevice objects. 202 """ 203 results = [] 204 for c in configs: 205 try: 206 serial = c.pop(keys.ConfigKeys.IKEY_SERIAL) 207 except KeyError: 208 raise AndroidDeviceError(('Required value %s is missing in ' 209 'AndroidDevice config %s.') % 210 (keys.ConfigKeys.IKEY_SERIAL, c)) 211 try: 212 product_type = c.pop(keys.ConfigKeys.IKEY_PRODUCT_TYPE) 213 except KeyError: 214 logging.error('Required value %s is missing in ' 215 'AndroidDevice config %s.', 216 keys.ConfigKeys.IKEY_PRODUCT_TYPE, c) 217 product_type = ANDROID_PRODUCT_TYPE_UNKNOWN 218 219 ad = AndroidDevice(serial, product_type) 220 ad.loadConfig(c) 221 results.append(ad) 222 return results 223 224 225 def get_all_instances(include_fastboot=False): 226 """Create AndroidDevice instances for all attached android devices. 227 228 Args: 229 include_fastboot: Whether to include devices in bootloader mode or not. 230 231 Returns: 232 A list of AndroidDevice objects each representing an android device 233 attached to the computer. 234 """ 235 if include_fastboot: 236 serial_list = list_adb_devices() + list_fastboot_devices() 237 return get_instances(serial_list) 238 return get_instances(list_adb_devices()) 239 240 241 def filter_devices(ads, func): 242 """Finds the AndroidDevice instances from a list that match certain 243 conditions. 244 245 Args: 246 ads: A list of AndroidDevice instances. 247 func: A function that takes an AndroidDevice object and returns True 248 if the device satisfies the filter condition. 249 250 Returns: 251 A list of AndroidDevice instances that satisfy the filter condition. 252 """ 253 results = [] 254 for ad in ads: 255 if func(ad): 256 results.append(ad) 257 return results 258 259 260 def get_device(ads, **kwargs): 261 """Finds a unique AndroidDevice instance from a list that has specific 262 attributes of certain values. 263 264 Example: 265 get_device(android_devices, label="foo", phone_number="1234567890") 266 get_device(android_devices, model="angler") 267 268 Args: 269 ads: A list of AndroidDevice instances. 270 kwargs: keyword arguments used to filter AndroidDevice instances. 271 272 Returns: 273 The target AndroidDevice instance. 274 275 Raises: 276 AndroidDeviceError is raised if none or more than one device is 277 matched. 278 """ 279 280 def _get_device_filter(ad): 281 for k, v in kwargs.items(): 282 if not hasattr(ad, k): 283 return False 284 elif getattr(ad, k) != v: 285 return False 286 return True 287 288 filtered = filter_devices(ads, _get_device_filter) 289 if not filtered: 290 raise AndroidDeviceError(("Could not find a target device that matches" 291 " condition: %s.") % kwargs) 292 elif len(filtered) == 1: 293 return filtered[0] 294 else: 295 serials = [ad.serial for ad in filtered] 296 raise AndroidDeviceError("More than one device matched: %s" % serials) 297 298 299 def takeBugReports(ads, test_name, begin_time): 300 """Takes bug reports on a list of android devices. 301 302 If you want to take a bug report, call this function with a list of 303 android_device objects in on_fail. But reports will be taken on all the 304 devices in the list concurrently. Bug report takes a relative long 305 time to take, so use this cautiously. 306 307 Args: 308 ads: A list of AndroidDevice instances. 309 test_name: Name of the test case that triggered this bug report. 310 begin_time: Logline format timestamp taken when the test started. 311 """ 312 begin_time = vts_logger.normalizeLogLineTimestamp(begin_time) 313 314 def take_br(test_name, begin_time, ad): 315 ad.takeBugReport(test_name, begin_time) 316 317 args = [(test_name, begin_time, ad) for ad in ads] 318 utils.concurrent_exec(take_br, args) 319 320 321 class AndroidDevice(object): 322 """Class representing an android device. 323 324 Each object of this class represents one Android device. The object holds 325 handles to adb, fastboot, and various RPC clients. 326 327 Attributes: 328 serial: A string that's the serial number of the Android device. 329 device_command_port: int, the port number used on the Android device 330 for adb port forwarding (for command-response sessions). 331 device_callback_port: int, the port number used on the Android device 332 for adb port reverse forwarding (for callback sessions). 333 Set -1 if callback is not needed (e.g., when this class is used 334 as an adb library). 335 log: A logger project with a device-specific prefix for each line - 336 [AndroidDevice|<serial>] 337 log_path: A string that is the path where all logs collected on this 338 android device should be stored. 339 adb_logcat_process: A process that collects the adb logcat. 340 adb_logcat_file_path: A string that's the full path to the adb logcat 341 file collected, if any. 342 vts_agent_process: A process that runs the HAL agent. 343 adb: An AdbProxy object used for interacting with the device via adb. 344 fastboot: A FastbootProxy object used for interacting with the device 345 via fastboot. 346 customflasher: A CustomFlasherProxy object used for interacting with 347 the device via user defined flashing binary. 348 enable_vts_agent: bool, whether VTS agent is used. 349 enable_sl4a: bool, whether SL4A is used. 350 enable_sl4a_ed: bool, whether SL4A Event Dispatcher is used. 351 host_command_port: the host-side port for runner to agent sessions 352 (to send commands and receive responses). 353 host_callback_port: the host-side port for agent to runner sessions 354 (to get callbacks from agent). 355 hal: HalMirror, in charge of all communications with the HAL layer. 356 lib: LibMirror, in charge of all communications with static and shared 357 native libs. 358 shell: ShellMirror, in charge of all communications with shell. 359 _product_type: A string, the device product type (e.g., bullhead) if 360 known, ANDROID_PRODUCT_TYPE_UNKNOWN otherwise. 361 """ 362 363 def __init__(self, 364 serial="", 365 product_type=ANDROID_PRODUCT_TYPE_UNKNOWN, 366 device_callback_port=5010): 367 self.serial = serial 368 self._product_type = product_type 369 self.device_command_port = None 370 self.device_callback_port = device_callback_port 371 self.log = AndroidDeviceLoggerAdapter(logging.getLogger(), 372 {"serial": self.serial}) 373 base_log_path = getattr(logging, "log_path", "/tmp/logs/") 374 self.log_path = os.path.join(base_log_path, "AndroidDevice%s" % serial) 375 self.adb_logcat_process = None 376 self.adb_logcat_file_path = None 377 self.vts_agent_process = None 378 self.adb = adb.AdbProxy(serial) 379 self.fastboot = fastboot.FastbootProxy(serial) 380 self.customflasher = customflasher.CustomFlasherProxy(serial) 381 if not self.isBootloaderMode: 382 self.rootAdb() 383 self.host_command_port = None 384 self.host_callback_port = adb.get_available_host_port() 385 if self.device_callback_port >= 0: 386 self.adb.reverse_tcp_forward(self.device_callback_port, 387 self.host_callback_port) 388 self.hal = None 389 self.lib = None 390 self.shell = None 391 self.sl4a_host_port = None 392 # TODO: figure out a good way to detect which port is available 393 # on the target side, instead of hard coding a port number. 394 self.sl4a_target_port = 8082 395 396 def __del__(self): 397 self.cleanUp() 398 399 def SetCustomFlasherPath(self, customflasher_path): 400 """Sets customflasher path to use to flash the device. 401 402 Args: 403 customflasher_path: string, path to user-spcified flash binary. 404 """ 405 self.customflasher.SetCustomBinaryPath(customflasher_path) 406 407 def cleanUp(self): 408 """Cleans up the AndroidDevice object and releases any resources it 409 claimed. 410 """ 411 self.stopServices() 412 if self.host_command_port: 413 self.adb.forward("--remove tcp:%s" % self.host_command_port) 414 self.host_command_port = None 415 if self.sl4a_host_port: 416 self.adb.forward("--remove tcp:%s" % self.sl4a_host_port) 417 self.sl4a_host_port = None 418 419 @property 420 def isBootloaderMode(self): 421 """True if the device is in bootloader mode.""" 422 return self.serial in list_fastboot_devices() 423 424 @property 425 def isAdbRoot(self): 426 """True if adb is running as root for this device.""" 427 id_str = self.adb.shell("id -un").strip().decode("utf-8") 428 return id_str == "root" 429 430 @property 431 def verityEnabled(self): 432 """True if verity is enabled for this device.""" 433 try: 434 verified = self.getProp("partition.system.verified") 435 if not verified: 436 return False 437 except adb.AdbError: 438 # If verity is disabled, there is no property 'partition.system.verified' 439 return False 440 return True 441 442 @property 443 def model(self): 444 """The Android code name for the device.""" 445 # If device is in bootloader mode, get mode name from fastboot. 446 if self.isBootloaderMode: 447 out = self.fastboot.getvar("product").strip() 448 # "out" is never empty because of the "total time" message fastboot 449 # writes to stderr. 450 lines = out.decode("utf-8").split('\n', 1) 451 if lines: 452 tokens = lines[0].split(' ') 453 if len(tokens) > 1: 454 return tokens[1].lower() 455 return None 456 model = self.getProp("ro.build.product").lower() 457 if model == "sprout": 458 return model 459 else: 460 model = self.getProp("ro.product.name").lower() 461 return model 462 463 @property 464 def first_api_level(self): 465 """Gets the API level that the device was initially launched with.""" 466 return self.getProp("ro.product.first_api_level") 467 468 @property 469 def sdk_version(self): 470 """Gets the SDK version that the device is running with.""" 471 return self.getProp("ro.build.version.sdk") 472 473 def getLaunchApiLevel(self, strict=True): 474 """Gets the API level that the device was initially launched with. 475 476 This method reads ro.product.first_api_level from the device. If the 477 value is 0, it then reads ro.build.version.sdk. 478 479 Args: 480 strict: A boolean, whether to fail the test if the property is 481 not an integer or not defined. 482 483 Returns: 484 An integer, the API level. 485 0 if the property is not an integer or not defined. 486 """ 487 level_str = self.first_api_level 488 try: 489 level = int(level_str) 490 except ValueError: 491 error_msg = "Cannot parse first_api_level: %s" % level_str 492 if strict: 493 asserts.fail(error_msg) 494 logging.error(error_msg) 495 return 0 496 497 if level != 0: 498 return level 499 500 level_str = self.sdk_version 501 try: 502 return int(level_str) 503 except ValueError: 504 error_msg = "Cannot parse version.sdk: %s" % level_str 505 if strict: 506 asserts.fail(error_msg) 507 logging.error(error_msg) 508 return 0 509 510 @property 511 def vndk_version(self): 512 """Gets the VNDK version that the vendor partition is using.""" 513 return self.getProp("ro.vndk.version") 514 515 @property 516 def vndk_lite(self): 517 """Checks whether the vendor partition requests lite VNDK 518 enforcement. 519 520 Returns: 521 bool, True for lite vndk enforcement. 522 """ 523 vndk_lite_str = self.getProp("ro.vndk.lite") 524 if vndk_lite_str is None: 525 logging.debug('ro.vndk.lite: %s' % vndk_lite_str) 526 return False 527 return vndk_lite_str.lower() == "true" 528 529 @property 530 def cpu_abi(self): 531 """CPU ABI (Application Binary Interface) of the device.""" 532 out = self.getProp("ro.product.cpu.abi") 533 if not out: 534 return "unknown" 535 536 cpu_abi = out.lower() 537 return cpu_abi 538 539 def getCpuAbiList(self, bitness=""): 540 """Gets list of supported ABIs from property. 541 542 Args: 543 bitness: 32 or 64. If the argument is not specified, this method 544 returns both 32 and 64-bit ABIs. 545 546 Returns: 547 A list of strings, the supported ABIs. 548 """ 549 out = self.getProp("ro.product.cpu.abilist" + str(bitness)) 550 return out.lower().split(",") if out else [] 551 552 @property 553 def is64Bit(self): 554 """True if device is 64 bit.""" 555 out = self.adb.shell('uname -m') 556 return "64" in out 557 558 @property 559 def total_memory(self): 560 """Total memory on device. 561 562 Returns: 563 long, total memory in bytes. -1 if cannot get memory information. 564 """ 565 total_memory_command = 'cat /proc/meminfo | grep MemTotal' 566 out = self.adb.shell(total_memory_command) 567 value_unit = out.split(':')[-1].strip().split(' ') 568 569 if len(value_unit) != 2: 570 logging.error('Cannot get memory information. %s', out) 571 return -1 572 573 value, unit = value_unit 574 575 try: 576 value = int(value) 577 except ValueError: 578 logging.error('Unrecognized total memory value: %s', value_unit) 579 return -1 580 581 unit = unit.lower() 582 if unit == 'kb': 583 value *= 1024 584 elif unit == 'mb': 585 value *= 1024 * 1024 586 elif unit == 'b': 587 pass 588 else: 589 logging.error('Unrecognized total memory unit: %s', value_unit) 590 return -1 591 592 return value 593 594 @property 595 def libPaths(self): 596 """List of strings representing the paths to the native library directories.""" 597 paths_32 = ["/system/lib", "/vendor/lib"] 598 if self.is64Bit: 599 paths_64 = ["/system/lib64", "/vendor/lib64"] 600 paths_64.extend(paths_32) 601 return paths_64 602 return paths_32 603 604 @property 605 def isAdbLogcatOn(self): 606 """Whether there is an ongoing adb logcat collection. 607 """ 608 if self.adb_logcat_process: 609 return True 610 return False 611 612 @property 613 def mac_address(self): 614 """The MAC address of the device. 615 """ 616 try: 617 command = 'su root cat /sys/class/net/wlan0/address' 618 response = self.adb.shell(command) 619 return response.strip() 620 except adb.AdbError as e: 621 logging.exception(e) 622 return "unknown" 623 624 @property 625 def sim_state(self): 626 """The SIM state of the device. 627 """ 628 return self.getProp('gsm.sim.state') 629 630 @property 631 def sim_operator(self): 632 """The SIM operator of the device. 633 """ 634 return self.getProp('gsm.operator.alpha') 635 636 def getKernelConfig(self, config_name): 637 """Gets kernel config from the device. 638 639 Args: 640 config_name: A string, the name of the configuration. 641 642 Returns: 643 "y" or "m" if the config is set. 644 "" if the config is not set. 645 None if fails to read config. 646 """ 647 line_prefix = config_name + "=" 648 with tempfile.NamedTemporaryFile(delete=False) as temp_file: 649 config_path = temp_file.name 650 try: 651 logging.debug("Pull config.gz to %s", config_path) 652 self.adb.pull("/proc/config.gz", config_path) 653 with gzip.GzipFile(config_path, "rb") as config_file: 654 for line in config_file: 655 if line.strip().startswith(line_prefix): 656 logging.debug("Found config: %s", line) 657 return line.strip()[len(line_prefix):] 658 logging.debug("%s is not set.", config_name) 659 return "" 660 except (adb.AdbError, IOError) as e: 661 logging.exception("Cannot read kernel config.", e) 662 return None 663 finally: 664 os.remove(config_path) 665 666 def getBinderBitness(self): 667 """Returns the value of BINDER_IPC_32BIT in kernel config. 668 669 Returns: 670 32 or 64, binder bitness of the device. 671 None if fails to read config. 672 """ 673 config_value = self.getKernelConfig("CONFIG_ANDROID_BINDER_IPC_32BIT") 674 if config_value is None: 675 return None 676 elif config_value: 677 return 32 678 else: 679 return 64 680 681 def loadConfig(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 if hasattr(self, k): 693 raise AndroidDeviceError( 694 "Attempting to set existing attribute %s on %s" % 695 (k, self.serial)) 696 setattr(self, k, v) 697 698 def rootAdb(self): 699 """Changes adb to root mode for this device.""" 700 if not self.isAdbRoot: 701 try: 702 self.adb.root() 703 self.adb.wait_for_device() 704 self.adb.remount() 705 self.adb.wait_for_device() 706 except adb.AdbError as e: 707 # adb wait-for-device is not always possible in the lab 708 # continue with an assumption it's done by the harness. 709 logging.exception(e) 710 711 def startAdbLogcat(self): 712 """Starts a standing adb logcat collection in separate subprocesses and 713 save the logcat in a file. 714 """ 715 if self.isAdbLogcatOn: 716 raise AndroidDeviceError(("Android device %s already has an adb " 717 "logcat thread going on. Cannot start " 718 "another one.") % self.serial) 719 f_name = "adblog_%s_%s.txt" % (self.model, self.serial) 720 utils.create_dir(self.log_path) 721 logcat_file_path = os.path.join(self.log_path, f_name) 722 try: 723 extra_params = self.adb_logcat_param 724 except AttributeError: 725 extra_params = "-b all" 726 cmd = "adb -s %s logcat -v threadtime %s >> %s" % (self.serial, 727 extra_params, 728 logcat_file_path) 729 self.adb_logcat_process = utils.start_standing_subprocess(cmd) 730 self.adb_logcat_file_path = logcat_file_path 731 732 def stopAdbLogcat(self): 733 """Stops the adb logcat collection subprocess. 734 """ 735 if not self.isAdbLogcatOn: 736 raise AndroidDeviceError( 737 "Android device %s does not have an ongoing adb logcat collection." 738 % self.serial) 739 try: 740 utils.stop_standing_subprocess(self.adb_logcat_process) 741 except utils.VTSUtilsError as e: 742 logging.error("Cannot stop adb logcat. %s", e) 743 self.adb_logcat_process = None 744 745 def takeBugReport(self, test_name, begin_time): 746 """Takes a bug report on the device and stores it in a file. 747 748 Args: 749 test_name: Name of the test case that triggered this bug report. 750 begin_time: Logline format timestamp taken when the test started. 751 """ 752 br_path = os.path.join(self.log_path, "BugReports") 753 utils.create_dir(br_path) 754 base_name = ",%s,%s.txt" % (begin_time, self.serial) 755 test_name_len = utils.MAX_FILENAME_LEN - len(base_name) 756 out_name = test_name[:test_name_len] + base_name 757 full_out_path = os.path.join(br_path, out_name.replace(' ', '\ ')) 758 self.log.info("Taking bugreport for %s on %s", test_name, self.serial) 759 self.adb.bugreport(" > %s" % full_out_path) 760 self.log.info("Bugreport for %s taken at %s", test_name, full_out_path) 761 762 def waitForBootCompletion(self, timeout=900): 763 """Waits for Android framework to broadcast ACTION_BOOT_COMPLETED. 764 765 Args: 766 timeout: int, seconds to wait for boot completion. Default is 767 15 minutes. 768 769 Returns: 770 bool, True if boot completed. False if any error or timeout 771 """ 772 start = time.time() 773 try: 774 self.adb.wait_for_device(timeout=timeout) 775 except adb.AdbError as e: 776 # adb wait-for-device is not always possible in the lab 777 logging.exception(e) 778 return False 779 780 while not self.isBootCompleted(): 781 if time.time() - start >= timeout: 782 logging.error("Timeout while waiting for boot completion.") 783 return False 784 time.sleep(1) 785 786 return True 787 788 # Deprecated. Use isBootCompleted instead 789 def hasBooted(self): 790 """Checks whether the device has booted. 791 792 Returns: 793 True if booted, False otherwise. 794 """ 795 return self.isBootCompleted() 796 797 def isBootCompleted(self): 798 """Checks whether the device has booted. 799 800 Returns: 801 True if booted, False otherwise. 802 """ 803 try: 804 completed = self.getProp("sys.boot_completed") 805 if completed == '1': 806 return True 807 except adb.AdbError: 808 # adb shell calls may fail during certain period of booting 809 # process, which is normal. Ignoring these errors. 810 pass 811 812 return False 813 814 def isFrameworkRunning(self, check_boot_completion=True): 815 """Checks whether Android framework is started. 816 817 This function will first check boot_completed prop. If boot_completed 818 is 0, then return False meaning framework not started. 819 Then this function will check whether system_server process is running. 820 If yes, then return True meaning framework is started. 821 822 The assumption here is if prop boot_completed is 0 then framework 823 is stopped. 824 825 There are still cases which can make this function return wrong 826 result. For example, boot_completed is set to 0 manually without 827 without stopping framework. 828 829 Args: 830 check_boot_completion: bool, whether to check boot completion 831 before checking framework status. This is an 832 important step for ensuring framework is 833 started. Under most circumstances this value 834 should be set to True. 835 Default True. 836 837 Returns: 838 True if started, False otherwise. 839 """ 840 # First, check whether boot has completed. 841 if check_boot_completion and not self.isBootCompleted(): 842 return False 843 844 cmd = 'ps -g system | grep system_server' 845 res = self.adb.shell(cmd) 846 847 return 'system_server' in res 848 849 def startFramework(self, 850 wait_for_completion=True, 851 wait_for_completion_timeout=120): 852 """Starts Android framework. 853 854 By default this function will wait for framework starting process to 855 finish before returning. 856 857 Args: 858 wait_for_completion: bool, whether to wait for framework to complete 859 starting. Default: True 860 wait_for_completion_timeout: timeout in seconds for waiting framework 861 to start. Default: 2 minutes 862 863 Returns: 864 bool, True if framework start success. False otherwise. 865 """ 866 logging.info("starting Android framework") 867 self.adb.shell("start") 868 869 if wait_for_completion: 870 return self.waitForFrameworkStartComplete(wait_for_completion_timeout) 871 872 return True 873 874 def start(self): 875 """Starts Android framework and waits for ACTION_BOOT_COMPLETED. 876 877 Returns: 878 bool, True if framework start success. False otherwise. 879 """ 880 return self.startFramework() 881 882 def stopFramework(self): 883 """Stops Android framework. 884 885 Method will block until stop is complete. 886 """ 887 logging.info("stopping Android framework") 888 self.adb.shell("stop") 889 self.setProp("sys.boot_completed", 0) 890 logging.info("Android framework stopped") 891 892 def stop(self): 893 """Stops Android framework. 894 895 Method will block until stop is complete. 896 """ 897 self.stopFramework() 898 899 def waitForFrameworkStartComplete(self, timeout_secs=120): 900 """Wait for Android framework to complete starting. 901 902 Args: 903 timeout_secs: int, seconds to wait for boot completion. Default is 904 2 minutes. 905 906 Returns: 907 bool, True if framework is started. False otherwise or timeout 908 """ 909 start = time.time() 910 911 # First, wait for boot completion and checks 912 self.waitForBootCompletion(timeout_secs) 913 914 while not self.isFrameworkRunning(check_boot_completion=False): 915 if time.time() - start >= timeout_secs: 916 logging.error("Timeout while waiting for framework to start.") 917 return False 918 time.sleep(1) 919 920 return True 921 922 def setProp(self, name, value): 923 """Calls setprop shell command. 924 925 Args: 926 name: string, the name of a system property to set 927 value: any type, value will be converted to string. Quotes in value 928 is not supported at this time; if value contains a quote, 929 this method will log an error and return. 930 931 Raises: 932 AdbError, if name contains invalid character 933 """ 934 if name is None or value is None: 935 logging.error("name or value of system property " 936 "should not be None. No property is set.") 937 return 938 939 value = str(value) 940 941 if "'" in value or "\"" in value: 942 logging.error("Quotes in value of system property " 943 "is not yet supported. No property is set.") 944 return 945 946 self.adb.shell("setprop %s \"%s\"" % (name, value)) 947 948 def getProp(self, name): 949 """Calls getprop shell command. 950 951 Args: 952 name: string, the name of a system property to get 953 954 Returns: 955 string, value of the property. If name does not exist; an empty 956 string will be returned. decode("utf-8") and strip() will be called 957 on the output before returning; None will be returned if input 958 name is None 959 960 Raises: 961 AdbError, if name contains invalid character 962 """ 963 if name is None: 964 logging.error("name of system property should not be None.") 965 return None 966 967 out = self.adb.shell("getprop %s" % name) 968 return out.decode("utf-8").strip() 969 970 def reboot(self, restart_services=True): 971 """Reboots the device and wait for device to complete booting. 972 973 This is probably going to print some error messages in console. Only 974 use if there's no other option. 975 976 Raises: 977 AndroidDeviceError is raised if waiting for completion timed 978 out. 979 """ 980 if self.isBootloaderMode: 981 self.fastboot.reboot() 982 return 983 984 if restart_services: 985 has_adb_log = self.isAdbLogcatOn 986 has_vts_agent = True if self.vts_agent_process else False 987 if has_adb_log: 988 self.stopAdbLogcat() 989 if has_vts_agent: 990 self.stopVtsAgent() 991 992 self.adb.reboot() 993 self.waitForBootCompletion() 994 self.rootAdb() 995 996 if restart_services: 997 if has_adb_log: 998 self.startAdbLogcat() 999 if has_vts_agent: 1000 self.startVtsAgent() 1001 1002 def startServices(self): 1003 """Starts long running services on the android device. 1004 1005 1. Start adb logcat capture. 1006 2. Start VtsAgent and create HalMirror unless disabled in config. 1007 3. If enabled in config, start sl4a service and create sl4a clients. 1008 """ 1009 self.enable_vts_agent = getattr(self, "enable_vts_agent", True) 1010 self.enable_sl4a = getattr(self, "enable_sl4a", False) 1011 self.enable_sl4a_ed = getattr(self, "enable_sl4a_ed", False) 1012 try: 1013 self.startAdbLogcat() 1014 except: 1015 self.log.exception("Failed to start adb logcat!") 1016 raise 1017 if self.enable_vts_agent: 1018 self.startVtsAgent() 1019 self.device_command_port = int( 1020 self.adb.shell("cat /data/local/tmp/vts_tcp_server_port")) 1021 logging.info("device_command_port: %s", self.device_command_port) 1022 if not self.host_command_port: 1023 self.host_command_port = adb.get_available_host_port() 1024 self.adb.tcp_forward(self.host_command_port, 1025 self.device_command_port) 1026 self.hal = mirror_tracker.MirrorTracker( 1027 self.host_command_port, self.host_callback_port, True) 1028 self.lib = mirror_tracker.MirrorTracker(self.host_command_port) 1029 self.shell = mirror_tracker.MirrorTracker( 1030 host_command_port=self.host_command_port, adb=self.adb) 1031 if self.enable_sl4a: 1032 try: 1033 self.startSl4aClient(eself.enable_sl4a_ed) 1034 except Exception as e: 1035 self.log.exception("Failed to start SL4A!") 1036 self.log.exception(e) 1037 raise 1038 1039 def stopServices(self): 1040 """Stops long running services on the android device. 1041 """ 1042 if self.adb_logcat_process: 1043 self.stopAdbLogcat() 1044 if getattr(self, "enable_sl4a", False): 1045 self._terminateAllSl4aSessions() 1046 self.stopSl4a() 1047 if getattr(self, "enable_vts_agent", True): 1048 self.stopVtsAgent() 1049 if self.hal: 1050 self.hal.CleanUp() 1051 1052 def startVtsAgent(self): 1053 """Start HAL agent on the AndroidDevice. 1054 1055 This function starts the target side native agent and is persisted 1056 throughout the test run. 1057 """ 1058 self.log.info("Starting VTS agent") 1059 if self.vts_agent_process: 1060 raise AndroidDeviceError( 1061 "HAL agent is already running on %s." % self.serial) 1062 1063 cleanup_commands = [ 1064 "rm -f /data/local/tmp/vts_driver_*", 1065 "rm -f /data/local/tmp/vts_agent_callback*" 1066 ] 1067 kill_commands = [ 1068 "killall vts_hal_agent32", "killall vts_hal_agent64", 1069 "killall vts_hal_driver32", "killall vts_hal_driver64", 1070 "killall vts_shell_driver32", "killall vts_shell_driver64" 1071 ] 1072 cleanup_commands.extend(kill_commands) 1073 chmod_commands = [ 1074 "chmod 755 %s/32/vts_hal_agent32" % DEFAULT_AGENT_BASE_DIR, 1075 "chmod 755 %s/64/vts_hal_agent64" % DEFAULT_AGENT_BASE_DIR, 1076 "chmod 755 %s/32/vts_hal_driver32" % DEFAULT_AGENT_BASE_DIR, 1077 "chmod 755 %s/64/vts_hal_driver64" % DEFAULT_AGENT_BASE_DIR, 1078 "chmod 755 %s/32/vts_shell_driver32" % DEFAULT_AGENT_BASE_DIR, 1079 "chmod 755 %s/64/vts_shell_driver64" % DEFAULT_AGENT_BASE_DIR 1080 ] 1081 cleanup_commands.extend(chmod_commands) 1082 for cmd in cleanup_commands: 1083 try: 1084 self.adb.shell(cmd) 1085 except adb.AdbError as e: 1086 self.log.warning( 1087 "A command to setup the env to start the VTS Agent failed %s", 1088 e) 1089 log_severity = getattr(self, keys.ConfigKeys.KEY_LOG_SEVERITY, "INFO") 1090 bits = ['64', '32'] if self.is64Bit else ['32'] 1091 for bitness in bits: 1092 vts_agent_log_path = os.path.join(self.log_path, 1093 "vts_agent_" + bitness + ".log") 1094 cmd = ('adb -s {s} shell LD_LIBRARY_PATH={path}/{bitness} ' 1095 '{path}/{bitness}/vts_hal_agent{bitness} ' 1096 '--hal_driver_path_32={path}/32/vts_hal_driver32 ' 1097 '--hal_driver_path_64={path}/64/vts_hal_driver64 ' 1098 '--spec_dir={path}/spec ' 1099 '--shell_driver_path_32={path}/32/vts_shell_driver32 ' 1100 '--shell_driver_path_64={path}/64/vts_shell_driver64 ' 1101 '-l {severity} >> {log} 2>&1').format( 1102 s=self.serial, 1103 bitness=bitness, 1104 path=DEFAULT_AGENT_BASE_DIR, 1105 log=vts_agent_log_path, 1106 severity=log_severity) 1107 try: 1108 self.vts_agent_process = utils.start_standing_subprocess( 1109 cmd, check_health_delay=1) 1110 break 1111 except utils.VTSUtilsError as e: 1112 logging.exception(e) 1113 with open(vts_agent_log_path, 'r') as log_file: 1114 logging.error("VTS agent output:\n") 1115 logging.error(log_file.read()) 1116 # one common cause is that 64-bit executable is not supported 1117 # in low API level devices. 1118 if bitness == '32': 1119 raise 1120 else: 1121 logging.error('retrying using a 32-bit binary.') 1122 1123 def stopVtsAgent(self): 1124 """Stop the HAL agent running on the AndroidDevice. 1125 """ 1126 if not self.vts_agent_process: 1127 return 1128 try: 1129 utils.stop_standing_subprocess(self.vts_agent_process) 1130 except utils.VTSUtilsError as e: 1131 logging.error("Cannot stop VTS agent. %s", e) 1132 self.vts_agent_process = None 1133 1134 @property 1135 def product_type(self): 1136 """Gets the product type name.""" 1137 return self._product_type 1138 1139 # Code for using SL4A client 1140 def startSl4aClient(self, handle_event=True): 1141 """Create an sl4a connection to the device. 1142 1143 Return the connection handler 'droid'. By default, another connection 1144 on the same session is made for EventDispatcher, and the dispatcher is 1145 returned to the caller as well. 1146 If sl4a server is not started on the device, try to start it. 1147 1148 Args: 1149 handle_event: True if this droid session will need to handle 1150 events. 1151 """ 1152 self._sl4a_sessions = {} 1153 self._sl4a_event_dispatchers = {} 1154 1155 for i in range(PORT_RETRY_COUNT): 1156 try: 1157 if self.isRogueSl4aRunning(): 1158 self.log.info("Stop rogue sl4a") 1159 self.stopSl4a() 1160 time.sleep(15) 1161 sl4a_client.start_sl4a( 1162 self.adb, device_side_port=self.sl4a_target_port) 1163 time.sleep(5) 1164 1165 self.setupSl4aPort() 1166 droid = self._createNewSl4aSession() 1167 if handle_event: 1168 ed = self._getSl4aEventDispatcher(droid) 1169 ed.start() 1170 break 1171 except sl4a_client.Error as e: 1172 logging.exception("error: %s", e) 1173 1174 def setupSl4aPort(self): 1175 forward_success = False 1176 last_error = None 1177 for _ in range(PORT_RETRY_COUNT): 1178 if not self.sl4a_host_port or not adb.is_port_available( 1179 self.sl4a_host_port): 1180 self.sl4a_host_port = adb.get_available_host_port() 1181 logging.info("sl4a port host %s target %s", self.sl4a_host_port, 1182 self.sl4a_target_port) 1183 try: 1184 self.adb.tcp_forward(self.sl4a_host_port, 1185 self.sl4a_target_port) 1186 forward_success = True 1187 break 1188 except adb.AdbError as e: 1189 last_error = e 1190 pass 1191 if not forward_success: 1192 self.log.error(last_error) 1193 raise last_error 1194 1195 def stopSl4a(self): 1196 """Stops an SL4A apk on a target device.""" 1197 try: 1198 self.adb.shell("am force-stop %s" % SL4A_APK_NAME) 1199 except adb.AdbError as e: 1200 self.log.warn("Fail to stop package %s: %s", SL4A_APK_NAME, e) 1201 1202 def getPackagePid(self, package_name): 1203 """Gets the pid for a given package. Returns None if not running. 1204 1205 Args: 1206 package_name: The name of the package. 1207 1208 Returns: 1209 The first pid found under a given package name. None if no process 1210 was found running the package. 1211 1212 Raises: 1213 AndroidDeviceError if the output of the phone's process list was 1214 in an unexpected format. 1215 """ 1216 for cmd in ("ps -A", "ps"): 1217 try: 1218 out = self.adb.shell('%s | grep "S %s"' % (cmd, package_name)) 1219 if package_name not in out: 1220 continue 1221 try: 1222 pid = int(out.split()[1]) 1223 self.log.info('apk %s has pid %s.', package_name, pid) 1224 return pid 1225 except (IndexError, ValueError) as e: 1226 # Possible ValueError from string to int cast. 1227 # Possible IndexError from split. 1228 self.log.warn('Command \"%s\" returned output line: ' 1229 '\"%s\".\nError: %s', cmd, out, e) 1230 except Exception as e: 1231 self.log.warn( 1232 'Device fails to check if %s running with \"%s\"\n' 1233 'Exception %s', package_name, cmd, e) 1234 self.log.debug("apk %s is not running", package_name) 1235 return None 1236 1237 def isRogueSl4aRunning(self): 1238 """Returns true if SL4A was started by a process other than ACTS. 1239 1240 If SL4A is started by a process other than ACTS, the port will be set to 1241 something other than sl4a_client.DEFAULT_DEVICE_SIDE_PORT. This causes 1242 SL4A to be up and running, but nearly impossible to talk to. 1243 """ 1244 sl4a_pid = self.getPackagePid(SL4A_APK_NAME) 1245 if sl4a_pid is not None: 1246 sl4a_port_hex = '{0:02x}'.format( 1247 sl4a_client.DEFAULT_DEVICE_SIDE_PORT).upper() 1248 port_is_open = ( 1249 # Get the tcp info 1250 'cat /proc/%s/net/tcp | ' 1251 # Remove the space padding 1252 'tr -s " " | ' 1253 # Grab the 4th column (rem_address) 1254 'cut -d " " -f 4 | ' 1255 # Grab the port from that address 1256 'cut -d ":" -f 2 | ' 1257 # Find the port we are looking for 1258 'grep %s') 1259 # If the resulting string from the command is empty, SL4A does not 1260 # have a port open for ACTS to listen to. 1261 return not bool( 1262 self.adb.shell(port_is_open % (sl4a_pid, sl4a_port_hex))) 1263 return False 1264 1265 @property 1266 def droid(self): 1267 """The default SL4A session to the device if exist, None otherwise.""" 1268 if not hasattr(self, 1269 "_sl4a_sessions") or len(self._sl4a_sessions) == 0: 1270 return None 1271 try: 1272 session_id = sorted(self._sl4a_sessions)[0] 1273 result = self._sl4a_sessions[session_id][0] 1274 logging.info("key %s val %s", session_id, result) 1275 return result 1276 except IndexError as e: 1277 logging.exception(e) 1278 return None 1279 1280 @property 1281 def droids(self): 1282 """A list of the active SL4A sessions on this device.""" 1283 if not hasattr(self, 1284 "_sl4a_sessions") or len(self._sl4a_sessions) == 0: 1285 return None 1286 keys = sorted(self._sl4a_sessions) 1287 results = [] 1288 for key in keys: 1289 results.append(self._sl4a_sessions[key][0]) 1290 return results 1291 1292 @property 1293 def ed(self): 1294 """The default SL4A session to the device if exist, None otherwise.""" 1295 if (not hasattr(self, "_sl4a_event_dispatchers") or 1296 len(self._sl4a_event_dispatchers) == 0): 1297 return None 1298 logging.info("self._sl4a_event_dispatchers: %s", 1299 self._sl4a_event_dispatchers) 1300 try: 1301 session_id = sorted(self._sl4a_event_dispatchers)[0] 1302 return self._sl4a_event_dispatchers[session_id] 1303 except IndexError: 1304 return None 1305 1306 @property 1307 def sl4a(self): 1308 """The default SL4A session to the device if exist, None otherwise.""" 1309 try: 1310 return self._sl4a_sessions[sorted(self._sl4a_sessions)[0]][0] 1311 except IndexError: 1312 return None 1313 1314 @property 1315 def sl4as(self): 1316 """A list of the active SL4A sessions on this device. 1317 1318 If multiple connections exist for the same session, only one connection 1319 is listed. 1320 """ 1321 keys = sorted(self._sl4a_sessions) 1322 results = [] 1323 for key in keys: 1324 results.append(self._sl4a_sessions[key][0]) 1325 return results 1326 1327 def getVintfXml(self, use_lshal=True): 1328 """Reads the vendor interface manifest Xml. 1329 1330 Args: 1331 use_hal: bool, set True to use lshal command and False to fetch 1332 manifest.xml directly. 1333 1334 Returns: 1335 Vendor interface manifest string. 1336 """ 1337 if not use_lshal: 1338 return None 1339 try: 1340 stdout = self.adb.shell('"lshal --init-vintf 2> /dev/null"') 1341 return str(stdout) 1342 except adb.AdbError as e: 1343 return None 1344 1345 def _getSl4aEventDispatcher(self, droid): 1346 """Return an EventDispatcher for an sl4a session 1347 1348 Args: 1349 droid: Session to create EventDispatcher for. 1350 1351 Returns: 1352 ed: An EventDispatcher for specified session. 1353 """ 1354 # TODO (angli): Move service-specific start/stop functions out of 1355 # android_device, including VTS Agent, SL4A, and any other 1356 # target-side services. 1357 ed_key = self.serial + str(droid.uid) 1358 if ed_key in self._sl4a_event_dispatchers: 1359 if self._sl4a_event_dispatchers[ed_key] is None: 1360 raise AndroidDeviceError("EventDispatcher Key Empty") 1361 self.log.debug("Returning existing key %s for event dispatcher!", 1362 ed_key) 1363 return self._sl4a_event_dispatchers[ed_key] 1364 event_droid = self._addNewConnectionToSl4aSession(droid.uid) 1365 ed = event_dispatcher.EventDispatcher(event_droid) 1366 self._sl4a_event_dispatchers[ed_key] = ed 1367 return ed 1368 1369 def _createNewSl4aSession(self): 1370 """Start a new session in sl4a. 1371 1372 Also caches the droid in a dict with its uid being the key. 1373 1374 Returns: 1375 An Android object used to communicate with sl4a on the android 1376 device. 1377 1378 Raises: 1379 sl4a_client.Error: Something is wrong with sl4a and it returned an 1380 existing uid to a new session. 1381 """ 1382 droid = sl4a_client.Sl4aClient(port=self.sl4a_host_port) 1383 droid.open() 1384 if droid.uid in self._sl4a_sessions: 1385 raise sl4a_client.Error( 1386 "SL4A returned an existing uid for a new session. Abort.") 1387 logging.debug("set sl4a_session[%s]", droid.uid) 1388 self._sl4a_sessions[droid.uid] = [droid] 1389 return droid 1390 1391 def _addNewConnectionToSl4aSession(self, session_id): 1392 """Create a new connection to an existing sl4a session. 1393 1394 Args: 1395 session_id: UID of the sl4a session to add connection to. 1396 1397 Returns: 1398 An Android object used to communicate with sl4a on the android 1399 device. 1400 1401 Raises: 1402 DoesNotExistError: Raised if the session it's trying to connect to 1403 does not exist. 1404 """ 1405 if session_id not in self._sl4a_sessions: 1406 raise DoesNotExistError("Session %d doesn't exist." % session_id) 1407 droid = sl4a_client.Sl4aClient( 1408 port=self.sl4a_host_port, uid=session_id) 1409 droid.open(cmd=sl4a_client.Sl4aCommand.CONTINUE) 1410 return droid 1411 1412 def _terminateSl4aSession(self, session_id): 1413 """Terminate a session in sl4a. 1414 1415 Send terminate signal to sl4a server; stop dispatcher associated with 1416 the session. Clear corresponding droids and dispatchers from cache. 1417 1418 Args: 1419 session_id: UID of the sl4a session to terminate. 1420 """ 1421 if self._sl4a_sessions and (session_id in self._sl4a_sessions): 1422 for droid in self._sl4a_sessions[session_id]: 1423 droid.closeSl4aSession() 1424 droid.close() 1425 del self._sl4a_sessions[session_id] 1426 ed_key = self.serial + str(session_id) 1427 if ed_key in self._sl4a_event_dispatchers: 1428 self._sl4a_event_dispatchers[ed_key].clean_up() 1429 del self._sl4a_event_dispatchers[ed_key] 1430 1431 def _terminateAllSl4aSessions(self): 1432 """Terminate all sl4a sessions on the AndroidDevice instance. 1433 1434 Terminate all sessions and clear caches. 1435 """ 1436 if hasattr(self, "_sl4a_sessions") and self._sl4a_sessions: 1437 session_ids = list(self._sl4a_sessions.keys()) 1438 for session_id in session_ids: 1439 try: 1440 self._terminateSl4aSession(session_id) 1441 except: 1442 self.log.exception("Failed to terminate session %d.", 1443 session_id) 1444 if self.sl4a_host_port: 1445 self.adb.forward("--remove tcp:%d" % self.sl4a_host_port) 1446 self.sl4a_host_port = None 1447 1448 1449 class AndroidDeviceLoggerAdapter(logging.LoggerAdapter): 1450 """A wrapper class that attaches a prefix to all log lines from an 1451 AndroidDevice object. 1452 """ 1453 1454 def process(self, msg, kwargs): 1455 """Process every log message written via the wrapped logger object. 1456 1457 We are adding the prefix "[AndroidDevice|<serial>]" to all log lines. 1458 1459 Args: 1460 msg: string, the original log message. 1461 kwargs: dict, the key value pairs that can be used to modify the 1462 original log message. 1463 """ 1464 msg = "[AndroidDevice|%s] %s" % (self.extra["serial"], msg) 1465 return (msg, kwargs) 1466 1467 def warn(self, msg, *args, **kwargs): 1468 """Function call warper for warn() to warning().""" 1469 super(AndroidDeviceLoggerAdapter, self).warning(msg, *args, **kwargs) 1470