| # |
| # 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 |