1#
2# Copyright (C) 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#
16
17import json
18import logging
19import os
20import socket
21import time
22
23from vts.proto import AndroidSystemControlMessage_pb2 as SysMsg_pb2
24from vts.proto import ComponentSpecificationMessage_pb2 as CompSpecMsg_pb2
25from vts.proto import VtsResourceControllerMessage_pb2 as ResControlMsg_pb2
26from vts.runners.host import const
27from vts.runners.host import errors
28from vts.utils.python.mirror import mirror_object
29
30from google.protobuf import text_format
31
32TARGET_IP = os.environ.get("TARGET_IP", None)
33TARGET_PORT = os.environ.get("TARGET_PORT", None)
34_DEFAULT_SOCKET_TIMEOUT_SECS = 1800
35_SOCKET_CONN_TIMEOUT_SECS = 60
36_SOCKET_CONN_RETRY_NUMBER = 5
37COMMAND_TYPE_NAME = {
38    1: "LIST_HALS",
39    2: "SET_HOST_INFO",
40    101: "CHECK_DRIVER_SERVICE",
41    102: "LAUNCH_DRIVER_SERVICE",
42    103: "VTS_AGENT_COMMAND_READ_SPECIFICATION",
43    201: "LIST_APIS",
44    202: "CALL_API",
45    203: "VTS_AGENT_COMMAND_GET_ATTRIBUTE",
46    301: "VTS_AGENT_COMMAND_EXECUTE_SHELL_COMMAND",
47    401: "VTS_FMQ_COMMAND",
48    402: "VTS_HIDL_MEMORY_COMMAND",
49    403: "VTS_HIDL_HANDLE_COMMAND"
50}
51
52
53class VtsTcpClient(object):
54    """VTS TCP Client class.
55
56    Attribute:
57        NO_RESPONSE_MSG: string, error message when there is no response
58                         from device.
59        FAIL_RESPONSE_MSG: string, error message when the device sends back
60                           a failure message.
61        connection: a TCP socket instance.
62        channel: a file to write and read data.
63        error: string, ongoing tcp connection error. None means no error.
64        _mode: the connection mode (adb_forwarding or ssh_tunnel)
65        timeout: tcp connection timeout.
66    """
67
68    NO_RESPONSE_MSG = "Framework error: TCP client did not receive response from device."
69    FAIL_RESPONSE_MSG = "Framework error: TCP client received unsuccessful response code."
70
71    def __init__(self,
72                 mode="adb_forwarding",
73                 timeout=_DEFAULT_SOCKET_TIMEOUT_SECS):
74        self.connection = None
75        self.channel = None
76        self._mode = mode
77        self.timeout = timeout
78        self.error = None
79
80    @property
81    def timeout(self):
82        """Get TCP connection timeout.
83
84        This function assumes timeout property setter is in __init__before
85        any getter calls.
86
87        Returns:
88            int, timeout
89        """
90        return self._timeout
91
92    @timeout.setter
93    def timeout(self, timeout):
94        """Set TCP connection timeout.
95
96        Args:
97            timeout: int, TCP connection timeout in seconds.
98        """
99        self._timeout = timeout
100
101    def Heal(self):
102        """Performs a self healing.
103
104        Includes self diagnosis that looks for any framework errors.
105
106        Returns:
107            bool, True if everything is ok; False otherwise.
108        """
109        res = self.error is None
110
111        if not res:
112            logging.error('Self diagnosis found problems in TCP client: %s', self.error)
113
114        return res
115
116    def Connect(self,
117                ip=TARGET_IP,
118                command_port=TARGET_PORT,
119                callback_port=None,
120                retry=_SOCKET_CONN_RETRY_NUMBER,
121                timeout=None):
122        """Connects to a target device.
123
124        Args:
125            ip: string, the IP address of a target device.
126            command_port: int, the TCP port which can be used to connect to
127                          a target device.
128            callback_port: int, the TCP port number of a host-side callback
129                           server.
130            retry: int, the number of times to retry connecting before giving
131                   up.
132            timeout: tcp connection timeout.
133
134        Returns:
135            True if success, False otherwise
136
137        Raises:
138            socket.error when the connection fails.
139        """
140        if not command_port:
141            logging.error("ip %s, command_port %s, callback_port %s invalid",
142                          ip, command_port, callback_port)
143            return False
144
145        for i in xrange(retry):
146            connection_timeout = self._timeout if timeout is None else timeout
147            try:
148                self.connection = socket.create_connection(
149                    (ip, command_port), timeout=connection_timeout)
150                break
151            except socket.error as e:
152                # Wait a bit and retry.
153                logging.exception("Connect failed %s", e)
154                time.sleep(1)
155                if i + 1 == retry:
156                    raise errors.VtsTcpClientCreationError(
157                        "Couldn't connect to %s:%s" % (ip, command_port))
158        self.channel = self.connection.makefile(mode="brw")
159
160        if callback_port is not None:
161            self.SendCommand(
162                SysMsg_pb2.SET_HOST_INFO, callback_port=callback_port)
163            resp = self.RecvResponse()
164            if (resp.response_code != SysMsg_pb2.SUCCESS):
165                return False
166        return True
167
168    def Disconnect(self):
169        """Disconnects from the target device.
170
171        TODO(yim): Send a msg to the target side to teardown handler session
172        and release memory before closing the socket.
173        """
174        if self.connection is not None:
175            self.channel = None
176            self.connection.close()
177            self.connection = None
178
179    def ListHals(self, base_paths):
180        """RPC to LIST_HALS."""
181        self.SendCommand(SysMsg_pb2.LIST_HALS, paths=base_paths)
182        resp = self.RecvResponse()
183        if (resp.response_code == SysMsg_pb2.SUCCESS):
184            return resp.file_names
185        return None
186
187    def CheckDriverService(self, service_name):
188        """RPC to CHECK_DRIVER_SERVICE."""
189        self.SendCommand(
190            SysMsg_pb2.CHECK_DRIVER_SERVICE, service_name=service_name)
191        resp = self.RecvResponse()
192        return (resp.response_code == SysMsg_pb2.SUCCESS)
193
194    def LaunchDriverService(self,
195                            driver_type,
196                            service_name,
197                            bits,
198                            file_path=None,
199                            target_class=None,
200                            target_type=None,
201                            target_version_major=None,
202                            target_version_minor=None,
203                            target_package=None,
204                            target_component_name=None,
205                            hw_binder_service_name=None,
206                            is_test_hal=None):
207        """RPC to LAUNCH_DRIVER_SERVICE.
208
209           Args:
210               driver_type: enum, type of the driver (shared lib, shell).
211               service_name: string, binder service name.
212               bits: int, whether a target driver binary is 64-bits or 32-bits.
213               file_path: string, the name of a target.
214               target_class: int, target class.
215               target_type: int, target type.
216               target_version_major: int, HAL major version, e.g. 1.0 -> 1.
217               target_version_minor: int, HAL minor version, e.g. 1.0 -> 0.
218               target_package: string, package name of a HIDL HAL.
219               target_component_name: string, name of a target component.
220               hw_binder_service_name: name of a HW Binder service to use.
221               is_test_hal: bool, whether the HAL service is a test HAL
222                            (e.g. msgq).
223
224           Returns:
225               response code, -1 or 0 on failure, other values on success.
226        """
227        logging.debug("service_name: %s", service_name)
228        logging.debug("file_path: %s", file_path)
229        logging.debug("bits: %s", bits)
230        logging.debug("driver_type: %s", driver_type)
231        self.SendCommand(
232            SysMsg_pb2.LAUNCH_DRIVER_SERVICE,
233            driver_type=driver_type,
234            service_name=service_name,
235            bits=bits,
236            file_path=file_path,
237            target_class=target_class,
238            target_type=target_type,
239            target_version_major=target_version_major,
240            target_version_minor=target_version_minor,
241            target_package=target_package,
242            target_component_name=target_component_name,
243            hw_binder_service_name=hw_binder_service_name,
244            is_test_hal=is_test_hal)
245        resp = self.RecvResponse()
246        logging.debug("resp for LAUNCH_DRIVER_SERVICE: %s", resp)
247        if driver_type == SysMsg_pb2.VTS_DRIVER_TYPE_HAL_HIDL \
248                or driver_type == SysMsg_pb2.VTS_DRIVER_TYPE_HAL_CONVENTIONAL \
249                or driver_type == SysMsg_pb2.VTS_DRIVER_TYPE_HAL_LEGACY:
250            if resp.response_code == SysMsg_pb2.SUCCESS:
251                return int(resp.result)
252            else:
253                return -1
254        else:
255            return (resp.response_code == SysMsg_pb2.SUCCESS)
256
257    def ListApis(self):
258        """RPC to LIST_APIS."""
259        self.SendCommand(SysMsg_pb2.LIST_APIS)
260        resp = self.RecvResponse()
261        logging.debug("resp for LIST_APIS: %s", resp)
262        if (resp.response_code == SysMsg_pb2.SUCCESS):
263            return resp.spec
264        return None
265
266    def GetPythonDataOfVariableSpecMsg(self, var_spec_msg):
267        """Returns the python native data structure for a given message.
268
269        Args:
270            var_spec_msg: VariableSpecificationMessage
271
272        Returns:
273            python native data structure (e.g., string, integer, list).
274
275        Raises:
276            VtsUnsupportedTypeError if unsupported type is specified.
277            VtsMalformedProtoStringError if StringDataValueMessage is
278                not populated.
279        """
280        if var_spec_msg.type == CompSpecMsg_pb2.TYPE_SCALAR:
281            scalar_type = getattr(var_spec_msg, "scalar_type", "")
282            if scalar_type:
283                return getattr(var_spec_msg.scalar_value, scalar_type)
284        elif var_spec_msg.type == CompSpecMsg_pb2.TYPE_ENUM:
285            scalar_type = getattr(var_spec_msg, "scalar_type", "")
286            if scalar_type:
287                return getattr(var_spec_msg.scalar_value, scalar_type)
288            else:
289                return var_spec_msg.scalar_value.int32_t
290        elif var_spec_msg.type == CompSpecMsg_pb2.TYPE_STRING:
291            if hasattr(var_spec_msg, "string_value"):
292                return getattr(var_spec_msg.string_value, "message", "")
293            raise errors.VtsMalformedProtoStringError()
294        elif var_spec_msg.type == CompSpecMsg_pb2.TYPE_STRUCT:
295            result = {}
296            index = 1
297            for struct_value in var_spec_msg.struct_value:
298                if len(struct_value.name) > 0:
299                    result[struct_value.
300                           name] = self.GetPythonDataOfVariableSpecMsg(
301                               struct_value)
302                else:
303                    result["attribute%d" %
304                           index] = self.GetPythonDataOfVariableSpecMsg(
305                               struct_value)
306                index += 1
307            return result
308        elif var_spec_msg.type == CompSpecMsg_pb2.TYPE_UNION:
309            result = {}
310            index = 1
311            for union_value in var_spec_msg.union_value:
312                if len(union_value.name) > 0:
313                    result[union_value.
314                           name] = self.GetPythonDataOfVariableSpecMsg(
315                               union_value)
316                else:
317                    result["attribute%d" %
318                           index] = self.GetPythonDataOfVariableSpecMsg(
319                               union_value)
320                index += 1
321            return result
322        elif (var_spec_msg.type == CompSpecMsg_pb2.TYPE_VECTOR
323              or var_spec_msg.type == CompSpecMsg_pb2.TYPE_ARRAY):
324            result = []
325            for vector_value in var_spec_msg.vector_value:
326                result.append(
327                    self.GetPythonDataOfVariableSpecMsg(vector_value))
328            return result
329        elif (var_spec_msg.type == CompSpecMsg_pb2.TYPE_HIDL_INTERFACE
330              or var_spec_msg.type == CompSpecMsg_pb2.TYPE_FMQ_SYNC
331              or var_spec_msg.type == CompSpecMsg_pb2.TYPE_FMQ_UNSYNC
332              or var_spec_msg.type == CompSpecMsg_pb2.TYPE_HIDL_MEMORY
333              or var_spec_msg.type == CompSpecMsg_pb2.TYPE_HANDLE):
334            logging.debug("var_spec_msg: %s", var_spec_msg)
335            return var_spec_msg
336
337        raise errors.VtsUnsupportedTypeError(
338            "unsupported type %s" % var_spec_msg.type)
339
340    def CallApi(self, arg, caller_uid=None):
341        """RPC to CALL_API."""
342        self.SendCommand(SysMsg_pb2.CALL_API, arg=arg, caller_uid=caller_uid)
343        resp = self.RecvResponse()
344        resp_code = resp.response_code
345        if (resp_code == SysMsg_pb2.SUCCESS):
346            result = CompSpecMsg_pb2.FunctionSpecificationMessage()
347            if resp.result == "error":
348                raise errors.VtsTcpCommunicationError(
349                    "API call error by the VTS driver.")
350            try:
351                text_format.Merge(resp.result, result)
352            except text_format.ParseError as e:
353                logging.exception(e)
354                logging.error("Paring error\n%s", resp.result)
355            if result.return_type.type == CompSpecMsg_pb2.TYPE_SUBMODULE:
356                logging.debug("returned a submodule spec")
357                logging.debug("spec: %s", result.return_type_submodule_spec)
358                return mirror_object.MirrorObject(
359                    self, result.return_type_submodule_spec, None)
360
361            if result.HasField("return_type"):  # For non-HIDL return value
362                result_value = result
363            else:
364                result_value = []
365                for return_type_hidl in result.return_type_hidl:
366                    result_value.append(
367                        self.GetPythonDataOfVariableSpecMsg(return_type_hidl))
368
369            if len(result.raw_coverage_data) > 0:
370                return result_value, {"coverage": result.raw_coverage_data}
371            else:
372                return result_value
373
374        logging.error("NOTICE - Likely a crash discovery!")
375        logging.error("SysMsg_pb2.SUCCESS is %s", SysMsg_pb2.SUCCESS)
376        raise errors.VtsTcpCommunicationError(
377            "RPC Error, response code for %s is %s" % (arg, resp_code))
378
379    def GetAttribute(self, arg):
380        """RPC to VTS_AGENT_COMMAND_GET_ATTRIBUTE."""
381        self.SendCommand(SysMsg_pb2.VTS_AGENT_COMMAND_GET_ATTRIBUTE, arg=arg)
382        resp = self.RecvResponse()
383        resp_code = resp.response_code
384        if (resp_code == SysMsg_pb2.SUCCESS):
385            result = CompSpecMsg_pb2.FunctionSpecificationMessage()
386            if resp.result == "error":
387                raise errors.VtsTcpCommunicationError(
388                    "Get attribute request failed on target.")
389            try:
390                text_format.Merge(resp.result, result)
391            except text_format.ParseError as e:
392                logging.exception(e)
393                logging.error("Paring error\n%s", resp.result)
394            if result.return_type.type == CompSpecMsg_pb2.TYPE_SUBMODULE:
395                logging.debug("returned a submodule spec")
396                logging.debug("spec: %s", result.return_type_submodule_spec)
397                return mirror_object.MirrorObject(
398                    self, result.return_type_submodule_spec, None)
399            elif result.return_type.type == CompSpecMsg_pb2.TYPE_SCALAR:
400                return getattr(result.return_type.scalar_value,
401                               result.return_type.scalar_type)
402            return result
403        logging.error("NOTICE - Likely a crash discovery!")
404        logging.error("SysMsg_pb2.SUCCESS is %s", SysMsg_pb2.SUCCESS)
405        raise errors.VtsTcpCommunicationError(
406            "RPC Error, response code for %s is %s" % (arg, resp_code))
407
408    def ExecuteShellCommand(self, command, no_except=False):
409        """RPC to VTS_AGENT_COMMAND_EXECUTE_SHELL_COMMAND.
410
411        Args:
412            command: string or list or tuple, command to execute on device
413            no_except: bool, whether to throw exceptions. If set to True,
414                       when exception happens, return code will be -1 and
415                       str(err) will be in stderr. Result will maintain the
416                       same length as with input command.
417
418        Returns:
419            dictionary of list, command results that contains stdout,
420            stderr, and exit_code.
421        """
422        try:
423            return self.__ExecuteShellCommand(command)
424        except Exception as e:
425            self.error = e
426            if not no_except:
427                raise e
428            logging.exception(e)
429            return {
430                const.STDOUT: [""] * len(command),
431                const.STDERR: [str(e)] * len(command),
432                const.EXIT_CODE: [-1] * len(command)
433            }
434
435    def __ExecuteShellCommand(self, command):
436        """RPC to VTS_AGENT_COMMAND_EXECUTE_SHELL_COMMAND.
437
438        Args:
439            command: string or list of string, command to execute on device
440
441        Returns:
442            dictionary of list, command results that contains stdout,
443            stderr, and exit_code.
444        """
445        self.SendCommand(
446            SysMsg_pb2.VTS_AGENT_COMMAND_EXECUTE_SHELL_COMMAND,
447            shell_command=command)
448        resp = self.RecvResponse(retries=2)
449        logging.debug("resp for VTS_AGENT_COMMAND_EXECUTE_SHELL_COMMAND: %s",
450                      resp)
451
452        stdout = []
453        stderr = []
454        exit_code = -1
455
456        if not resp:
457            logging.error(self.NO_RESPONSE_MSG)
458            stderr = [self.NO_RESPONSE_MSG]
459            self.error = self.NO_RESPONSE_MSG
460        elif resp.response_code != SysMsg_pb2.SUCCESS:
461            logging.error(self.FAIL_RESPONSE_MSG)
462            stderr = [self.FAIL_RESPONSE_MSG]
463            self.error = self.FAIL_RESPONSE_MSG
464        else:
465            stdout = resp.stdout
466            stderr = resp.stderr
467            exit_code = resp.exit_code
468            self.error = None
469
470        return {
471            const.STDOUT: stdout,
472            const.STDERR: stderr,
473            const.EXIT_CODE: exit_code
474        }
475
476    def SendFmqRequest(self, message):
477        """Sends a command to the FMQ driver and receives the response.
478
479        Args:
480            message: FmqRequestMessage, message that contains the arguments
481                     in the FMQ request.
482
483        Returns:
484            FmqResponseMessage, which includes all possible return value types,
485            including int, bool, and data read from the queue.
486        """
487        self.SendCommand(SysMsg_pb2.VTS_FMQ_COMMAND, fmq_request=message)
488        resp = self.RecvResponse()
489        logging.debug("resp for VTS_FMQ_COMMAND: %s", resp)
490        return self.CheckResourceCommandResponse(
491            resp, getattr(resp, "fmq_response", None))
492
493    def SendHidlMemoryRequest(self, message):
494        """Sends a command to the hidl_memory driver and receives the response.
495
496        Args:
497            message: HidlMemoryRequestMessage, message that contains the arguments
498                     in the hidl_memory request.
499
500        Returns:
501            HidlMemoryResponseMessage, return values needed by the host-side caller.
502        """
503        self.SendCommand(
504            SysMsg_pb2.VTS_HIDL_MEMORY_COMMAND, hidl_memory_request=message)
505        resp = self.RecvResponse()
506        logging.debug("resp for VTS_HIDL_MEMORY_COMMAND: %s", resp)
507        return self.CheckResourceCommandResponse(
508            resp, getattr(resp, "hidl_memory_response", None))
509
510    def SendHidlHandleRequest(self, message):
511        """Sends a command to the hidl_handle driver and receives the response.
512
513        Args:
514            message: HidlHandleRequestMessage, message that contains the arguments
515                     in the hidl_handle request.
516
517        Returns:
518            HidlHandleResponseMessage, return values needed by the host-side caller.
519        """
520        self.SendCommand(
521            SysMsg_pb2.VTS_HIDL_HANDLE_COMMAND, hidl_handle_request=message)
522        resp = self.RecvResponse()
523        logging.debug("resp for VTS_HIDL_HANDLE_COMMAND: %s", resp)
524        return self.CheckResourceCommandResponse(
525            resp, getattr(resp, "hidl_handle_response", None))
526
527    @staticmethod
528    def CheckResourceCommandResponse(agent_response, resource_response):
529        """Checks the response from a VTS resource command.
530
531        This function checks and logs framework errors such as not receiving
532        a response, receving a failure response. Then it checks the response
533        for the resource type is not None, and logs error if needed.
534
535        Args:
536            agent_response: AndroidSystemControlResponseMessage,
537                            response from agent.
538            resource_response: FmqResponseMessage, HidlMemoryResponseMessage,
539                               or HidlHandleResponseMessage, the response for
540                               one of fmq, hidl_memory, and hidl_handle.
541
542        Returns:
543            resource_response that is passed in.
544        """
545        if agent_response is None:
546            logging.error(VtsTcpClient.NO_RESPONSE_MSG)
547        elif agent_response.response_code != SysMsg_pb2.SUCCESS:
548            logging.error(VtsTcpClient.FAIL_RESPONSE_MSG)
549        elif resource_response is None:
550            logging.error("TCP client did not receive a response for " +
551                          "the resource command.")
552        return resource_response
553
554    def Ping(self):
555        """RPC to send a PING request.
556
557        Returns:
558            True if the agent is alive, False otherwise.
559        """
560        self.SendCommand(SysMsg_pb2.PING)
561        resp = self.RecvResponse()
562        logging.debug("resp for PING: %s", resp)
563        if resp is not None and resp.response_code == SysMsg_pb2.SUCCESS:
564            return True
565        return False
566
567    def ReadSpecification(self,
568                          interface_name,
569                          target_class,
570                          target_type,
571                          target_version_major,
572                          target_version_minor,
573                          target_package,
574                          recursive=False):
575        """RPC to VTS_AGENT_COMMAND_READ_SPECIFICATION.
576
577        Args:
578            other args: see SendCommand
579            recursive: boolean, set to recursively read the imported
580                       specification(s) and return the merged one.
581        """
582        self.SendCommand(
583            SysMsg_pb2.VTS_AGENT_COMMAND_READ_SPECIFICATION,
584            service_name=interface_name,
585            target_class=target_class,
586            target_type=target_type,
587            target_version_major=target_version_major,
588            target_version_minor=target_version_minor,
589            target_package=target_package)
590        resp = self.RecvResponse(retries=2)
591        logging.debug("resp for VTS_AGENT_COMMAND_EXECUTE_READ_INTERFACE: %s",
592                      resp)
593        logging.debug("proto: %s", resp.result)
594        result = CompSpecMsg_pb2.ComponentSpecificationMessage()
595        if resp.result == "error":
596            raise errors.VtsTcpCommunicationError(
597                "API call error by the VTS driver.")
598        try:
599            text_format.Merge(resp.result, result)
600        except text_format.ParseError as e:
601            logging.exception(e)
602            logging.error("Paring error\n%s", resp.result)
603
604        if recursive and hasattr(result, "import"):
605            for imported_interface in getattr(result, "import"):
606                if imported_interface == "android.hidl.base@1.0::types":
607                    logging.warn("import android.hidl.base@1.0::types skipped")
608                    continue
609                [package, version_str] = imported_interface.split("@")
610                [version_major,
611                 version_minor] = (version_str.split("::")[0]).split(".")
612                imported_result = self.ReadSpecification(
613                    imported_interface.split("::")[1],
614                    # TODO(yim): derive target_class and
615                    # target_type from package path or remove them
616                    msg.component_class
617                    if target_class is None else target_class,
618                    msg.component_type if target_type is None else target_type,
619                    int(version_major),
620                    int(version_minor),
621                    package)
622                # Merge the attributes from imported interface.
623                for attribute in imported_result.attribute:
624                    imported_attribute = result.attribute.add()
625                    imported_attribute.CopyFrom(attribute)
626
627        return result
628
629    def SendCommand(self,
630                    command_type,
631                    paths=None,
632                    file_path=None,
633                    bits=None,
634                    target_class=None,
635                    target_type=None,
636                    target_version_major=None,
637                    target_version_minor=None,
638                    target_package=None,
639                    target_component_name=None,
640                    hw_binder_service_name=None,
641                    is_test_hal=None,
642                    module_name=None,
643                    service_name=None,
644                    callback_port=None,
645                    driver_type=None,
646                    shell_command=None,
647                    caller_uid=None,
648                    arg=None,
649                    fmq_request=None,
650                    hidl_memory_request=None,
651                    hidl_handle_request=None):
652        """Sends a command.
653
654        Args:
655            command_type: integer, the command type.
656            each of the other args are to fill in a field in
657            AndroidSystemControlCommandMessage.
658        """
659        if not self.channel:
660            raise errors.VtsTcpCommunicationError(
661                "channel is None, unable to send command.")
662
663        command_msg = SysMsg_pb2.AndroidSystemControlCommandMessage()
664        command_msg.command_type = command_type
665        logging.debug("sending a command (type %s)",
666                      COMMAND_TYPE_NAME[command_type])
667        if command_type == 202:
668            logging.debug("target API: %s", arg)
669
670        if target_class is not None:
671            command_msg.target_class = target_class
672
673        if target_type is not None:
674            command_msg.target_type = target_type
675
676        if target_version_major is not None:
677            command_msg.target_version_major = target_version_major
678
679        if target_version_minor is not None:
680            command_msg.target_version_minor = target_version_minor
681
682        if target_package is not None:
683            command_msg.target_package = target_package
684
685        if target_component_name is not None:
686            command_msg.target_component_name = target_component_name
687
688        if hw_binder_service_name is not None:
689            command_msg.hw_binder_service_name = hw_binder_service_name
690
691        if is_test_hal is not None:
692            command_msg.is_test_hal = is_test_hal
693
694        if module_name is not None:
695            command_msg.module_name = module_name
696
697        if service_name is not None:
698            command_msg.service_name = service_name
699
700        if driver_type is not None:
701            command_msg.driver_type = driver_type
702
703        if paths is not None:
704            command_msg.paths.extend(paths)
705
706        if file_path is not None:
707            command_msg.file_path = file_path
708
709        if bits is not None:
710            command_msg.bits = bits
711
712        if callback_port is not None:
713            command_msg.callback_port = callback_port
714
715        if caller_uid is not None:
716            command_msg.driver_caller_uid = caller_uid
717
718        if arg is not None:
719            command_msg.arg = arg
720
721        if shell_command is not None:
722            if isinstance(shell_command, (list, tuple)):
723                command_msg.shell_command.extend(shell_command)
724            else:
725                command_msg.shell_command.append(shell_command)
726
727        if fmq_request is not None:
728            command_msg.fmq_request.CopyFrom(fmq_request)
729
730        if hidl_memory_request is not None:
731            command_msg.hidl_memory_request.CopyFrom(hidl_memory_request)
732
733        if hidl_handle_request is not None:
734            command_msg.hidl_handle_request.CopyFrom(hidl_handle_request)
735
736        logging.debug("command %s" % command_msg)
737        message = command_msg.SerializeToString()
738        message_len = len(message)
739        logging.debug("sending %d bytes", message_len)
740        self.channel.write(str(message_len) + b'\n')
741        self.channel.write(message)
742        self.channel.flush()
743
744    def RecvResponse(self, retries=0):
745        """Receives and parses the response, and returns the relevant ResponseMessage.
746
747        Args:
748            retries: an integer indicating the max number of retries in case of
749                     session timeout error.
750        """
751        for index in xrange(1 + retries):
752            try:
753                if index != 0:
754                    logging.info("retrying...")
755                header = self.channel.readline().strip("\n")
756                length = int(header) if header else 0
757                logging.debug("resp %d bytes", length)
758                data = self.channel.read(length)
759                response_msg = SysMsg_pb2.AndroidSystemControlResponseMessage()
760                response_msg.ParseFromString(data)
761                logging.debug(
762                    "Response %s", "success"
763                    if response_msg.response_code == SysMsg_pb2.SUCCESS else
764                    "fail")
765                return response_msg
766            except socket.timeout as e:
767                logging.exception(e)
768        return None
769