普通文本  |  112行  |  3.71 KB

#
# Copyright (C) 2016 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import logging
import os
import re
import shutil
import tempfile

from vts.runners.host import const
from vts.utils.python.mirror import mirror_object


class ShellMirror(mirror_object.MirrorObject):
    """The class that acts as the mirror to an Android device's shell terminal.

    Attributes:
        _client: the TCP client instance.
        _adb: An AdbProxy object used for interacting with the device via adb.
        enabled: bool, whether remote shell feature is enabled for the device.
    """

    TMP_FILE_PATTERN = "/data/local/tmp/nohup.*"

    def __init__(self, client, adb):
        super(ShellMirror, self).__init__(client)
        self._adb = adb
        self.enabled = True

    def Heal(self):
        """Performs a self healing.

        Includes self diagnosis that looks for any framework errors.

        Returns:
            bool, True if everything is ok; False otherwise.
        """
        res = True

        if self._client:
            res &= self._client.Heal()

        if not res:
            logging.error('Self diagnosis found problems in shell mirror.')

        return res

    def Execute(self, command, no_except=False):
        '''Execute remote shell commands on device.

        Args:
            command: string or a list of string, shell commands to execute on
                     device.
            no_except: bool, if set to True, no exception will be thrown and
                       error code will be -1 with error message on stderr.

        Returns:
            A dictionary containing shell command execution results
        '''
        if not self.enabled:
            # TODO(yuexima): use adb shell instead when RPC is disabled
            return {
                const.STDOUT: [""] * len(command),
                const.STDERR:
                ["VTS remote shell has been disabled."] * len(command),
                const.EXIT_CODE: [-2] * len(command)
            }
        result = self._client.ExecuteShellCommand(command, no_except)

        tmp_dir = tempfile.mkdtemp()
        pattern = re.compile(self.TMP_FILE_PATTERN)

        for result_val, result_type in zip(
            [result[const.STDOUT], result[const.STDERR]],
            ["stdout", "stderr"]):
            for index, val in enumerate(result_val):
                # If val is a tmp file name, pull the file and set the contents
                # to result.
                if pattern.match(val):
                    tmp_file = os.path.join(tmp_dir, result_type + str(index))
                    logging.debug("pulling file: %s to %s", val, tmp_file)
                    self._adb.pull(val, tmp_file)
                    result_val[index] = open(tmp_file, "r").read()
                    self._adb.shell("rm -f %s" % val)
                else:
                    result_val[index] = val

        shutil.rmtree(tmp_dir)
        logging.debug("resp for VTS_AGENT_COMMAND_EXECUTE_SHELL_COMMAND: %s",
                      result)
        return result

    def SetConnTimeout(self, timeout):
        """Set remote shell connection timeout.

        Args:
            timeout: int, TCP connection timeout in seconds.
        """
        self._client.timeout = timeout