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