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 copy
18import logging
19import random
20import sys
21
22from google.protobuf import text_format
23
24from vts.proto import AndroidSystemControlMessage_pb2 as ASysCtrlMsg
25from vts.proto import ComponentSpecificationMessage_pb2 as CompSpecMsg
26from vts.utils.python.fuzzer import FuzzerUtils
27from vts.utils.python.mirror import mirror_object
28from vts.utils.python.mirror import py2pb
29from vts.utils.python.mirror import resource_mirror
30
31_DEFAULT_TARGET_BASE_PATHS = ["/system/lib64/hw"]
32_DEFAULT_HWBINDER_SERVICE = "default"
33
34INTERFACE = "interface"
35API = "api"
36
37
38class MirrorObjectError(Exception):
39    """Raised when there is a general error in manipulating a mirror object."""
40    pass
41
42
43class VtsEnum(object):
44    """Enum's host-side mirror instance."""
45
46    def __init__(self, attribute):
47        logging.debug(attribute)
48        for enumerator, scalar_value in zip(attribute.enum_value.enumerator,
49                                            attribute.enum_value.scalar_value):
50            setattr(self, enumerator,
51                    getattr(scalar_value, attribute.enum_value.scalar_type))
52
53
54class NativeEntityMirror(mirror_object.MirrorObject):
55    """The class that acts as the mirror to an Android device's HAL layer.
56
57    This is the base class used to communicate to a particular HAL or Lib in
58    the VTS agent on the target side.
59
60    Attributes:
61        _client: the TCP client instance.
62        _caller_uid: string, the caller's UID if not None.
63        _if_spec_msg: the interface specification message of a host object to
64                      mirror.
65        _last_raw_code_coverage_data: NativeCodeCoverageRawDataMessage,
66                                      last seen raw code coverage data.
67    """
68
69    def __init__(self,
70                 client,
71                 driver_id=None,
72                 if_spec_message=None,
73                 caller_uid=None):
74        super(NativeEntityMirror, self).__init__(client, caller_uid)
75        self._driver_id = driver_id
76        self._if_spec_msg = if_spec_message
77        self._last_raw_code_coverage_data = None
78
79    def GetApi(self, api_name):
80        """Gets the ProtoBuf message for given api.
81
82        Args:
83            api_name: string, the name of the target function API.
84
85        Returns:
86            FunctionSpecificationMessage if found, None otherwise
87        """
88        logging.debug("GetAPI %s for %s", api_name, self._if_spec_msg)
89        # handle reserved methods first.
90        if api_name == "notifySyspropsChanged":
91            func_msg = CompSpecMsg.FunctionSpecificationMessage()
92            func_msg.name = api_name
93            return func_msg
94        if isinstance(self._if_spec_msg,
95                      CompSpecMsg.ComponentSpecificationMessage):
96            if len(self._if_spec_msg.interface.api) > 0:
97                for api in self._if_spec_msg.interface.api:
98                    if api.name == api_name:
99                        return copy.copy(api)
100        else:
101            logging.error("unknown spec type %s", type(self._if_spec_msg))
102            sys.exit(1)
103        return None
104
105    def GetAttribute(self, attribute_name):
106        """Gets the ProtoBuf message for given attribute.
107
108        Args:
109            attribute_name: string, the name of a target attribute
110                            (optionally excluding the namespace).
111
112        Returns:
113            VariableSpecificationMessage if found, None otherwise
114        """
115        if self._if_spec_msg.attribute:
116            for attribute in self._if_spec_msg.attribute:
117                if (not attribute.is_const and
118                    (attribute.name == attribute_name
119                     or attribute.name.endswith("::" + attribute_name))):
120                    return attribute
121        if (self._if_spec_msg.interface
122                and self._if_spec_msg.interface.attribute):
123            for attribute in self._if_spec_msg.interface.attribute:
124                if (not attribute.is_const and
125                    (attribute.name == attribute_name
126                     or attribute.name.endswith("::" + attribute_name))):
127                    return attribute
128        return None
129
130    def GetConstType(self, type_name):
131        """Returns the ProtoBuf message for given const type.
132
133        Args:
134            type_name: string, the name of the target const data variable.
135
136        Returns:
137            VariableSpecificationMessage if found, None otherwise
138        """
139        try:
140            if self._if_spec_msg.attribute:
141                for attribute in self._if_spec_msg.attribute:
142                    if attribute.is_const and attribute.name == type_name:
143                        return attribute
144                    elif (attribute.type == CompSpecMsg.TYPE_ENUM
145                          and attribute.name.endswith(type_name)):
146                        return attribute
147            if self._if_spec_msg.interface and self._if_spec_msg.interface.attribute:
148                for attribute in self._if_spec_msg.interface.attribute:
149                    if attribute.is_const and attribute.name == type_name:
150                        return attribute
151                    elif (attribute.type == CompSpecMsg.TYPE_ENUM
152                          and attribute.name.endswith(type_name)):
153                        return attribute
154            return None
155        except AttributeError as e:
156            # TODO: check in advance whether self._if_spec_msg Interface
157            # SpecificationMessage.
158            return None
159
160    def Py2Pb(self, attribute_name, py_values):
161        """Returns the ProtoBuf of a give Python values.
162
163        Args:
164            attribute_name: string, the name of a target attribute.
165            py_values: Python values.
166
167        Returns:
168            Converted VariableSpecificationMessage if found, None otherwise
169        """
170        attribute_spec = self.GetAttribute(attribute_name)
171        if attribute_spec:
172            converted_attr = py2pb.Convert(attribute_spec, py_values)
173            if converted_attr is None:
174              raise MirrorObjectError(
175                  "Failed to convert attribute %s", attribute_spec)
176            return converted_attr
177        logging.error("Can not find attribute: %s", attribute_name)
178        return None
179
180    # TODO: Guard against calls to this function after self.CleanUp is called.
181    def __getattr__(self, api_name, *args, **kwargs):
182        """Calls a target component's API.
183
184        Args:
185            api_name: string, the name of an API function to call.
186            *args: a list of arguments
187            **kwargs: a dict for the arg name and value pairs
188        """
189
190        def RemoteCall(*args, **kwargs):
191            """Dynamically calls a remote API and returns the result value."""
192            func_msg = self.GetApi(api_name)
193            if not func_msg:
194                raise MirrorObjectError("api %s unknown", func_msg)
195
196            logging.debug("remote call %s%s", api_name, args)
197            if args:
198                for arg_msg, value_msg in zip(func_msg.arg, args):
199                    logging.debug("arg msg %s", arg_msg)
200                    logging.debug("value %s", value_msg)
201                    if value_msg is not None:
202                        converted_msg = py2pb.Convert(arg_msg, value_msg)
203                        if converted_msg is None:
204                          raise MirrorObjectError("Failed to convert arg %s", value_msg)
205                        logging.debug("converted_message: %s", converted_msg)
206                        arg_msg.CopyFrom(converted_msg)
207            else:
208                # TODO: use kwargs
209                for arg in func_msg.arg:
210                    # TODO: handle other
211                    if (arg.type == CompSpecMsg.TYPE_SCALAR
212                            and arg.scalar_type == "pointer"):
213                        arg.scalar_value.pointer = 0
214                logging.debug(func_msg)
215
216            call_msg = CompSpecMsg.FunctionCallMessage()
217            if self._if_spec_msg.component_class:
218                call_msg.component_class = self._if_spec_msg.component_class
219            call_msg.hal_driver_id = self._driver_id
220            call_msg.api.CopyFrom(func_msg)
221            logging.debug("final msg %s", call_msg)
222            results = self._client.CallApi(
223                text_format.MessageToString(call_msg), self._caller_uid)
224            if (isinstance(results, tuple) and len(results) == 2
225                    and isinstance(results[1], dict)
226                    and "coverage" in results[1]):
227                self._last_raw_code_coverage_data = results[1]["coverage"]
228                results = results[0]
229
230            if isinstance(results, list):  # Non-HIDL HAL does not return list.
231                # Translate TYPE_HIDL_INTERFACE to halMirror.
232                for i, _ in enumerate(results):
233                    result = results[i]
234                    if (not result or not isinstance(
235                            result, CompSpecMsg.VariableSpecificationMessage)):
236                        # no need to process the return values.
237                        continue
238
239                    if result.type == CompSpecMsg.TYPE_HIDL_INTERFACE:
240                        if result.hidl_interface_id <= -1:
241                            results[i] = None
242                        driver_id = result.hidl_interface_id
243                        nested_interface_name = \
244                            result.predefined_type.split("::")[-1]
245                        logging.debug("Nested interface name: %s",
246                                      nested_interface_name)
247                        nested_interface = self.GetHalMirrorForInterface(
248                            nested_interface_name, driver_id)
249                        results[i] = nested_interface
250                    elif (result.type == CompSpecMsg.TYPE_FMQ_SYNC
251                          or result.type == CompSpecMsg.TYPE_FMQ_UNSYNC):
252                        if (result.fmq_value[0].fmq_id == -1):
253                            logging.error("Invalid new queue_id.")
254                            results[i] = None
255                        else:
256                            # Retrieve type of data in this FMQ.
257                            data_type = None
258                            # For scalar, read scalar_type field.
259                            if result.fmq_value[0].type == \
260                                    CompSpecMsg.TYPE_SCALAR:
261                                data_type = result.fmq_value[0].scalar_type
262                            # For enum, struct, and union, read predefined_type
263                            # field.
264                            elif (result.fmq_value[0].type ==
265                                     CompSpecMsg.TYPE_ENUM or
266                                  result.fmq_value[0].type ==
267                                     CompSpecMsg.TYPE_STRUCT or
268                                  result.fmq_value[0].type ==
269                                     CompSpecMsg.TYPE_UNION):
270                                data_type = result.fmq_value[0].predefined_type
271
272                            # Encounter an unknown type in FMQ.
273                            if data_type == None:
274                                logging.error(
275                                    "Unknown type %d in the new FMQ.",
276                                    result.fmq_value[0].type)
277                                results[i] = None
278                                continue
279                            sync = result.type == CompSpecMsg.TYPE_FMQ_SYNC
280                            fmq_mirror = resource_mirror.ResourceFmqMirror(
281                                data_type, sync, self._client,
282                                result.fmq_value[0].fmq_id)
283                            results[i] = fmq_mirror
284                    elif result.type == CompSpecMsg.TYPE_HIDL_MEMORY:
285                        if result.hidl_memory_value.mem_id == -1:
286                            logging.error("Invalid new mem_id.")
287                            results[i] = None
288                        else:
289                            mem_mirror = resource_mirror.ResourceHidlMemoryMirror(
290                                self._client, result.hidl_memory_value.mem_id)
291                            results[i] = mem_mirror
292                    elif result.type == CompSpecMsg.TYPE_HANDLE:
293                        if result.handle_value.handle_id == -1:
294                            logging.error("Invalid new handle_id.")
295                            results[i] = None
296                        else:
297                            handle_mirror = resource_mirror.ResourceHidlHandleMirror(
298                                self._client, result.handle_value.handle_id)
299                            results[i] = handle_mirror
300                if len(results) == 1:
301                    # single return result, return the value directly.
302                    return results[0]
303            return results
304
305        def MessageGenerator(*args, **kwargs):
306            """Dynamically generates a custom message instance."""
307            if args:
308                return self.Py2Pb(api_name, args)
309            else:
310                #TODO: handle kwargs
311                return None
312
313        #TODO: deprecate this or move it to a separate class.
314        def MessageFuzzer(arg_msg):
315            """Fuzz a custom message instance."""
316            if not self.GetAttribute(api_name):
317                raise MirrorObjectError("fuzz arg %s unknown" % arg_msg)
318
319            if arg_msg.type == CompSpecMsg.TYPE_STRUCT:
320                index = random.randint(0, len(arg_msg.struct_value))
321                count = 0
322                for struct_value in arg_msg.struct_value:
323                    if count == index:
324                        if struct_value.scalar_type == "uint32_t":
325                            struct_value.scalar_value.uint32_t ^= FuzzerUtils.mask_uint32_t(
326                            )
327                        elif struct_value.scalar_type == "int32_t":
328                            mask = FuzzerUtils.mask_int32_t()
329                            if mask == (1 << 31):
330                                struct_value.scalar_value.int32_t *= -1
331                                struct_value.scalar_value.int32_t += 1
332                            else:
333                                struct_value.scalar_value.int32_t ^= mask
334                        else:
335                            raise MirrorObjectError(
336                                "support %s" % struct_value.scalar_type)
337                        break
338                    count += 1
339                logging.debug("fuzzed %s", arg_msg)
340            else:
341                raise MirrorObjectError(
342                    "unsupported fuzz message type %s." % arg_msg.type)
343            return arg_msg
344
345        def ConstGenerator():
346            """Dynamically generates a const variable's value."""
347            arg_msg = self.GetConstType(api_name)
348            if not arg_msg:
349                raise MirrorObjectError("const %s unknown" % arg_msg)
350            logging.debug("check %s", api_name)
351            if arg_msg.type == CompSpecMsg.TYPE_SCALAR:
352                ret_v = getattr(arg_msg.scalar_value, arg_msg.scalar_type,
353                                None)
354                if ret_v is None:
355                    raise MirrorObjectError("No value found for type %s in %s."
356                                            % (arg_msg.scalar_type, api_name))
357                return ret_v
358            elif arg_msg.type == CompSpecMsg.TYPE_STRING:
359                return arg_msg.string_value.message
360            elif arg_msg.type == CompSpecMsg.TYPE_ENUM:
361                return VtsEnum(arg_msg)
362            raise MirrorObjectError("const %s not found" % api_name)
363
364        # handle APIs.
365        func_msg = self.GetApi(api_name)
366        if func_msg:
367            logging.debug("api %s", func_msg)
368            return RemoteCall
369
370        # handle attributes.
371        arg_msg = self.GetConstType(api_name)
372        if arg_msg:
373            logging.debug("const %s *\n%s", api_name, arg_msg)
374            return ConstGenerator()
375
376        fuzz = False
377        if api_name.endswith("_fuzz"):
378            fuzz = True
379            api_name = api_name[:-5]
380        arg_msg = self.GetAttribute(api_name)
381        if arg_msg:
382            logging.debug("arg %s", arg_msg)
383            if not fuzz:
384                return MessageGenerator
385            else:
386                return MessageFuzzer
387
388        raise MirrorObjectError("unknown api name %s" % api_name)
389
390    def GetRawCodeCoverage(self):
391        """Returns any measured raw code coverage data."""
392        return self._last_raw_code_coverage_data
393
394    def __str__(self):
395        """Prints all the attributes and methods."""
396        result = ""
397        if self._if_spec_msg:
398            if self._if_spec_msg.attribute:
399                for attribute in self._if_spec_msg.attribute:
400                    result += "attribute %s\n" % attribute.name
401            if self._if_spec_msg.interface:
402                for attribute in self._if_spec_msg.interface.attribute:
403                    result += "interface attribute %s\n" % attribute.name
404                for api in self._if_spec_msg.interface.api:
405                    result += "api %s\n" % api.name
406        return result
407