• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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