769 lines
30 KiB
Python
Executable File
769 lines
30 KiB
Python
Executable File
#
|
|
# 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 json
|
|
import logging
|
|
import os
|
|
import socket
|
|
import time
|
|
|
|
from vts.proto import AndroidSystemControlMessage_pb2 as SysMsg_pb2
|
|
from vts.proto import ComponentSpecificationMessage_pb2 as CompSpecMsg_pb2
|
|
from vts.proto import VtsResourceControllerMessage_pb2 as ResControlMsg_pb2
|
|
from vts.runners.host import const
|
|
from vts.runners.host import errors
|
|
from vts.utils.python.mirror import mirror_object
|
|
|
|
from google.protobuf import text_format
|
|
|
|
TARGET_IP = os.environ.get("TARGET_IP", None)
|
|
TARGET_PORT = os.environ.get("TARGET_PORT", None)
|
|
_DEFAULT_SOCKET_TIMEOUT_SECS = 1800
|
|
_SOCKET_CONN_TIMEOUT_SECS = 60
|
|
_SOCKET_CONN_RETRY_NUMBER = 5
|
|
COMMAND_TYPE_NAME = {
|
|
1: "LIST_HALS",
|
|
2: "SET_HOST_INFO",
|
|
101: "CHECK_DRIVER_SERVICE",
|
|
102: "LAUNCH_DRIVER_SERVICE",
|
|
103: "VTS_AGENT_COMMAND_READ_SPECIFICATION",
|
|
201: "LIST_APIS",
|
|
202: "CALL_API",
|
|
203: "VTS_AGENT_COMMAND_GET_ATTRIBUTE",
|
|
301: "VTS_AGENT_COMMAND_EXECUTE_SHELL_COMMAND",
|
|
401: "VTS_FMQ_COMMAND",
|
|
402: "VTS_HIDL_MEMORY_COMMAND",
|
|
403: "VTS_HIDL_HANDLE_COMMAND"
|
|
}
|
|
|
|
|
|
class VtsTcpClient(object):
|
|
"""VTS TCP Client class.
|
|
|
|
Attribute:
|
|
NO_RESPONSE_MSG: string, error message when there is no response
|
|
from device.
|
|
FAIL_RESPONSE_MSG: string, error message when the device sends back
|
|
a failure message.
|
|
connection: a TCP socket instance.
|
|
channel: a file to write and read data.
|
|
error: string, ongoing tcp connection error. None means no error.
|
|
_mode: the connection mode (adb_forwarding or ssh_tunnel)
|
|
timeout: tcp connection timeout.
|
|
"""
|
|
|
|
NO_RESPONSE_MSG = "Framework error: TCP client did not receive response from device."
|
|
FAIL_RESPONSE_MSG = "Framework error: TCP client received unsuccessful response code."
|
|
|
|
def __init__(self,
|
|
mode="adb_forwarding",
|
|
timeout=_DEFAULT_SOCKET_TIMEOUT_SECS):
|
|
self.connection = None
|
|
self.channel = None
|
|
self._mode = mode
|
|
self.timeout = timeout
|
|
self.error = None
|
|
|
|
@property
|
|
def timeout(self):
|
|
"""Get TCP connection timeout.
|
|
|
|
This function assumes timeout property setter is in __init__before
|
|
any getter calls.
|
|
|
|
Returns:
|
|
int, timeout
|
|
"""
|
|
return self._timeout
|
|
|
|
@timeout.setter
|
|
def timeout(self, timeout):
|
|
"""Set TCP connection timeout.
|
|
|
|
Args:
|
|
timeout: int, TCP connection timeout in seconds.
|
|
"""
|
|
self._timeout = timeout
|
|
|
|
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 = self.error is None
|
|
|
|
if not res:
|
|
logging.error('Self diagnosis found problems in TCP client: %s', self.error)
|
|
|
|
return res
|
|
|
|
def Connect(self,
|
|
ip=TARGET_IP,
|
|
command_port=TARGET_PORT,
|
|
callback_port=None,
|
|
retry=_SOCKET_CONN_RETRY_NUMBER,
|
|
timeout=None):
|
|
"""Connects to a target device.
|
|
|
|
Args:
|
|
ip: string, the IP address of a target device.
|
|
command_port: int, the TCP port which can be used to connect to
|
|
a target device.
|
|
callback_port: int, the TCP port number of a host-side callback
|
|
server.
|
|
retry: int, the number of times to retry connecting before giving
|
|
up.
|
|
timeout: tcp connection timeout.
|
|
|
|
Returns:
|
|
True if success, False otherwise
|
|
|
|
Raises:
|
|
socket.error when the connection fails.
|
|
"""
|
|
if not command_port:
|
|
logging.error("ip %s, command_port %s, callback_port %s invalid",
|
|
ip, command_port, callback_port)
|
|
return False
|
|
|
|
for i in xrange(retry):
|
|
connection_timeout = self._timeout if timeout is None else timeout
|
|
try:
|
|
self.connection = socket.create_connection(
|
|
(ip, command_port), timeout=connection_timeout)
|
|
break
|
|
except socket.error as e:
|
|
# Wait a bit and retry.
|
|
logging.exception("Connect failed %s", e)
|
|
time.sleep(1)
|
|
if i + 1 == retry:
|
|
raise errors.VtsTcpClientCreationError(
|
|
"Couldn't connect to %s:%s" % (ip, command_port))
|
|
self.channel = self.connection.makefile(mode="brw")
|
|
|
|
if callback_port is not None:
|
|
self.SendCommand(
|
|
SysMsg_pb2.SET_HOST_INFO, callback_port=callback_port)
|
|
resp = self.RecvResponse()
|
|
if (resp.response_code != SysMsg_pb2.SUCCESS):
|
|
return False
|
|
return True
|
|
|
|
def Disconnect(self):
|
|
"""Disconnects from the target device.
|
|
|
|
TODO(yim): Send a msg to the target side to teardown handler session
|
|
and release memory before closing the socket.
|
|
"""
|
|
if self.connection is not None:
|
|
self.channel = None
|
|
self.connection.close()
|
|
self.connection = None
|
|
|
|
def ListHals(self, base_paths):
|
|
"""RPC to LIST_HALS."""
|
|
self.SendCommand(SysMsg_pb2.LIST_HALS, paths=base_paths)
|
|
resp = self.RecvResponse()
|
|
if (resp.response_code == SysMsg_pb2.SUCCESS):
|
|
return resp.file_names
|
|
return None
|
|
|
|
def CheckDriverService(self, service_name):
|
|
"""RPC to CHECK_DRIVER_SERVICE."""
|
|
self.SendCommand(
|
|
SysMsg_pb2.CHECK_DRIVER_SERVICE, service_name=service_name)
|
|
resp = self.RecvResponse()
|
|
return (resp.response_code == SysMsg_pb2.SUCCESS)
|
|
|
|
def LaunchDriverService(self,
|
|
driver_type,
|
|
service_name,
|
|
bits,
|
|
file_path=None,
|
|
target_class=None,
|
|
target_type=None,
|
|
target_version_major=None,
|
|
target_version_minor=None,
|
|
target_package=None,
|
|
target_component_name=None,
|
|
hw_binder_service_name=None,
|
|
is_test_hal=None):
|
|
"""RPC to LAUNCH_DRIVER_SERVICE.
|
|
|
|
Args:
|
|
driver_type: enum, type of the driver (shared lib, shell).
|
|
service_name: string, binder service name.
|
|
bits: int, whether a target driver binary is 64-bits or 32-bits.
|
|
file_path: string, the name of a target.
|
|
target_class: int, target class.
|
|
target_type: int, target type.
|
|
target_version_major: int, HAL major version, e.g. 1.0 -> 1.
|
|
target_version_minor: int, HAL minor version, e.g. 1.0 -> 0.
|
|
target_package: string, package name of a HIDL HAL.
|
|
target_component_name: string, name of a target component.
|
|
hw_binder_service_name: name of a HW Binder service to use.
|
|
is_test_hal: bool, whether the HAL service is a test HAL
|
|
(e.g. msgq).
|
|
|
|
Returns:
|
|
response code, -1 or 0 on failure, other values on success.
|
|
"""
|
|
logging.debug("service_name: %s", service_name)
|
|
logging.debug("file_path: %s", file_path)
|
|
logging.debug("bits: %s", bits)
|
|
logging.debug("driver_type: %s", driver_type)
|
|
self.SendCommand(
|
|
SysMsg_pb2.LAUNCH_DRIVER_SERVICE,
|
|
driver_type=driver_type,
|
|
service_name=service_name,
|
|
bits=bits,
|
|
file_path=file_path,
|
|
target_class=target_class,
|
|
target_type=target_type,
|
|
target_version_major=target_version_major,
|
|
target_version_minor=target_version_minor,
|
|
target_package=target_package,
|
|
target_component_name=target_component_name,
|
|
hw_binder_service_name=hw_binder_service_name,
|
|
is_test_hal=is_test_hal)
|
|
resp = self.RecvResponse()
|
|
logging.debug("resp for LAUNCH_DRIVER_SERVICE: %s", resp)
|
|
if driver_type == SysMsg_pb2.VTS_DRIVER_TYPE_HAL_HIDL \
|
|
or driver_type == SysMsg_pb2.VTS_DRIVER_TYPE_HAL_CONVENTIONAL \
|
|
or driver_type == SysMsg_pb2.VTS_DRIVER_TYPE_HAL_LEGACY:
|
|
if resp.response_code == SysMsg_pb2.SUCCESS:
|
|
return int(resp.result)
|
|
else:
|
|
return -1
|
|
else:
|
|
return (resp.response_code == SysMsg_pb2.SUCCESS)
|
|
|
|
def ListApis(self):
|
|
"""RPC to LIST_APIS."""
|
|
self.SendCommand(SysMsg_pb2.LIST_APIS)
|
|
resp = self.RecvResponse()
|
|
logging.debug("resp for LIST_APIS: %s", resp)
|
|
if (resp.response_code == SysMsg_pb2.SUCCESS):
|
|
return resp.spec
|
|
return None
|
|
|
|
def GetPythonDataOfVariableSpecMsg(self, var_spec_msg):
|
|
"""Returns the python native data structure for a given message.
|
|
|
|
Args:
|
|
var_spec_msg: VariableSpecificationMessage
|
|
|
|
Returns:
|
|
python native data structure (e.g., string, integer, list).
|
|
|
|
Raises:
|
|
VtsUnsupportedTypeError if unsupported type is specified.
|
|
VtsMalformedProtoStringError if StringDataValueMessage is
|
|
not populated.
|
|
"""
|
|
if var_spec_msg.type == CompSpecMsg_pb2.TYPE_SCALAR:
|
|
scalar_type = getattr(var_spec_msg, "scalar_type", "")
|
|
if scalar_type:
|
|
return getattr(var_spec_msg.scalar_value, scalar_type)
|
|
elif var_spec_msg.type == CompSpecMsg_pb2.TYPE_ENUM:
|
|
scalar_type = getattr(var_spec_msg, "scalar_type", "")
|
|
if scalar_type:
|
|
return getattr(var_spec_msg.scalar_value, scalar_type)
|
|
else:
|
|
return var_spec_msg.scalar_value.int32_t
|
|
elif var_spec_msg.type == CompSpecMsg_pb2.TYPE_STRING:
|
|
if hasattr(var_spec_msg, "string_value"):
|
|
return getattr(var_spec_msg.string_value, "message", "")
|
|
raise errors.VtsMalformedProtoStringError()
|
|
elif var_spec_msg.type == CompSpecMsg_pb2.TYPE_STRUCT:
|
|
result = {}
|
|
index = 1
|
|
for struct_value in var_spec_msg.struct_value:
|
|
if len(struct_value.name) > 0:
|
|
result[struct_value.
|
|
name] = self.GetPythonDataOfVariableSpecMsg(
|
|
struct_value)
|
|
else:
|
|
result["attribute%d" %
|
|
index] = self.GetPythonDataOfVariableSpecMsg(
|
|
struct_value)
|
|
index += 1
|
|
return result
|
|
elif var_spec_msg.type == CompSpecMsg_pb2.TYPE_UNION:
|
|
result = {}
|
|
index = 1
|
|
for union_value in var_spec_msg.union_value:
|
|
if len(union_value.name) > 0:
|
|
result[union_value.
|
|
name] = self.GetPythonDataOfVariableSpecMsg(
|
|
union_value)
|
|
else:
|
|
result["attribute%d" %
|
|
index] = self.GetPythonDataOfVariableSpecMsg(
|
|
union_value)
|
|
index += 1
|
|
return result
|
|
elif (var_spec_msg.type == CompSpecMsg_pb2.TYPE_VECTOR
|
|
or var_spec_msg.type == CompSpecMsg_pb2.TYPE_ARRAY):
|
|
result = []
|
|
for vector_value in var_spec_msg.vector_value:
|
|
result.append(
|
|
self.GetPythonDataOfVariableSpecMsg(vector_value))
|
|
return result
|
|
elif (var_spec_msg.type == CompSpecMsg_pb2.TYPE_HIDL_INTERFACE
|
|
or var_spec_msg.type == CompSpecMsg_pb2.TYPE_FMQ_SYNC
|
|
or var_spec_msg.type == CompSpecMsg_pb2.TYPE_FMQ_UNSYNC
|
|
or var_spec_msg.type == CompSpecMsg_pb2.TYPE_HIDL_MEMORY
|
|
or var_spec_msg.type == CompSpecMsg_pb2.TYPE_HANDLE):
|
|
logging.debug("var_spec_msg: %s", var_spec_msg)
|
|
return var_spec_msg
|
|
|
|
raise errors.VtsUnsupportedTypeError(
|
|
"unsupported type %s" % var_spec_msg.type)
|
|
|
|
def CallApi(self, arg, caller_uid=None):
|
|
"""RPC to CALL_API."""
|
|
self.SendCommand(SysMsg_pb2.CALL_API, arg=arg, caller_uid=caller_uid)
|
|
resp = self.RecvResponse()
|
|
resp_code = resp.response_code
|
|
if (resp_code == SysMsg_pb2.SUCCESS):
|
|
result = CompSpecMsg_pb2.FunctionSpecificationMessage()
|
|
if resp.result == "error":
|
|
raise errors.VtsTcpCommunicationError(
|
|
"API call error by the VTS driver.")
|
|
try:
|
|
text_format.Merge(resp.result, result)
|
|
except text_format.ParseError as e:
|
|
logging.exception(e)
|
|
logging.error("Paring error\n%s", resp.result)
|
|
if result.return_type.type == CompSpecMsg_pb2.TYPE_SUBMODULE:
|
|
logging.debug("returned a submodule spec")
|
|
logging.debug("spec: %s", result.return_type_submodule_spec)
|
|
return mirror_object.MirrorObject(
|
|
self, result.return_type_submodule_spec, None)
|
|
|
|
if result.HasField("return_type"): # For non-HIDL return value
|
|
result_value = result
|
|
else:
|
|
result_value = []
|
|
for return_type_hidl in result.return_type_hidl:
|
|
result_value.append(
|
|
self.GetPythonDataOfVariableSpecMsg(return_type_hidl))
|
|
|
|
if len(result.raw_coverage_data) > 0:
|
|
return result_value, {"coverage": result.raw_coverage_data}
|
|
else:
|
|
return result_value
|
|
|
|
logging.error("NOTICE - Likely a crash discovery!")
|
|
logging.error("SysMsg_pb2.SUCCESS is %s", SysMsg_pb2.SUCCESS)
|
|
raise errors.VtsTcpCommunicationError(
|
|
"RPC Error, response code for %s is %s" % (arg, resp_code))
|
|
|
|
def GetAttribute(self, arg):
|
|
"""RPC to VTS_AGENT_COMMAND_GET_ATTRIBUTE."""
|
|
self.SendCommand(SysMsg_pb2.VTS_AGENT_COMMAND_GET_ATTRIBUTE, arg=arg)
|
|
resp = self.RecvResponse()
|
|
resp_code = resp.response_code
|
|
if (resp_code == SysMsg_pb2.SUCCESS):
|
|
result = CompSpecMsg_pb2.FunctionSpecificationMessage()
|
|
if resp.result == "error":
|
|
raise errors.VtsTcpCommunicationError(
|
|
"Get attribute request failed on target.")
|
|
try:
|
|
text_format.Merge(resp.result, result)
|
|
except text_format.ParseError as e:
|
|
logging.exception(e)
|
|
logging.error("Paring error\n%s", resp.result)
|
|
if result.return_type.type == CompSpecMsg_pb2.TYPE_SUBMODULE:
|
|
logging.debug("returned a submodule spec")
|
|
logging.debug("spec: %s", result.return_type_submodule_spec)
|
|
return mirror_object.MirrorObject(
|
|
self, result.return_type_submodule_spec, None)
|
|
elif result.return_type.type == CompSpecMsg_pb2.TYPE_SCALAR:
|
|
return getattr(result.return_type.scalar_value,
|
|
result.return_type.scalar_type)
|
|
return result
|
|
logging.error("NOTICE - Likely a crash discovery!")
|
|
logging.error("SysMsg_pb2.SUCCESS is %s", SysMsg_pb2.SUCCESS)
|
|
raise errors.VtsTcpCommunicationError(
|
|
"RPC Error, response code for %s is %s" % (arg, resp_code))
|
|
|
|
def ExecuteShellCommand(self, command, no_except=False):
|
|
"""RPC to VTS_AGENT_COMMAND_EXECUTE_SHELL_COMMAND.
|
|
|
|
Args:
|
|
command: string or list or tuple, command to execute on device
|
|
no_except: bool, whether to throw exceptions. If set to True,
|
|
when exception happens, return code will be -1 and
|
|
str(err) will be in stderr. Result will maintain the
|
|
same length as with input command.
|
|
|
|
Returns:
|
|
dictionary of list, command results that contains stdout,
|
|
stderr, and exit_code.
|
|
"""
|
|
try:
|
|
return self.__ExecuteShellCommand(command)
|
|
except Exception as e:
|
|
self.error = e
|
|
if not no_except:
|
|
raise e
|
|
logging.exception(e)
|
|
return {
|
|
const.STDOUT: [""] * len(command),
|
|
const.STDERR: [str(e)] * len(command),
|
|
const.EXIT_CODE: [-1] * len(command)
|
|
}
|
|
|
|
def __ExecuteShellCommand(self, command):
|
|
"""RPC to VTS_AGENT_COMMAND_EXECUTE_SHELL_COMMAND.
|
|
|
|
Args:
|
|
command: string or list of string, command to execute on device
|
|
|
|
Returns:
|
|
dictionary of list, command results that contains stdout,
|
|
stderr, and exit_code.
|
|
"""
|
|
self.SendCommand(
|
|
SysMsg_pb2.VTS_AGENT_COMMAND_EXECUTE_SHELL_COMMAND,
|
|
shell_command=command)
|
|
resp = self.RecvResponse(retries=2)
|
|
logging.debug("resp for VTS_AGENT_COMMAND_EXECUTE_SHELL_COMMAND: %s",
|
|
resp)
|
|
|
|
stdout = []
|
|
stderr = []
|
|
exit_code = -1
|
|
|
|
if not resp:
|
|
logging.error(self.NO_RESPONSE_MSG)
|
|
stderr = [self.NO_RESPONSE_MSG]
|
|
self.error = self.NO_RESPONSE_MSG
|
|
elif resp.response_code != SysMsg_pb2.SUCCESS:
|
|
logging.error(self.FAIL_RESPONSE_MSG)
|
|
stderr = [self.FAIL_RESPONSE_MSG]
|
|
self.error = self.FAIL_RESPONSE_MSG
|
|
else:
|
|
stdout = resp.stdout_str
|
|
stderr = resp.stderr_str
|
|
exit_code = resp.exit_code
|
|
self.error = None
|
|
|
|
return {
|
|
const.STDOUT: stdout,
|
|
const.STDERR: stderr,
|
|
const.EXIT_CODE: exit_code
|
|
}
|
|
|
|
def SendFmqRequest(self, message):
|
|
"""Sends a command to the FMQ driver and receives the response.
|
|
|
|
Args:
|
|
message: FmqRequestMessage, message that contains the arguments
|
|
in the FMQ request.
|
|
|
|
Returns:
|
|
FmqResponseMessage, which includes all possible return value types,
|
|
including int, bool, and data read from the queue.
|
|
"""
|
|
self.SendCommand(SysMsg_pb2.VTS_FMQ_COMMAND, fmq_request=message)
|
|
resp = self.RecvResponse()
|
|
logging.debug("resp for VTS_FMQ_COMMAND: %s", resp)
|
|
return self.CheckResourceCommandResponse(
|
|
resp, getattr(resp, "fmq_response", None))
|
|
|
|
def SendHidlMemoryRequest(self, message):
|
|
"""Sends a command to the hidl_memory driver and receives the response.
|
|
|
|
Args:
|
|
message: HidlMemoryRequestMessage, message that contains the arguments
|
|
in the hidl_memory request.
|
|
|
|
Returns:
|
|
HidlMemoryResponseMessage, return values needed by the host-side caller.
|
|
"""
|
|
self.SendCommand(
|
|
SysMsg_pb2.VTS_HIDL_MEMORY_COMMAND, hidl_memory_request=message)
|
|
resp = self.RecvResponse()
|
|
logging.debug("resp for VTS_HIDL_MEMORY_COMMAND: %s", resp)
|
|
return self.CheckResourceCommandResponse(
|
|
resp, getattr(resp, "hidl_memory_response", None))
|
|
|
|
def SendHidlHandleRequest(self, message):
|
|
"""Sends a command to the hidl_handle driver and receives the response.
|
|
|
|
Args:
|
|
message: HidlHandleRequestMessage, message that contains the arguments
|
|
in the hidl_handle request.
|
|
|
|
Returns:
|
|
HidlHandleResponseMessage, return values needed by the host-side caller.
|
|
"""
|
|
self.SendCommand(
|
|
SysMsg_pb2.VTS_HIDL_HANDLE_COMMAND, hidl_handle_request=message)
|
|
resp = self.RecvResponse()
|
|
logging.debug("resp for VTS_HIDL_HANDLE_COMMAND: %s", resp)
|
|
return self.CheckResourceCommandResponse(
|
|
resp, getattr(resp, "hidl_handle_response", None))
|
|
|
|
@staticmethod
|
|
def CheckResourceCommandResponse(agent_response, resource_response):
|
|
"""Checks the response from a VTS resource command.
|
|
|
|
This function checks and logs framework errors such as not receiving
|
|
a response, receving a failure response. Then it checks the response
|
|
for the resource type is not None, and logs error if needed.
|
|
|
|
Args:
|
|
agent_response: AndroidSystemControlResponseMessage,
|
|
response from agent.
|
|
resource_response: FmqResponseMessage, HidlMemoryResponseMessage,
|
|
or HidlHandleResponseMessage, the response for
|
|
one of fmq, hidl_memory, and hidl_handle.
|
|
|
|
Returns:
|
|
resource_response that is passed in.
|
|
"""
|
|
if agent_response is None:
|
|
logging.error(VtsTcpClient.NO_RESPONSE_MSG)
|
|
elif agent_response.response_code != SysMsg_pb2.SUCCESS:
|
|
logging.error(VtsTcpClient.FAIL_RESPONSE_MSG)
|
|
elif resource_response is None:
|
|
logging.error("TCP client did not receive a response for " +
|
|
"the resource command.")
|
|
return resource_response
|
|
|
|
def Ping(self):
|
|
"""RPC to send a PING request.
|
|
|
|
Returns:
|
|
True if the agent is alive, False otherwise.
|
|
"""
|
|
self.SendCommand(SysMsg_pb2.PING)
|
|
resp = self.RecvResponse()
|
|
logging.debug("resp for PING: %s", resp)
|
|
if resp is not None and resp.response_code == SysMsg_pb2.SUCCESS:
|
|
return True
|
|
return False
|
|
|
|
def ReadSpecification(self,
|
|
interface_name,
|
|
target_class,
|
|
target_type,
|
|
target_version_major,
|
|
target_version_minor,
|
|
target_package,
|
|
recursive=False):
|
|
"""RPC to VTS_AGENT_COMMAND_READ_SPECIFICATION.
|
|
|
|
Args:
|
|
other args: see SendCommand
|
|
recursive: boolean, set to recursively read the imported
|
|
specification(s) and return the merged one.
|
|
"""
|
|
self.SendCommand(
|
|
SysMsg_pb2.VTS_AGENT_COMMAND_READ_SPECIFICATION,
|
|
service_name=interface_name,
|
|
target_class=target_class,
|
|
target_type=target_type,
|
|
target_version_major=target_version_major,
|
|
target_version_minor=target_version_minor,
|
|
target_package=target_package)
|
|
resp = self.RecvResponse(retries=2)
|
|
logging.debug("resp for VTS_AGENT_COMMAND_EXECUTE_READ_INTERFACE: %s",
|
|
resp)
|
|
logging.debug("proto: %s", resp.result)
|
|
result = CompSpecMsg_pb2.ComponentSpecificationMessage()
|
|
if resp.result == "error":
|
|
raise errors.VtsTcpCommunicationError(
|
|
"API call error by the VTS driver.")
|
|
try:
|
|
text_format.Merge(resp.result, result)
|
|
except text_format.ParseError as e:
|
|
logging.exception(e)
|
|
logging.error("Paring error\n%s", resp.result)
|
|
|
|
if recursive and hasattr(result, "import"):
|
|
for imported_interface in getattr(result, "import"):
|
|
if imported_interface == "android.hidl.base@1.0::types":
|
|
logging.warn("import android.hidl.base@1.0::types skipped")
|
|
continue
|
|
[package, version_str] = imported_interface.split("@")
|
|
[version_major,
|
|
version_minor] = (version_str.split("::")[0]).split(".")
|
|
imported_result = self.ReadSpecification(
|
|
imported_interface.split("::")[1],
|
|
# TODO(yim): derive target_class and
|
|
# target_type from package path or remove them
|
|
msg.component_class
|
|
if target_class is None else target_class,
|
|
msg.component_type if target_type is None else target_type,
|
|
int(version_major),
|
|
int(version_minor),
|
|
package)
|
|
# Merge the attributes from imported interface.
|
|
for attribute in imported_result.attribute:
|
|
imported_attribute = result.attribute.add()
|
|
imported_attribute.CopyFrom(attribute)
|
|
|
|
return result
|
|
|
|
def SendCommand(self,
|
|
command_type,
|
|
paths=None,
|
|
file_path=None,
|
|
bits=None,
|
|
target_class=None,
|
|
target_type=None,
|
|
target_version_major=None,
|
|
target_version_minor=None,
|
|
target_package=None,
|
|
target_component_name=None,
|
|
hw_binder_service_name=None,
|
|
is_test_hal=None,
|
|
module_name=None,
|
|
service_name=None,
|
|
callback_port=None,
|
|
driver_type=None,
|
|
shell_command=None,
|
|
caller_uid=None,
|
|
arg=None,
|
|
fmq_request=None,
|
|
hidl_memory_request=None,
|
|
hidl_handle_request=None):
|
|
"""Sends a command.
|
|
|
|
Args:
|
|
command_type: integer, the command type.
|
|
each of the other args are to fill in a field in
|
|
AndroidSystemControlCommandMessage.
|
|
"""
|
|
if not self.channel:
|
|
raise errors.VtsTcpCommunicationError(
|
|
"channel is None, unable to send command.")
|
|
|
|
command_msg = SysMsg_pb2.AndroidSystemControlCommandMessage()
|
|
command_msg.command_type = command_type
|
|
logging.debug("sending a command (type %s)",
|
|
COMMAND_TYPE_NAME[command_type])
|
|
if command_type == 202:
|
|
logging.debug("target API: %s", arg)
|
|
|
|
if target_class is not None:
|
|
command_msg.target_class = target_class
|
|
|
|
if target_type is not None:
|
|
command_msg.target_type = target_type
|
|
|
|
if target_version_major is not None:
|
|
command_msg.target_version_major = target_version_major
|
|
|
|
if target_version_minor is not None:
|
|
command_msg.target_version_minor = target_version_minor
|
|
|
|
if target_package is not None:
|
|
command_msg.target_package = target_package
|
|
|
|
if target_component_name is not None:
|
|
command_msg.target_component_name = target_component_name
|
|
|
|
if hw_binder_service_name is not None:
|
|
command_msg.hw_binder_service_name = hw_binder_service_name
|
|
|
|
if is_test_hal is not None:
|
|
command_msg.is_test_hal = is_test_hal
|
|
|
|
if module_name is not None:
|
|
command_msg.module_name = module_name
|
|
|
|
if service_name is not None:
|
|
command_msg.service_name = service_name
|
|
|
|
if driver_type is not None:
|
|
command_msg.driver_type = driver_type
|
|
|
|
if paths is not None:
|
|
command_msg.paths.extend(paths)
|
|
|
|
if file_path is not None:
|
|
command_msg.file_path = file_path
|
|
|
|
if bits is not None:
|
|
command_msg.bits = bits
|
|
|
|
if callback_port is not None:
|
|
command_msg.callback_port = callback_port
|
|
|
|
if caller_uid is not None:
|
|
command_msg.driver_caller_uid = caller_uid
|
|
|
|
if arg is not None:
|
|
command_msg.arg = arg
|
|
|
|
if shell_command is not None:
|
|
if isinstance(shell_command, (list, tuple)):
|
|
command_msg.shell_command.extend(shell_command)
|
|
else:
|
|
command_msg.shell_command.append(shell_command)
|
|
|
|
if fmq_request is not None:
|
|
command_msg.fmq_request.CopyFrom(fmq_request)
|
|
|
|
if hidl_memory_request is not None:
|
|
command_msg.hidl_memory_request.CopyFrom(hidl_memory_request)
|
|
|
|
if hidl_handle_request is not None:
|
|
command_msg.hidl_handle_request.CopyFrom(hidl_handle_request)
|
|
|
|
logging.debug("command %s" % command_msg)
|
|
message = command_msg.SerializeToString()
|
|
message_len = len(message)
|
|
logging.debug("sending %d bytes", message_len)
|
|
self.channel.write(str(message_len) + b'\n')
|
|
self.channel.write(message)
|
|
self.channel.flush()
|
|
|
|
def RecvResponse(self, retries=0):
|
|
"""Receives and parses the response, and returns the relevant ResponseMessage.
|
|
|
|
Args:
|
|
retries: an integer indicating the max number of retries in case of
|
|
session timeout error.
|
|
"""
|
|
for index in xrange(1 + retries):
|
|
try:
|
|
if index != 0:
|
|
logging.info("retrying...")
|
|
header = self.channel.readline().strip("\n")
|
|
length = int(header) if header else 0
|
|
logging.debug("resp %d bytes", length)
|
|
data = self.channel.read(length)
|
|
response_msg = SysMsg_pb2.AndroidSystemControlResponseMessage()
|
|
response_msg.ParseFromString(data)
|
|
logging.debug(
|
|
"Response %s", "success"
|
|
if response_msg.response_code == SysMsg_pb2.SUCCESS else
|
|
"fail")
|
|
return response_msg
|
|
except socket.timeout as e:
|
|
logging.exception(e)
|
|
return None
|