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 16from builtins import str 17from builtins import open 18 19import logging 20import os 21import time 22import traceback 23import threading 24import socket 25 26from vts.runners.host import keys 27from vts.runners.host import logger as vts_logger 28from vts.runners.host import signals 29from vts.runners.host import utils 30from vts.utils.python.controllers import adb 31from vts.utils.python.controllers import event_dispatcher 32from vts.utils.python.controllers import fastboot 33from vts.utils.python.controllers import sl4a_client 34from vts.runners.host.tcp_client import vts_tcp_client 35from vts.utils.python.mirror import hal_mirror 36from vts.utils.python.mirror import shell_mirror 37from vts.utils.python.mirror import lib_mirror 38from vts.runners.host import errors 39import subprocess 40 41VTS_CONTROLLER_CONFIG_NAME = "AndroidDevice" 42VTS_CONTROLLER_REFERENCE_NAME = "android_devices" 43 44ANDROID_DEVICE_PICK_ALL_TOKEN = "*" 45# Key name for adb logcat extra params in config file. 46ANDROID_DEVICE_ADB_LOGCAT_PARAM_KEY = "adb_logcat_param" 47ANDROID_DEVICE_EMPTY_CONFIG_MSG = "Configuration is empty, abort!" 48ANDROID_DEVICE_NOT_LIST_CONFIG_MSG = "Configuration should be a list, abort!" 49 50ANDROID_PRODUCT_TYPE_UNKNOWN = "unknown" 51 52# Target-side directory where the VTS binaries are uploaded 53DEFAULT_AGENT_BASE_DIR = "/data/local/tmp" 54# Time for which the current is put on sleep when the client is unable to 55# make a connection. 56THREAD_SLEEP_TIME = 1 57# Max number of attempts that the client can make to connect to the agent 58MAX_AGENT_CONNECT_RETRIES = 10 59 60class AndroidDeviceError(signals.ControllerError): 61 pass 62 63 64def create(configs, start_services=True): 65 """Creates AndroidDevice controller objects. 66 67 Args: 68 configs: A list of dicts, each representing a configuration for an 69 Android device. 70 start_services: boolean, controls whether services will be started. 71 72 Returns: 73 A list of AndroidDevice objects. 74 """ 75 if not configs: 76 raise AndroidDeviceError(ANDROID_DEVICE_EMPTY_CONFIG_MSG) 77 elif configs == ANDROID_DEVICE_PICK_ALL_TOKEN: 78 ads = get_all_instances() 79 elif not isinstance(configs, list): 80 raise AndroidDeviceError(ANDROID_DEVICE_NOT_LIST_CONFIG_MSG) 81 elif isinstance(configs[0], str): 82 # Configs is a list of serials. 83 ads = get_instances(configs) 84 else: 85 # Configs is a list of dicts. 86 ads = get_instances_with_configs(configs) 87 connected_ads = list_adb_devices() 88 for ad in ads: 89 if ad.serial not in connected_ads: 90 raise DoesNotExistError(("Android device %s is specified in config" 91 " but is not attached.") % ad.serial) 92 if start_services: 93 _startServicesOnAds(ads) 94 return ads 95 96 97def destroy(ads): 98 """Cleans up AndroidDevice objects. 99 100 Args: 101 ads: A list of AndroidDevice objects. 102 """ 103 for ad in ads: 104 try: 105 ad.cleanUp() 106 except: 107 ad.log.exception("Failed to clean up properly.") 108 109 110def _startServicesOnAds(ads): 111 """Starts long running services on multiple AndroidDevice objects. 112 113 If any one AndroidDevice object fails to start services, cleans up all 114 existing AndroidDevice objects and their services. 115 116 Args: 117 ads: A list of AndroidDevice objects whose services to start. 118 """ 119 running_ads = [] 120 for ad in ads: 121 running_ads.append(ad) 122 try: 123 ad.startServices() 124 except: 125 ad.log.exception("Failed to start some services, abort!") 126 destroy(running_ads) 127 raise 128 129 130def _parse_device_list(device_list_str, key): 131 """Parses a byte string representing a list of devices. The string is 132 generated by calling either adb or fastboot. 133 134 Args: 135 device_list_str: Output of adb or fastboot. 136 key: The token that signifies a device in device_list_str. 137 138 Returns: 139 A list of android device serial numbers. 140 """ 141 clean_lines = str(device_list_str, 'utf-8').strip().split('\n') 142 results = [] 143 for line in clean_lines: 144 tokens = line.strip().split('\t') 145 if len(tokens) == 2 and tokens[1] == key: 146 results.append(tokens[0]) 147 return results 148 149 150def list_adb_devices(): 151 """List all target devices connected to the host and detected by adb. 152 153 Returns: 154 A list of android device serials. Empty if there's none. 155 """ 156 out = adb.AdbProxy().devices() 157 return _parse_device_list(out, "device") 158 159 160def list_fastboot_devices(): 161 """List all android devices connected to the computer that are in in 162 fastboot mode. These are detected by fastboot. 163 164 Returns: 165 A list of android device serials. Empty if there's none. 166 """ 167 out = fastboot.FastbootProxy().devices() 168 return _parse_device_list(out, "fastboot") 169 170 171def get_instances(serials): 172 """Create AndroidDevice instances from a list of serials. 173 174 Args: 175 serials: A list of android device serials. 176 177 Returns: 178 A list of AndroidDevice objects. 179 """ 180 results = [] 181 for s in serials: 182 results.append(AndroidDevice(s)) 183 return results 184 185 186def get_instances_with_configs(configs): 187 """Create AndroidDevice instances from a list of json configs. 188 189 Each config should have the required key-value pair "serial". 190 191 Args: 192 configs: A list of dicts each representing the configuration of one 193 android device. 194 195 Returns: 196 A list of AndroidDevice objects. 197 """ 198 results = [] 199 for c in configs: 200 try: 201 serial = c.pop(keys.ConfigKeys.IKEY_SERIAL) 202 except KeyError: 203 raise AndroidDeviceError( 204 ('Required value %s is missing in ' 205 'AndroidDevice config %s.') % (keys.ConfigKeys.IKEY_SERIAL, 206 c)) 207 try: 208 product_type = c.pop(keys.ConfigKeys.IKEY_PRODUCT_TYPE) 209 except KeyError: 210 logging.error( 211 'Required value %s is missing in ' 212 'AndroidDevice config %s.', 213 keys.ConfigKeys.IKEY_PRODUCT_TYPE, c) 214 product_type = ANDROID_PRODUCT_TYPE_UNKNOWN 215 216 ad = AndroidDevice(serial, product_type) 217 ad.loadConfig(c) 218 results.append(ad) 219 return results 220 221 222def get_all_instances(include_fastboot=False): 223 """Create AndroidDevice instances for all attached android devices. 224 225 Args: 226 include_fastboot: Whether to include devices in bootloader mode or not. 227 228 Returns: 229 A list of AndroidDevice objects each representing an android device 230 attached to the computer. 231 """ 232 if include_fastboot: 233 serial_list = list_adb_devices() + list_fastboot_devices() 234 return get_instances(serial_list) 235 return get_instances(list_adb_devices()) 236 237 238def filter_devices(ads, func): 239 """Finds the AndroidDevice instances from a list that match certain 240 conditions. 241 242 Args: 243 ads: A list of AndroidDevice instances. 244 func: A function that takes an AndroidDevice object and returns True 245 if the device satisfies the filter condition. 246 247 Returns: 248 A list of AndroidDevice instances that satisfy the filter condition. 249 """ 250 results = [] 251 for ad in ads: 252 if func(ad): 253 results.append(ad) 254 return results 255 256 257def get_device(ads, **kwargs): 258 """Finds a unique AndroidDevice instance from a list that has specific 259 attributes of certain values. 260 261 Example: 262 get_device(android_devices, label="foo", phone_number="1234567890") 263 get_device(android_devices, model="angler") 264 265 Args: 266 ads: A list of AndroidDevice instances. 267 kwargs: keyword arguments used to filter AndroidDevice instances. 268 269 Returns: 270 The target AndroidDevice instance. 271 272 Raises: 273 AndroidDeviceError is raised if none or more than one device is 274 matched. 275 """ 276 277 def _get_device_filter(ad): 278 for k, v in kwargs.items(): 279 if not hasattr(ad, k): 280 return False 281 elif getattr(ad, k) != v: 282 return False 283 return True 284 285 filtered = filter_devices(ads, _get_device_filter) 286 if not filtered: 287 raise AndroidDeviceError(("Could not find a target device that matches" 288 " condition: %s.") % kwargs) 289 elif len(filtered) == 1: 290 return filtered[0] 291 else: 292 serials = [ad.serial for ad in filtered] 293 raise AndroidDeviceError("More than one device matched: %s" % serials) 294 295 296def takeBugReports(ads, test_name, begin_time): 297 """Takes bug reports on a list of android devices. 298 299 If you want to take a bug report, call this function with a list of 300 android_device objects in on_fail. But reports will be taken on all the 301 devices in the list concurrently. Bug report takes a relative long 302 time to take, so use this cautiously. 303 304 Args: 305 ads: A list of AndroidDevice instances. 306 test_name: Name of the test case that triggered this bug report. 307 begin_time: Logline format timestamp taken when the test started. 308 """ 309 begin_time = vts_logger.normalizeLogLineTimestamp(begin_time) 310 311 def take_br(test_name, begin_time, ad): 312 ad.takeBugReport(test_name, begin_time) 313 314 args = [(test_name, begin_time, ad) for ad in ads] 315 utils.concurrent_exec(take_br, args) 316 317 318class AndroidDevice(object): 319 """Class representing an android device. 320 321 Each object of this class represents one Android device. The object holds 322 handles to adb, fastboot, and various RPC clients. 323 324 Attributes: 325 serial: A string that's the serial number of the Android device. 326 device_command_port: int, the port number used on the Android device 327 for adb port forwarding (for command-response sessions). 328 device_callback_port: int, the port number used on the Android device 329 for adb port reverse forwarding (for callback sessions). 330 log: A logger project with a device-specific prefix for each line - 331 [AndroidDevice|<serial>] 332 log_path: A string that is the path where all logs collected on this 333 android device should be stored. 334 adb_logcat_process: A process that collects the adb logcat. 335 adb_logcat_file_path: A string that's the full path to the adb logcat 336 file collected, if any. 337 vts_agent_process: A process that runs the HAL agent. 338 adb: An AdbProxy object used for interacting with the device via adb. 339 fastboot: A FastbootProxy object used for interacting with the device 340 via fastboot. 341 host_command_port: the host-side port for runner to agent sessions 342 (to send commands and receive responses). 343 host_callback_port: the host-side port for agent to runner sessions 344 (to get callbacks from agent). 345 hal: HalMirror, in charge of all communications with the HAL layer. 346 lib: LibMirror, in charge of all communications with static and shared 347 native libs. 348 shell: ShellMirror, in charge of all communications with shell. 349 _product_type: A string, the device product type (e.g., bullhead) if 350 known, ANDROID_PRODUCT_TYPE_UNKNOWN otherwise. 351 """ 352 353 def __init__(self, serial="", product_type=ANDROID_PRODUCT_TYPE_UNKNOWN, 354 device_callback_port=5010): 355 self.serial = serial 356 self._product_type = product_type 357 self.device_command_port = None 358 self.device_callback_port = device_callback_port 359 self.log = AndroidDeviceLoggerAdapter(logging.getLogger(), 360 {"serial": self.serial}) 361 base_log_path = getattr(logging, "log_path", "/tmp/logs/") 362 self.log_path = os.path.join(base_log_path, "AndroidDevice%s" % serial) 363 self.adb_logcat_process = None 364 self.adb_logcat_file_path = None 365 self.vts_agent_process = None 366 self.adb = adb.AdbProxy(serial) 367 self.fastboot = fastboot.FastbootProxy(serial) 368 if not self.isBootloaderMode: 369 self.rootAdb() 370 self.host_command_port = None 371 self.host_callback_port = adb.get_available_host_port() 372 self.adb.reverse_tcp_forward(self.device_callback_port, 373 self.host_callback_port) 374 self.hal = None 375 self.lib = None 376 self.shell = None 377 self.sl4a_host_port = None 378 # TODO: figure out a good way to detect which port is available 379 # on the target side, instead of hard coding a port number. 380 self.sl4a_target_port = 8082 381 382 def __del__(self): 383 self.cleanUp() 384 385 def cleanUp(self): 386 """Cleans up the AndroidDevice object and releases any resources it 387 claimed. 388 """ 389 self.stopServices() 390 if self.host_command_port: 391 self.adb.forward("--remove tcp:%s" % self.host_command_port) 392 self.host_command_port = None 393 if self.sl4a_host_port: 394 self.adb.forward("--remove tcp:%s" % self.sl4a_host_port) 395 self.sl4a_host_port = None 396 397 @property 398 def isBootloaderMode(self): 399 """True if the device is in bootloader mode.""" 400 return self.serial in list_fastboot_devices() 401 402 @property 403 def isAdbRoot(self): 404 """True if adb is running as root for this device.""" 405 id_str = self.adb.shell("id -u").decode("utf-8") 406 return "root" in id_str 407 408 @property 409 def verityEnabled(self): 410 """True if verity is enabled for this device.""" 411 try: 412 verified = self.getProp("partition.system.verified") 413 if not verified: 414 return False 415 except adb.AdbError: 416 # If verity is disabled, there is no property 'partition.system.verified' 417 return False 418 return True 419 420 @property 421 def model(self): 422 """The Android code name for the device.""" 423 # If device is in bootloader mode, get mode name from fastboot. 424 if self.isBootloaderMode: 425 out = self.fastboot.getvar("product").strip() 426 # "out" is never empty because of the "total time" message fastboot 427 # writes to stderr. 428 lines = out.decode("utf-8").split('\n', 1) 429 if lines: 430 tokens = lines[0].split(' ') 431 if len(tokens) > 1: 432 return tokens[1].lower() 433 return None 434 model = self.getProp("ro.build.product").lower() 435 if model == "sprout": 436 return model 437 else: 438 model = self.getProp("ro.product.name").lower() 439 return model 440 441 @property 442 def cpu_abi(self): 443 """CPU ABI (Application Binary Interface) of the device.""" 444 out = self.getProp("ro.product.cpu.abi") 445 if not out: 446 return "unknown" 447 448 cpu_abi = out.lower() 449 return cpu_abi 450 451 @property 452 def is64Bit(self): 453 """True if device is 64 bit.""" 454 out = self.adb.shell('uname -m') 455 return "64" in out 456 457 @property 458 def libPaths(self): 459 """List of strings representing the paths to the native library directories.""" 460 paths_32 = ["/system/lib", "/vendor/lib"] 461 if self.is64Bit: 462 paths_64 = ["/system/lib64", "/vendor/lib64"] 463 paths_64.extend(paths_32) 464 return paths_64 465 return paths_32 466 467 @property 468 def isAdbLogcatOn(self): 469 """Whether there is an ongoing adb logcat collection. 470 """ 471 if self.adb_logcat_process: 472 return True 473 return False 474 475 def loadConfig(self, config): 476 """Add attributes to the AndroidDevice object based on json config. 477 478 Args: 479 config: A dictionary representing the configs. 480 481 Raises: 482 AndroidDeviceError is raised if the config is trying to overwrite 483 an existing attribute. 484 """ 485 for k, v in config.items(): 486 if hasattr(self, k): 487 raise AndroidDeviceError( 488 "Attempting to set existing attribute %s on %s" % 489 (k, self.serial)) 490 setattr(self, k, v) 491 492 def rootAdb(self): 493 """Changes adb to root mode for this device.""" 494 if not self.isAdbRoot: 495 try: 496 self.adb.root() 497 self.adb.wait_for_device() 498 self.adb.remount() 499 self.adb.wait_for_device() 500 except adb.AdbError as e: 501 # adb wait-for-device is not always possible in the lab 502 # continue with an assumption it's done by the harness. 503 logging.exception(e) 504 505 def startAdbLogcat(self): 506 """Starts a standing adb logcat collection in separate subprocesses and 507 save the logcat in a file. 508 """ 509 if self.isAdbLogcatOn: 510 raise AndroidDeviceError(("Android device %s already has an adb " 511 "logcat thread going on. Cannot start " 512 "another one.") % self.serial) 513 f_name = "adblog,%s,%s.txt" % (self.model, self.serial) 514 utils.create_dir(self.log_path) 515 logcat_file_path = os.path.join(self.log_path, f_name) 516 try: 517 extra_params = self.adb_logcat_param 518 except AttributeError: 519 extra_params = "-b all" 520 cmd = "adb -s %s logcat -v threadtime %s >> %s" % ( 521 self.serial, extra_params, logcat_file_path) 522 self.adb_logcat_process = utils.start_standing_subprocess(cmd) 523 self.adb_logcat_file_path = logcat_file_path 524 525 def stopAdbLogcat(self): 526 """Stops the adb logcat collection subprocess. 527 """ 528 if not self.isAdbLogcatOn: 529 raise AndroidDeviceError( 530 "Android device %s does not have an ongoing adb logcat collection." 531 % self.serial) 532 utils.stop_standing_subprocess(self.adb_logcat_process) 533 self.adb_logcat_process = None 534 535 def takeBugReport(self, test_name, begin_time): 536 """Takes a bug report on the device and stores it in a file. 537 538 Args: 539 test_name: Name of the test case that triggered this bug report. 540 begin_time: Logline format timestamp taken when the test started. 541 """ 542 br_path = os.path.join(self.log_path, "BugReports") 543 utils.create_dir(br_path) 544 base_name = ",%s,%s.txt" % (begin_time, self.serial) 545 test_name_len = utils.MAX_FILENAME_LEN - len(base_name) 546 out_name = test_name[:test_name_len] + base_name 547 full_out_path = os.path.join(br_path, out_name.replace(' ', '\ ')) 548 self.log.info("Taking bugreport for %s on %s", test_name, self.serial) 549 self.adb.bugreport(" > %s" % full_out_path) 550 self.log.info("Bugreport for %s taken at %s", test_name, full_out_path) 551 552 @utils.timeout(15 * 60) 553 def waitForBootCompletion(self): 554 """Waits for Android framework to broadcast ACTION_BOOT_COMPLETED. 555 556 This function times out after 15 minutes. 557 """ 558 try: 559 self.adb.wait_for_device() 560 except adb.AdbError as e: 561 # adb wait-for-device is not always possible in the lab 562 logging.exception(e) 563 while not self.hasBooted(): 564 time.sleep(5) 565 566 def hasBooted(self): 567 """Checks whether the device has booted. 568 569 Returns: 570 True if booted, False otherwise. 571 """ 572 try: 573 completed = self.getProp("sys.boot_completed") 574 if completed == '1': 575 return True 576 except adb.AdbError: 577 # adb shell calls may fail during certain period of booting 578 # process, which is normal. Ignoring these errors. 579 return False 580 581 def start(self): 582 """Starts Android runtime and waits for ACTION_BOOT_COMPLETED.""" 583 logging.info("starting Android Runtime") 584 self.adb.shell("start") 585 self.waitForBootCompletion() 586 logging.info("Android Runtime started") 587 588 def stop(self): 589 """Stops Android runtime.""" 590 logging.info("stopping Android Runtime") 591 self.adb.shell("stop") 592 self.setProp("sys.boot_completed", 0) 593 logging.info("Android Runtime stopped") 594 595 def setProp(self, name, value): 596 """Calls setprop shell command. 597 598 Args: 599 name: string, the name of a system property to set 600 value: any type, value will be converted to string. Quotes in value 601 is not supported at this time; if value contains a quote, 602 this method will log an error and return. 603 604 Raises: 605 AdbError, if name contains invalid character 606 """ 607 if name is None or value is None: 608 logging.error("name or value of system property " 609 "should not be None. No property is set.") 610 return 611 612 value = str(value) 613 614 if "'" in value or "\"" in value: 615 logging.error("Quotes in value of system property " 616 "is not yet supported. No property is set.") 617 return 618 619 self.adb.shell("setprop %s \"%s\"" % (name, value)) 620 621 def getProp(self, name): 622 """Calls getprop shell command. 623 624 Args: 625 name: string, the name of a system property to get 626 627 Returns: 628 string, value of the property. If name does not exist; an empty 629 string will be returned. decode("utf-8") and strip() will be called 630 on the output before returning; None will be returned if input 631 name is None 632 633 Raises: 634 AdbError, if name contains invalid character 635 """ 636 if name is None: 637 logging.error("name of system property should not be None.") 638 return None 639 640 out = self.adb.shell("getprop %s" % name) 641 return out.decode("utf-8").strip() 642 643 def reboot(self, restart_services=True): 644 """Reboots the device and wait for device to complete booting. 645 646 This is probably going to print some error messages in console. Only 647 use if there's no other option. 648 649 Raises: 650 AndroidDeviceError is raised if waiting for completion timed 651 out. 652 """ 653 if self.isBootloaderMode: 654 self.fastboot.reboot() 655 return 656 657 if restart_services: 658 has_adb_log = self.isAdbLogcatOn 659 has_vts_agent = True if self.vts_agent_process else False 660 if has_adb_log: 661 self.stopAdbLogcat() 662 if has_vts_agent: 663 self.stopVtsAgent() 664 665 self.adb.reboot() 666 self.waitForBootCompletion() 667 self.rootAdb() 668 669 if restart_services: 670 if has_adb_log: 671 self.startAdbLogcat() 672 if has_vts_agent: 673 self.startVtsAgent() 674 675 def startServices(self): 676 """Starts long running services on the android device. 677 678 1. Start adb logcat capture. 679 2. Start VtsAgent and create HalMirror unless disabled in config. 680 3. If enabled in config, start sl4a service and create sl4a clients. 681 """ 682 enable_vts_agent = getattr(self, "enable_vts_agent", True) 683 enable_sl4a = getattr(self, "enable_sl4a", False) 684 try: 685 self.startAdbLogcat() 686 except: 687 self.log.exception("Failed to start adb logcat!") 688 raise 689 if enable_vts_agent: 690 self.startVtsAgent() 691 self.device_command_port = int( 692 self.adb.shell("cat /data/local/tmp/vts_tcp_server_port")) 693 logging.info("device_command_port: %s", self.device_command_port) 694 if not self.host_command_port: 695 self.host_command_port = adb.get_available_host_port() 696 self.adb.tcp_forward(self.host_command_port, self.device_command_port) 697 self.hal = hal_mirror.HalMirror(self.host_command_port, 698 self.host_callback_port) 699 self.lib = lib_mirror.LibMirror(self.host_command_port) 700 self.shell = shell_mirror.ShellMirror(self.host_command_port) 701 if enable_sl4a: 702 self.startSl4aClient() 703 704 def stopServices(self): 705 """Stops long running services on the android device. 706 """ 707 if self.adb_logcat_process: 708 self.stopAdbLogcat() 709 self.stopVtsAgent() 710 if self.hal: 711 self.hal.CleanUp() 712 713 def startVtsAgent(self): 714 """Start HAL agent on the AndroidDevice. 715 716 This function starts the target side native agent and is persisted 717 throughout the test run. 718 """ 719 self.log.info("Starting VTS agent") 720 if self.vts_agent_process: 721 raise AndroidDeviceError("HAL agent is already running on %s." % 722 self.serial) 723 724 cleanup_commands = [ 725 "rm -f /data/local/tmp/vts_driver_*", 726 "rm -f /data/local/tmp/vts_agent_callback*" 727 ] 728 kill_commands = ["killall vts_hal_agent32", "killall vts_hal_agent64", 729 "killall fuzzer32", "killall fuzzer64", 730 "killall vts_shell_driver32", 731 "killall vts_shell_driver64"] 732 cleanup_commands.extend(kill_commands) 733 chmod_commands = [ 734 "chmod 755 %s/32/vts_hal_agent32" % DEFAULT_AGENT_BASE_DIR, 735 "chmod 755 %s/64/vts_hal_agent64" % DEFAULT_AGENT_BASE_DIR, 736 "chmod 755 %s/32/fuzzer32" % DEFAULT_AGENT_BASE_DIR, 737 "chmod 755 %s/64/fuzzer64" % DEFAULT_AGENT_BASE_DIR, 738 "chmod 755 %s/32/vts_shell_driver32" % DEFAULT_AGENT_BASE_DIR, 739 "chmod 755 %s/64/vts_shell_driver64" % DEFAULT_AGENT_BASE_DIR 740 ] 741 cleanup_commands.extend(chmod_commands) 742 for cmd in cleanup_commands: 743 try: 744 self.adb.shell(cmd) 745 except adb.AdbError as e: 746 self.log.warning( 747 "A command to setup the env to start the VTS Agent failed %s", 748 e) 749 750 bits = ['64', '32'] if self.is64Bit else ['32'] 751 for bitness in bits: 752 vts_agent_log_path = os.path.join(self.log_path, 753 "vts_agent_" + bitness + ".log") 754 cmd = ( 755 'adb -s {s} shell LD_LIBRARY_PATH={path}/{bitness} ' 756 '{path}/{bitness}/vts_hal_agent{bitness}' 757 ' {path}/32/fuzzer32 {path}/64/fuzzer64 {path}/spec' 758 ' {path}/32/vts_shell_driver32 {path}/64/vts_shell_driver64 >> {log} 2>&1' 759 ).format(s=self.serial, 760 bitness=bitness, 761 path=DEFAULT_AGENT_BASE_DIR, 762 log=vts_agent_log_path) 763 try: 764 self.vts_agent_process = utils.start_standing_subprocess( 765 cmd, check_health_delay=1) 766 break 767 except utils.VTSUtilsError as e: 768 logging.exception(e) 769 with open(vts_agent_log_path, 'r') as log_file: 770 logging.error("VTS agent output:\n") 771 logging.error(log_file.read()) 772 # one common cause is that 64-bit executable is not supported 773 # in low API level devices. 774 if bitness == '32': 775 raise 776 else: 777 logging.error('retrying using a 32-bit binary.') 778 779 def stopVtsAgent(self): 780 """Stop the HAL agent running on the AndroidDevice. 781 """ 782 if self.vts_agent_process: 783 utils.stop_standing_subprocess(self.vts_agent_process) 784 self.vts_agent_process = None 785 786 @property 787 def product_type(self): 788 """Gets the product type name.""" 789 return self._product_type 790 791 # Code for using SL4A client 792 def startSl4aClient(self, handle_event=True): 793 """Create an sl4a connection to the device. 794 795 Return the connection handler 'droid'. By default, another connection 796 on the same session is made for EventDispatcher, and the dispatcher is 797 returned to the caller as well. 798 If sl4a server is not started on the device, try to start it. 799 800 Args: 801 handle_event: True if this droid session will need to handle 802 events. 803 """ 804 self._sl4a_sessions = {} 805 self._sl4a_event_dispatchers = {} 806 if not self.sl4a_host_port or not adb.is_port_available(self.sl4a_host_port): 807 self.sl4a_host_port = adb.get_available_host_port() 808 self.adb.tcp_forward(self.sl4a_host_port, self.sl4a_target_port) 809 try: 810 droid = self._createNewSl4aSession() 811 except sl4a_client.Error: 812 sl4a_client.start_sl4a(self.adb) 813 droid = self._createNewSl4aSession() 814 self.sl4a = droid 815 if handle_event: 816 ed = self._getSl4aEventDispatcher(droid) 817 self.sl4a_event = ed 818 819 def getVintfXml(self): 820 """Return vendor interface manifest string.""" 821 # TODO: (b/36137939) use vintf instead of lshal. 822 try: 823 stdout = self.adb.shell('"lshal --init-vintf 2> /dev/null"') 824 return str(stdout) 825 except adb.AdbError as e: 826 return None 827 828 def _getSl4aEventDispatcher(self, droid): 829 """Return an EventDispatcher for an sl4a session 830 831 Args: 832 droid: Session to create EventDispatcher for. 833 834 Returns: 835 ed: An EventDispatcher for specified session. 836 """ 837 # TODO (angli): Move service-specific start/stop functions out of 838 # android_device, including VTS Agent, SL4A, and any other 839 # target-side services. 840 ed_key = self.serial + str(droid.uid) 841 if ed_key in self._sl4a_event_dispatchers: 842 if self._sl4a_event_dispatchers[ed_key] is None: 843 raise AndroidDeviceError("EventDispatcher Key Empty") 844 self.log.debug("Returning existing key %s for event dispatcher!", 845 ed_key) 846 return self._sl4a_event_dispatchers[ed_key] 847 event_droid = self._addNewConnectionToSl4aSession(droid.uid) 848 ed = event_dispatcher.EventDispatcher(event_droid) 849 self._sl4a_event_dispatchers[ed_key] = ed 850 return ed 851 852 def _createNewSl4aSession(self): 853 """Start a new session in sl4a. 854 855 Also caches the droid in a dict with its uid being the key. 856 857 Returns: 858 An Android object used to communicate with sl4a on the android 859 device. 860 861 Raises: 862 sl4a_client.Error: Something is wrong with sl4a and it returned an 863 existing uid to a new session. 864 """ 865 droid = sl4a_client.Sl4aClient(port=self.sl4a_host_port) 866 droid.open() 867 if droid.uid in self._sl4a_sessions: 868 raise sl4a_client.Error( 869 "SL4A returned an existing uid for a new session. Abort.") 870 self._sl4a_sessions[droid.uid] = [droid] 871 return droid 872 873 def _addNewConnectionToSl4aSession(self, session_id): 874 """Create a new connection to an existing sl4a session. 875 876 Args: 877 session_id: UID of the sl4a session to add connection to. 878 879 Returns: 880 An Android object used to communicate with sl4a on the android 881 device. 882 883 Raises: 884 DoesNotExistError: Raised if the session it's trying to connect to 885 does not exist. 886 """ 887 if session_id not in self._sl4a_sessions: 888 raise DoesNotExistError("Session %d doesn't exist." % session_id) 889 droid = sl4a_client.Sl4aClient(port=self.sl4a_host_port, uid=session_id) 890 droid.open(cmd=sl4a_client.Sl4aCommand.CONTINUE) 891 return droid 892 893 def _terminateSl4aSession(self, session_id): 894 """Terminate a session in sl4a. 895 896 Send terminate signal to sl4a server; stop dispatcher associated with 897 the session. Clear corresponding droids and dispatchers from cache. 898 899 Args: 900 session_id: UID of the sl4a session to terminate. 901 """ 902 if self._sl4a_sessions and (session_id in self._sl4a_sessions): 903 for droid in self._sl4a_sessions[session_id]: 904 droid.closeSl4aSession() 905 droid.close() 906 del self._sl4a_sessions[session_id] 907 ed_key = self.serial + str(session_id) 908 if ed_key in self._sl4a_event_dispatchers: 909 self._sl4a_event_dispatchers[ed_key].clean_up() 910 del self._sl4a_event_dispatchers[ed_key] 911 912 def _terminateAllSl4aSessions(self): 913 """Terminate all sl4a sessions on the AndroidDevice instance. 914 915 Terminate all sessions and clear caches. 916 """ 917 if self._sl4a_sessions: 918 session_ids = list(self._sl4a_sessions.keys()) 919 for session_id in session_ids: 920 try: 921 self._terminateSl4aSession(session_id) 922 except: 923 self.log.exception("Failed to terminate session %d.", 924 session_id) 925 if self.sl4a_host_port: 926 self.adb.forward("--remove tcp:%d" % self.sl4a_host_port) 927 self.sl4a_host_port = None 928 929 930class AndroidDeviceLoggerAdapter(logging.LoggerAdapter): 931 """A wrapper class that attaches a prefix to all log lines from an 932 AndroidDevice object. 933 """ 934 935 def process(self, msg, kwargs): 936 """Process every log message written via the wrapped logger object. 937 938 We are adding the prefix "[AndroidDevice|<serial>]" to all log lines. 939 940 Args: 941 msg: string, the original log message. 942 kwargs: dict, the key value pairs that can be used to modify the 943 original log message. 944 """ 945 msg = "[AndroidDevice|%s] %s" % (self.extra["serial"], msg) 946 return (msg, kwargs) 947