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 try: 533 utils.stop_standing_subprocess(self.adb_logcat_process) 534 except utils.VTSUtilsError as e: 535 logging.error("Cannot stop adb logcat. %s", e) 536 self.adb_logcat_process = None 537 538 def takeBugReport(self, test_name, begin_time): 539 """Takes a bug report on the device and stores it in a file. 540 541 Args: 542 test_name: Name of the test case that triggered this bug report. 543 begin_time: Logline format timestamp taken when the test started. 544 """ 545 br_path = os.path.join(self.log_path, "BugReports") 546 utils.create_dir(br_path) 547 base_name = ",%s,%s.txt" % (begin_time, self.serial) 548 test_name_len = utils.MAX_FILENAME_LEN - len(base_name) 549 out_name = test_name[:test_name_len] + base_name 550 full_out_path = os.path.join(br_path, out_name.replace(' ', '\ ')) 551 self.log.info("Taking bugreport for %s on %s", test_name, self.serial) 552 self.adb.bugreport(" > %s" % full_out_path) 553 self.log.info("Bugreport for %s taken at %s", test_name, full_out_path) 554 555 @utils.timeout(15 * 60) 556 def waitForBootCompletion(self): 557 """Waits for Android framework to broadcast ACTION_BOOT_COMPLETED. 558 559 This function times out after 15 minutes. 560 """ 561 try: 562 self.adb.wait_for_device() 563 except adb.AdbError as e: 564 # adb wait-for-device is not always possible in the lab 565 logging.exception(e) 566 while not self.hasBooted(): 567 time.sleep(5) 568 569 def hasBooted(self): 570 """Checks whether the device has booted. 571 572 Returns: 573 True if booted, False otherwise. 574 """ 575 try: 576 completed = self.getProp("sys.boot_completed") 577 if completed == '1': 578 return True 579 except adb.AdbError: 580 # adb shell calls may fail during certain period of booting 581 # process, which is normal. Ignoring these errors. 582 return False 583 584 def start(self): 585 """Starts Android runtime and waits for ACTION_BOOT_COMPLETED.""" 586 logging.info("starting Android Runtime") 587 self.adb.shell("start") 588 self.waitForBootCompletion() 589 logging.info("Android Runtime started") 590 591 def stop(self): 592 """Stops Android runtime.""" 593 logging.info("stopping Android Runtime") 594 self.adb.shell("stop") 595 self.setProp("sys.boot_completed", 0) 596 logging.info("Android Runtime stopped") 597 598 def setProp(self, name, value): 599 """Calls setprop shell command. 600 601 Args: 602 name: string, the name of a system property to set 603 value: any type, value will be converted to string. Quotes in value 604 is not supported at this time; if value contains a quote, 605 this method will log an error and return. 606 607 Raises: 608 AdbError, if name contains invalid character 609 """ 610 if name is None or value is None: 611 logging.error("name or value of system property " 612 "should not be None. No property is set.") 613 return 614 615 value = str(value) 616 617 if "'" in value or "\"" in value: 618 logging.error("Quotes in value of system property " 619 "is not yet supported. No property is set.") 620 return 621 622 self.adb.shell("setprop %s \"%s\"" % (name, value)) 623 624 def getProp(self, name): 625 """Calls getprop shell command. 626 627 Args: 628 name: string, the name of a system property to get 629 630 Returns: 631 string, value of the property. If name does not exist; an empty 632 string will be returned. decode("utf-8") and strip() will be called 633 on the output before returning; None will be returned if input 634 name is None 635 636 Raises: 637 AdbError, if name contains invalid character 638 """ 639 if name is None: 640 logging.error("name of system property should not be None.") 641 return None 642 643 out = self.adb.shell("getprop %s" % name) 644 return out.decode("utf-8").strip() 645 646 def reboot(self, restart_services=True): 647 """Reboots the device and wait for device to complete booting. 648 649 This is probably going to print some error messages in console. Only 650 use if there's no other option. 651 652 Raises: 653 AndroidDeviceError is raised if waiting for completion timed 654 out. 655 """ 656 if self.isBootloaderMode: 657 self.fastboot.reboot() 658 return 659 660 if restart_services: 661 has_adb_log = self.isAdbLogcatOn 662 has_vts_agent = True if self.vts_agent_process else False 663 if has_adb_log: 664 self.stopAdbLogcat() 665 if has_vts_agent: 666 self.stopVtsAgent() 667 668 self.adb.reboot() 669 self.waitForBootCompletion() 670 self.rootAdb() 671 672 if restart_services: 673 if has_adb_log: 674 self.startAdbLogcat() 675 if has_vts_agent: 676 self.startVtsAgent() 677 678 def startServices(self): 679 """Starts long running services on the android device. 680 681 1. Start adb logcat capture. 682 2. Start VtsAgent and create HalMirror unless disabled in config. 683 3. If enabled in config, start sl4a service and create sl4a clients. 684 """ 685 enable_vts_agent = getattr(self, "enable_vts_agent", True) 686 enable_sl4a = getattr(self, "enable_sl4a", False) 687 try: 688 self.startAdbLogcat() 689 except: 690 self.log.exception("Failed to start adb logcat!") 691 raise 692 if enable_vts_agent: 693 self.startVtsAgent() 694 self.device_command_port = int( 695 self.adb.shell("cat /data/local/tmp/vts_tcp_server_port")) 696 logging.info("device_command_port: %s", self.device_command_port) 697 if not self.host_command_port: 698 self.host_command_port = adb.get_available_host_port() 699 self.adb.tcp_forward(self.host_command_port, self.device_command_port) 700 self.hal = hal_mirror.HalMirror(self.host_command_port, 701 self.host_callback_port) 702 self.lib = lib_mirror.LibMirror(self.host_command_port) 703 self.shell = shell_mirror.ShellMirror(self.host_command_port) 704 if enable_sl4a: 705 self.startSl4aClient() 706 707 def stopServices(self): 708 """Stops long running services on the android device. 709 """ 710 if self.adb_logcat_process: 711 self.stopAdbLogcat() 712 self.stopVtsAgent() 713 if self.hal: 714 self.hal.CleanUp() 715 716 def startVtsAgent(self): 717 """Start HAL agent on the AndroidDevice. 718 719 This function starts the target side native agent and is persisted 720 throughout the test run. 721 """ 722 self.log.info("Starting VTS agent") 723 if self.vts_agent_process: 724 raise AndroidDeviceError("HAL agent is already running on %s." % 725 self.serial) 726 727 cleanup_commands = [ 728 "rm -f /data/local/tmp/vts_driver_*", 729 "rm -f /data/local/tmp/vts_agent_callback*" 730 ] 731 kill_commands = ["killall vts_hal_agent32", "killall vts_hal_agent64", 732 "killall vts_hal_driver32", 733 "killall vts_hal_driver64", 734 "killall vts_shell_driver32", 735 "killall vts_shell_driver64"] 736 cleanup_commands.extend(kill_commands) 737 chmod_commands = [ 738 "chmod 755 %s/32/vts_hal_agent32" % DEFAULT_AGENT_BASE_DIR, 739 "chmod 755 %s/64/vts_hal_agent64" % DEFAULT_AGENT_BASE_DIR, 740 "chmod 755 %s/32/vts_hal_driver32" % DEFAULT_AGENT_BASE_DIR, 741 "chmod 755 %s/64/vts_hal_driver64" % DEFAULT_AGENT_BASE_DIR, 742 "chmod 755 %s/32/vts_shell_driver32" % DEFAULT_AGENT_BASE_DIR, 743 "chmod 755 %s/64/vts_shell_driver64" % DEFAULT_AGENT_BASE_DIR 744 ] 745 cleanup_commands.extend(chmod_commands) 746 for cmd in cleanup_commands: 747 try: 748 self.adb.shell(cmd) 749 except adb.AdbError as e: 750 self.log.warning( 751 "A command to setup the env to start the VTS Agent failed %s", 752 e) 753 754 bits = ['64', '32'] if self.is64Bit else ['32'] 755 for bitness in bits: 756 vts_agent_log_path = os.path.join(self.log_path, 757 "vts_agent_" + bitness + ".log") 758 cmd = ( 759 'adb -s {s} shell LD_LIBRARY_PATH={path}/{bitness} ' 760 '{path}/{bitness}/vts_hal_agent{bitness}' 761 ' {path}/32/vts_hal_driver32 {path}/64/vts_hal_driver64 {path}/spec' 762 ' {path}/32/vts_shell_driver32 {path}/64/vts_shell_driver64 >> {log} 2>&1' 763 ).format(s=self.serial, 764 bitness=bitness, 765 path=DEFAULT_AGENT_BASE_DIR, 766 log=vts_agent_log_path) 767 try: 768 self.vts_agent_process = utils.start_standing_subprocess( 769 cmd, check_health_delay=1) 770 break 771 except utils.VTSUtilsError as e: 772 logging.exception(e) 773 with open(vts_agent_log_path, 'r') as log_file: 774 logging.error("VTS agent output:\n") 775 logging.error(log_file.read()) 776 # one common cause is that 64-bit executable is not supported 777 # in low API level devices. 778 if bitness == '32': 779 raise 780 else: 781 logging.error('retrying using a 32-bit binary.') 782 783 def stopVtsAgent(self): 784 """Stop the HAL agent running on the AndroidDevice. 785 """ 786 if not self.vts_agent_process: 787 return 788 try: 789 utils.stop_standing_subprocess(self.vts_agent_process) 790 except utils.VTSUtilsError as e: 791 logging.error("Cannot stop VTS agent. %s", e) 792 self.vts_agent_process = None 793 794 @property 795 def product_type(self): 796 """Gets the product type name.""" 797 return self._product_type 798 799 # Code for using SL4A client 800 def startSl4aClient(self, handle_event=True): 801 """Create an sl4a connection to the device. 802 803 Return the connection handler 'droid'. By default, another connection 804 on the same session is made for EventDispatcher, and the dispatcher is 805 returned to the caller as well. 806 If sl4a server is not started on the device, try to start it. 807 808 Args: 809 handle_event: True if this droid session will need to handle 810 events. 811 """ 812 self._sl4a_sessions = {} 813 self._sl4a_event_dispatchers = {} 814 if not self.sl4a_host_port or not adb.is_port_available(self.sl4a_host_port): 815 self.sl4a_host_port = adb.get_available_host_port() 816 self.adb.tcp_forward(self.sl4a_host_port, self.sl4a_target_port) 817 try: 818 droid = self._createNewSl4aSession() 819 except sl4a_client.Error: 820 sl4a_client.start_sl4a(self.adb) 821 droid = self._createNewSl4aSession() 822 self.sl4a = droid 823 if handle_event: 824 ed = self._getSl4aEventDispatcher(droid) 825 self.sl4a_event = ed 826 827 def getVintfXml(self, use_lshal=True): 828 """Reads the vendor interface manifest Xml. 829 830 Args: 831 use_hal: bool, set True to use lshal command and False to fetch 832 /vendor/manifest.xml directly. 833 834 Returns: 835 Vendor interface manifest string. 836 """ 837 try: 838 if use_lshal: 839 stdout = self.adb.shell('"lshal --init-vintf 2> /dev/null"') 840 return str(stdout) 841 else: 842 stdout = self.adb.shell('cat /vendor/manifest.xml') 843 return str(stdout) 844 except adb.AdbError as e: 845 return None 846 847 def _getSl4aEventDispatcher(self, droid): 848 """Return an EventDispatcher for an sl4a session 849 850 Args: 851 droid: Session to create EventDispatcher for. 852 853 Returns: 854 ed: An EventDispatcher for specified session. 855 """ 856 # TODO (angli): Move service-specific start/stop functions out of 857 # android_device, including VTS Agent, SL4A, and any other 858 # target-side services. 859 ed_key = self.serial + str(droid.uid) 860 if ed_key in self._sl4a_event_dispatchers: 861 if self._sl4a_event_dispatchers[ed_key] is None: 862 raise AndroidDeviceError("EventDispatcher Key Empty") 863 self.log.debug("Returning existing key %s for event dispatcher!", 864 ed_key) 865 return self._sl4a_event_dispatchers[ed_key] 866 event_droid = self._addNewConnectionToSl4aSession(droid.uid) 867 ed = event_dispatcher.EventDispatcher(event_droid) 868 self._sl4a_event_dispatchers[ed_key] = ed 869 return ed 870 871 def _createNewSl4aSession(self): 872 """Start a new session in sl4a. 873 874 Also caches the droid in a dict with its uid being the key. 875 876 Returns: 877 An Android object used to communicate with sl4a on the android 878 device. 879 880 Raises: 881 sl4a_client.Error: Something is wrong with sl4a and it returned an 882 existing uid to a new session. 883 """ 884 droid = sl4a_client.Sl4aClient(port=self.sl4a_host_port) 885 droid.open() 886 if droid.uid in self._sl4a_sessions: 887 raise sl4a_client.Error( 888 "SL4A returned an existing uid for a new session. Abort.") 889 self._sl4a_sessions[droid.uid] = [droid] 890 return droid 891 892 def _addNewConnectionToSl4aSession(self, session_id): 893 """Create a new connection to an existing sl4a session. 894 895 Args: 896 session_id: UID of the sl4a session to add connection to. 897 898 Returns: 899 An Android object used to communicate with sl4a on the android 900 device. 901 902 Raises: 903 DoesNotExistError: Raised if the session it's trying to connect to 904 does not exist. 905 """ 906 if session_id not in self._sl4a_sessions: 907 raise DoesNotExistError("Session %d doesn't exist." % session_id) 908 droid = sl4a_client.Sl4aClient(port=self.sl4a_host_port, uid=session_id) 909 droid.open(cmd=sl4a_client.Sl4aCommand.CONTINUE) 910 return droid 911 912 def _terminateSl4aSession(self, session_id): 913 """Terminate a session in sl4a. 914 915 Send terminate signal to sl4a server; stop dispatcher associated with 916 the session. Clear corresponding droids and dispatchers from cache. 917 918 Args: 919 session_id: UID of the sl4a session to terminate. 920 """ 921 if self._sl4a_sessions and (session_id in self._sl4a_sessions): 922 for droid in self._sl4a_sessions[session_id]: 923 droid.closeSl4aSession() 924 droid.close() 925 del self._sl4a_sessions[session_id] 926 ed_key = self.serial + str(session_id) 927 if ed_key in self._sl4a_event_dispatchers: 928 self._sl4a_event_dispatchers[ed_key].clean_up() 929 del self._sl4a_event_dispatchers[ed_key] 930 931 def _terminateAllSl4aSessions(self): 932 """Terminate all sl4a sessions on the AndroidDevice instance. 933 934 Terminate all sessions and clear caches. 935 """ 936 if self._sl4a_sessions: 937 session_ids = list(self._sl4a_sessions.keys()) 938 for session_id in session_ids: 939 try: 940 self._terminateSl4aSession(session_id) 941 except: 942 self.log.exception("Failed to terminate session %d.", 943 session_id) 944 if self.sl4a_host_port: 945 self.adb.forward("--remove tcp:%d" % self.sl4a_host_port) 946 self.sl4a_host_port = None 947 948 949class AndroidDeviceLoggerAdapter(logging.LoggerAdapter): 950 """A wrapper class that attaches a prefix to all log lines from an 951 AndroidDevice object. 952 """ 953 954 def process(self, msg, kwargs): 955 """Process every log message written via the wrapped logger object. 956 957 We are adding the prefix "[AndroidDevice|<serial>]" to all log lines. 958 959 Args: 960 msg: string, the original log message. 961 kwargs: dict, the key value pairs that can be used to modify the 962 original log message. 963 """ 964 msg = "[AndroidDevice|%s] %s" % (self.extra["serial"], msg) 965 return (msg, kwargs) 966