blob: ded7c3fd04201e8ea187e701188c7eb113f3eaf4 [file] [log] [blame]
#
# 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 copy
import logging
import random
import sys
from google.protobuf import text_format
from vts.proto import AndroidSystemControlMessage_pb2 as ASysCtrlMsg
from vts.proto import ComponentSpecificationMessage_pb2 as CompSpecMsg
from vts.utils.python.fuzzer import FuzzerUtils
from vts.utils.python.mirror import mirror_object
from vts.utils.python.mirror import py2pb
from vts.utils.python.mirror import resource_mirror
_DEFAULT_TARGET_BASE_PATHS = ["/system/lib64/hw"]
_DEFAULT_HWBINDER_SERVICE = "default"
INTERFACE = "interface"
API = "api"
class MirrorObjectError(Exception):
"""Raised when there is a general error in manipulating a mirror object."""
pass
class VtsEnum(object):
"""Enum's host-side mirror instance."""
def __init__(self, attribute):
logging.debug(attribute)
for enumerator, scalar_value in zip(attribute.enum_value.enumerator,
attribute.enum_value.scalar_value):
setattr(self, enumerator,
getattr(scalar_value, attribute.enum_value.scalar_type))
class NativeEntityMirror(mirror_object.MirrorObject):
"""The class that acts as the mirror to an Android device's HAL layer.
This is the base class used to communicate to a particular HAL or Lib in
the VTS agent on the target side.
Attributes:
_client: the TCP client instance.
_caller_uid: string, the caller's UID if not None.
_if_spec_msg: the interface specification message of a host object to
mirror.
_last_raw_code_coverage_data: NativeCodeCoverageRawDataMessage,
last seen raw code coverage data.
"""
def __init__(self,
client,
driver_id=None,
if_spec_message=None,
caller_uid=None):
super(NativeEntityMirror, self).__init__(client, caller_uid)
self._driver_id = driver_id
self._if_spec_msg = if_spec_message
self._last_raw_code_coverage_data = None
def GetApi(self, api_name):
"""Gets the ProtoBuf message for given api.
Args:
api_name: string, the name of the target function API.
Returns:
FunctionSpecificationMessage if found, None otherwise
"""
logging.debug("GetAPI %s for %s", api_name, self._if_spec_msg)
# handle reserved methods first.
if api_name == "notifySyspropsChanged":
func_msg = CompSpecMsg.FunctionSpecificationMessage()
func_msg.name = api_name
return func_msg
if isinstance(self._if_spec_msg,
CompSpecMsg.ComponentSpecificationMessage):
if len(self._if_spec_msg.interface.api) > 0:
for api in self._if_spec_msg.interface.api:
if api.name == api_name:
return copy.copy(api)
else:
logging.error("unknown spec type %s", type(self._if_spec_msg))
sys.exit(1)
return None
def GetAttribute(self, attribute_name):
"""Gets the ProtoBuf message for given attribute.
Args:
attribute_name: string, the name of a target attribute
(optionally excluding the namespace).
Returns:
VariableSpecificationMessage if found, None otherwise
"""
if self._if_spec_msg.attribute:
for attribute in self._if_spec_msg.attribute:
if (not attribute.is_const and
(attribute.name == attribute_name
or attribute.name.endswith("::" + attribute_name))):
return attribute
if (self._if_spec_msg.interface
and self._if_spec_msg.interface.attribute):
for attribute in self._if_spec_msg.interface.attribute:
if (not attribute.is_const and
(attribute.name == attribute_name
or attribute.name.endswith("::" + attribute_name))):
return attribute
return None
def GetConstType(self, type_name):
"""Returns the ProtoBuf message for given const type.
Args:
type_name: string, the name of the target const data variable.
Returns:
VariableSpecificationMessage if found, None otherwise
"""
try:
if self._if_spec_msg.attribute:
for attribute in self._if_spec_msg.attribute:
if attribute.is_const and attribute.name == type_name:
return attribute
elif (attribute.type == CompSpecMsg.TYPE_ENUM
and attribute.name.endswith(type_name)):
return attribute
if self._if_spec_msg.interface and self._if_spec_msg.interface.attribute:
for attribute in self._if_spec_msg.interface.attribute:
if attribute.is_const and attribute.name == type_name:
return attribute
elif (attribute.type == CompSpecMsg.TYPE_ENUM
and attribute.name.endswith(type_name)):
return attribute
return None
except AttributeError as e:
# TODO: check in advance whether self._if_spec_msg Interface
# SpecificationMessage.
return None
def Py2Pb(self, attribute_name, py_values):
"""Returns the ProtoBuf of a give Python values.
Args:
attribute_name: string, the name of a target attribute.
py_values: Python values.
Returns:
Converted VariableSpecificationMessage if found, None otherwise
"""
attribute_spec = self.GetAttribute(attribute_name)
if attribute_spec:
converted_attr = py2pb.Convert(attribute_spec, py_values)
if converted_attr is None:
raise MirrorObjectError(
"Failed to convert attribute %s", attribute_spec)
return converted_attr
logging.error("Can not find attribute: %s", attribute_name)
return None
# TODO: Guard against calls to this function after self.CleanUp is called.
def __getattr__(self, api_name, *args, **kwargs):
"""Calls a target component's API.
Args:
api_name: string, the name of an API function to call.
*args: a list of arguments
**kwargs: a dict for the arg name and value pairs
"""
def RemoteCall(*args, **kwargs):
"""Dynamically calls a remote API and returns the result value."""
func_msg = self.GetApi(api_name)
if not func_msg:
raise MirrorObjectError("api %s unknown", func_msg)
logging.debug("remote call %s%s", api_name, args)
if args:
for arg_msg, value_msg in zip(func_msg.arg, args):
logging.debug("arg msg %s", arg_msg)
logging.debug("value %s", value_msg)
if value_msg is not None:
converted_msg = py2pb.Convert(arg_msg, value_msg)
if converted_msg is None:
raise MirrorObjectError("Failed to convert arg %s", value_msg)
logging.debug("converted_message: %s", converted_msg)
arg_msg.CopyFrom(converted_msg)
else:
# TODO: use kwargs
for arg in func_msg.arg:
# TODO: handle other
if (arg.type == CompSpecMsg.TYPE_SCALAR
and arg.scalar_type == "pointer"):
arg.scalar_value.pointer = 0
logging.debug(func_msg)
call_msg = CompSpecMsg.FunctionCallMessage()
if self._if_spec_msg.component_class:
call_msg.component_class = self._if_spec_msg.component_class
call_msg.hal_driver_id = self._driver_id
call_msg.api.CopyFrom(func_msg)
logging.debug("final msg %s", call_msg)
results = self._client.CallApi(
text_format.MessageToString(call_msg), self._caller_uid)
if (isinstance(results, tuple) and len(results) == 2
and isinstance(results[1], dict)
and "coverage" in results[1]):
self._last_raw_code_coverage_data = results[1]["coverage"]
results = results[0]
if isinstance(results, list): # Non-HIDL HAL does not return list.
# Translate TYPE_HIDL_INTERFACE to halMirror.
for i, _ in enumerate(results):
result = results[i]
if (not result or not isinstance(
result, CompSpecMsg.VariableSpecificationMessage)):
# no need to process the return values.
continue
if result.type == CompSpecMsg.TYPE_HIDL_INTERFACE:
if result.hidl_interface_id <= -1:
results[i] = None
driver_id = result.hidl_interface_id
nested_interface_name = \
result.predefined_type.split("::")[-1]
logging.debug("Nested interface name: %s",
nested_interface_name)
nested_interface = self.GetHalMirrorForInterface(
nested_interface_name, driver_id)
results[i] = nested_interface
elif (result.type == CompSpecMsg.TYPE_FMQ_SYNC
or result.type == CompSpecMsg.TYPE_FMQ_UNSYNC):
if (result.fmq_value[0].fmq_id == -1):
logging.error("Invalid new queue_id.")
results[i] = None
else:
# Retrieve type of data in this FMQ.
data_type = None
# For scalar, read scalar_type field.
if result.fmq_value[0].type == \
CompSpecMsg.TYPE_SCALAR:
data_type = result.fmq_value[0].scalar_type
# For enum, struct, and union, read predefined_type
# field.
elif (result.fmq_value[0].type ==
CompSpecMsg.TYPE_ENUM or
result.fmq_value[0].type ==
CompSpecMsg.TYPE_STRUCT or
result.fmq_value[0].type ==
CompSpecMsg.TYPE_UNION):
data_type = result.fmq_value[0].predefined_type
# Encounter an unknown type in FMQ.
if data_type == None:
logging.error(
"Unknown type %d in the new FMQ.",
result.fmq_value[0].type)
results[i] = None
continue
sync = result.type == CompSpecMsg.TYPE_FMQ_SYNC
fmq_mirror = resource_mirror.ResourceFmqMirror(
data_type, sync, self._client,
result.fmq_value[0].fmq_id)
results[i] = fmq_mirror
elif result.type == CompSpecMsg.TYPE_HIDL_MEMORY:
if result.hidl_memory_value.mem_id == -1:
logging.error("Invalid new mem_id.")
results[i] = None
else:
mem_mirror = resource_mirror.ResourceHidlMemoryMirror(
self._client, result.hidl_memory_value.mem_id)
results[i] = mem_mirror
elif result.type == CompSpecMsg.TYPE_HANDLE:
if result.handle_value.handle_id == -1:
logging.error("Invalid new handle_id.")
results[i] = None
else:
handle_mirror = resource_mirror.ResourceHidlHandleMirror(
self._client, result.handle_value.handle_id)
results[i] = handle_mirror
if len(results) == 1:
# single return result, return the value directly.
return results[0]
return results
def MessageGenerator(*args, **kwargs):
"""Dynamically generates a custom message instance."""
if args:
return self.Py2Pb(api_name, args)
else:
#TODO: handle kwargs
return None
#TODO: deprecate this or move it to a separate class.
def MessageFuzzer(arg_msg):
"""Fuzz a custom message instance."""
if not self.GetAttribute(api_name):
raise MirrorObjectError("fuzz arg %s unknown" % arg_msg)
if arg_msg.type == CompSpecMsg.TYPE_STRUCT:
index = random.randint(0, len(arg_msg.struct_value))
count = 0
for struct_value in arg_msg.struct_value:
if count == index:
if struct_value.scalar_type == "uint32_t":
struct_value.scalar_value.uint32_t ^= FuzzerUtils.mask_uint32_t(
)
elif struct_value.scalar_type == "int32_t":
mask = FuzzerUtils.mask_int32_t()
if mask == (1 << 31):
struct_value.scalar_value.int32_t *= -1
struct_value.scalar_value.int32_t += 1
else:
struct_value.scalar_value.int32_t ^= mask
else:
raise MirrorObjectError(
"support %s" % struct_value.scalar_type)
break
count += 1
logging.debug("fuzzed %s", arg_msg)
else:
raise MirrorObjectError(
"unsupported fuzz message type %s." % arg_msg.type)
return arg_msg
def ConstGenerator():
"""Dynamically generates a const variable's value."""
arg_msg = self.GetConstType(api_name)
if not arg_msg:
raise MirrorObjectError("const %s unknown" % arg_msg)
logging.debug("check %s", api_name)
if arg_msg.type == CompSpecMsg.TYPE_SCALAR:
ret_v = getattr(arg_msg.scalar_value, arg_msg.scalar_type,
None)
if ret_v is None:
raise MirrorObjectError("No value found for type %s in %s."
% (arg_msg.scalar_type, api_name))
return ret_v
elif arg_msg.type == CompSpecMsg.TYPE_STRING:
return arg_msg.string_value.message
elif arg_msg.type == CompSpecMsg.TYPE_ENUM:
return VtsEnum(arg_msg)
raise MirrorObjectError("const %s not found" % api_name)
# handle APIs.
func_msg = self.GetApi(api_name)
if func_msg:
logging.debug("api %s", func_msg)
return RemoteCall
# handle attributes.
arg_msg = self.GetConstType(api_name)
if arg_msg:
logging.debug("const %s *\n%s", api_name, arg_msg)
return ConstGenerator()
fuzz = False
if api_name.endswith("_fuzz"):
fuzz = True
api_name = api_name[:-5]
arg_msg = self.GetAttribute(api_name)
if arg_msg:
logging.debug("arg %s", arg_msg)
if not fuzz:
return MessageGenerator
else:
return MessageFuzzer
raise MirrorObjectError("unknown api name %s" % api_name)
def GetRawCodeCoverage(self):
"""Returns any measured raw code coverage data."""
return self._last_raw_code_coverage_data
def __str__(self):
"""Prints all the attributes and methods."""
result = ""
if self._if_spec_msg:
if self._if_spec_msg.attribute:
for attribute in self._if_spec_msg.attribute:
result += "attribute %s\n" % attribute.name
if self._if_spec_msg.interface:
for attribute in self._if_spec_msg.interface.attribute:
result += "interface attribute %s\n" % attribute.name
for api in self._if_spec_msg.interface.api:
result += "api %s\n" % api.name
return result