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