1#!/usr/bin/env python 2# 3# Copyright (C) 2016 The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16# 17 18import logging 19import time 20 21from vts.runners.host import asserts 22from vts.runners.host import const 23from vts.runners.host import test_runner 24from vts.testcases.template.hal_hidl_host_test import hal_hidl_host_test 25 26VEHICLE_V2_0_HAL = "android.hardware.automotive.vehicle@2.0::IVehicle" 27 28 29class VtsHalAutomotiveVehicleV2_0HostTest(hal_hidl_host_test.HalHidlHostTest): 30 """A simple testcase for the VEHICLE HIDL HAL. 31 32 Attributes: 33 _arrived: boolean, the flag of onPropertyEvent received. 34 onPropertyEventCalled: integer, the number of onPropertyEvent received. 35 onPropertySetErrorCalled: integer, the number of onPropertySetError 36 received. 37 DEVICE_TMP_DIR: string, target device's tmp directory path. 38 """ 39 40 TEST_HAL_SERVICES = { 41 VEHICLE_V2_0_HAL, 42 } 43 DEVICE_TMP_DIR = "/data/local/tmp" 44 45 def setUpClass(self): 46 """Creates a mirror and init vehicle hal.""" 47 super(VtsHalAutomotiveVehicleV2_0HostTest, self).setUpClass() 48 49 results = self.shell.Execute("id -u system") 50 system_uid = results[const.STDOUT][0].strip() 51 logging.info("system_uid: %s", system_uid) 52 53 self.dut.hal.InitHidlHal( 54 target_type="vehicle", 55 target_basepaths=self.dut.libPaths, 56 target_version=2.0, 57 target_package="android.hardware.automotive.vehicle", 58 target_component_name="IVehicle", 59 hw_binder_service_name=self.getHalServiceName(VEHICLE_V2_0_HAL), 60 bits=int(self.abi_bitness)) 61 62 self.vehicle = self.dut.hal.vehicle # shortcut 63 self.vehicle.SetCallerUid(system_uid) 64 self.vtypes = self.dut.hal.vehicle.GetHidlTypeInterface("types") 65 logging.info("vehicle types: %s", self.vtypes) 66 asserts.assertEqual(0x00ff0000, self.vtypes.VehiclePropertyType.MASK) 67 asserts.assertEqual(0x0f000000, self.vtypes.VehicleArea.MASK) 68 69 self.propToConfig = {} 70 for config in self.vehicle.getAllPropConfigs(): 71 self.propToConfig[config['prop']] = config 72 self.configList = self.propToConfig.values() 73 74 def tearDownClass(self): 75 """Performs clean-up pushed file""" 76 77 cmd_results = self.shell.Execute("rm -rf %s" % self.DEVICE_TMP_DIR) 78 if not cmd_results or any(cmd_results[const.EXIT_CODE]): 79 logging.info("Failed to remove: %s", cmd_results) 80 super(VtsHalAutomotiveVehicleV2_0HostTest, self).tearDownClass() 81 82 def testListProperties(self): 83 """Checks whether some PropConfigs are returned. 84 85 Verifies that call to getAllPropConfigs is not failing and 86 it returns at least 1 vehicle property config. 87 """ 88 logging.info("all supported properties: %s", self.configList) 89 asserts.assertLess(0, len(self.configList)) 90 91 def emptyValueProperty(self, propertyId, areaId=0): 92 """Creates a property structure for use with the Vehicle HAL. 93 94 Args: 95 propertyId: the numeric identifier of the output property. 96 areaId: the numeric identifier of the vehicle area of the output 97 property. 0, or omitted, for global. 98 99 Returns: 100 a property structure for use with the Vehicle HAL. 101 """ 102 return { 103 'prop': propertyId, 104 'timestamp': 0, 105 'areaId': areaId, 106 'status': self.vtypes.VehiclePropertyStatus.AVAILABLE, 107 'value': { 108 'int32Values': [], 109 'floatValues': [], 110 'int64Values': [], 111 'bytes': [], 112 'stringValue': "" 113 } 114 } 115 116 def readVhalProperty(self, propertyId, areaId=0): 117 """Reads a specified property from Vehicle HAL. 118 119 Args: 120 propertyId: the numeric identifier of the property to be read. 121 areaId: the numeric identifier of the vehicle area to retrieve the 122 property for. 0, or omitted, for global. 123 124 Returns: 125 the value of the property as read from Vehicle HAL, or None 126 if it could not read successfully. 127 """ 128 vp = self.vtypes.Py2Pb("VehiclePropValue", 129 self.emptyValueProperty(propertyId, areaId)) 130 logging.info("0x%x get request: %s", propertyId, vp) 131 status, value = self.vehicle.get(vp) 132 logging.info("0x%x get response: %s, %s", propertyId, status, value) 133 if self.vtypes.StatusCode.OK == status: 134 return value 135 else: 136 logging.warning("attempt to read property 0x%x returned error %d", 137 propertyId, status) 138 139 def setVhalProperty(self, propertyId, value, areaId=0, expectedStatus=0): 140 """Sets a specified property in the Vehicle HAL. 141 142 Args: 143 propertyId: the numeric identifier of the property to be set. 144 value: the value of the property, formatted as per the Vehicle HAL 145 (use emptyValueProperty() as a helper). 146 areaId: the numeric identifier of the vehicle area to set the 147 property for. 0, or omitted, for global. 148 expectedStatus: the StatusCode expected to be returned from setting 149 the property. 0, or omitted, for OK. 150 """ 151 propValue = self.emptyValueProperty(propertyId, areaId) 152 for k in propValue["value"]: 153 if k in value: 154 if k == "stringValue": 155 propValue["value"][k] += value[k] 156 else: 157 propValue["value"][k].extend(value[k]) 158 vp = self.vtypes.Py2Pb("VehiclePropValue", propValue) 159 logging.info("0x%x set request: %s", propertyId, vp) 160 status = self.vehicle.set(vp) 161 logging.info("0x%x set response: %s", propertyId, status) 162 if 0 == expectedStatus: 163 expectedStatus = self.vtypes.StatusCode.OK 164 asserts.assertEqual(expectedStatus, status, "Prop 0x%x" % propertyId) 165 166 def setAndVerifyIntProperty(self, propertyId, value, areaId=0): 167 """Sets a integer property in the Vehicle HAL and reads it back. 168 169 Args: 170 propertyId: the numeric identifier of the property to be set. 171 value: the int32 value of the property to be set. 172 areaId: the numeric identifier of the vehicle area to set the 173 property for. 0, or omitted, for global. 174 """ 175 self.setVhalProperty( 176 propertyId, {"int32Values": [value]}, areaId=areaId) 177 178 propValue = self.readVhalProperty(propertyId, areaId=areaId) 179 asserts.assertEqual(1, len(propValue["value"]["int32Values"])) 180 asserts.assertEqual(value, propValue["value"]["int32Values"][0]) 181 182 def extractZonesAsList(self, supportedAreas): 183 """Converts bitwise area flags to list of zones""" 184 allZones = [ 185 self.vtypes.VehicleAreaZone.ROW_1_LEFT, 186 self.vtypes.VehicleAreaZone.ROW_1_CENTER, 187 self.vtypes.VehicleAreaZone.ROW_1_RIGHT, 188 self.vtypes.VehicleAreaZone.ROW_2_LEFT, 189 self.vtypes.VehicleAreaZone.ROW_2_CENTER, 190 self.vtypes.VehicleAreaZone.ROW_2_RIGHT, 191 self.vtypes.VehicleAreaZone.ROW_3_LEFT, 192 self.vtypes.VehicleAreaZone.ROW_3_CENTER, 193 self.vtypes.VehicleAreaZone.ROW_3_RIGHT, 194 self.vtypes.VehicleAreaZone.ROW_4_LEFT, 195 self.vtypes.VehicleAreaZone.ROW_4_CENTER, 196 self.vtypes.VehicleAreaZone.ROW_4_RIGHT, 197 ] 198 199 extractedZones = [] 200 for zone in allZones: 201 if (zone & supportedAreas == zone): 202 extractedZones.append(zone) 203 return extractedZones 204 205 def disableTestHvacPowerOn(self): 206 # Disable this test for now. HVAC Power On will no longer behave like this now that we've 207 # added the status field in VehiclePropValue. Need to update the test for this. 208 """Test power on/off and properties associated with it. 209 210 Gets the list of properties that are affected by the HVAC power state 211 and validates them. 212 213 Turns power on to start in a defined state, verifies that power is on 214 and properties are available. State change from on->off and verifies 215 that properties are no longer available, then state change again from 216 off->on to verify properties are now available again. 217 """ 218 219 # Checks that HVAC_POWER_ON property is supported and returns valid 220 # result initially. 221 hvacPowerOnConfig = self.propToConfig[ 222 self.vtypes.VehicleProperty.HVAC_POWER_ON] 223 if hvacPowerOnConfig is None: 224 logging.info("HVAC_POWER_ON not supported") 225 return 226 227 zones = self.extractZonesAsList(hvacPowerOnConfig['supportedAreas']) 228 asserts.assertLess( 229 0, len(zones), 230 "supportedAreas for HVAC_POWER_ON property is invalid") 231 232 # TODO(pavelm): consider to check for all zones 233 zone = zones[0] 234 235 propValue = self.readVhalProperty( 236 self.vtypes.VehicleProperty.HVAC_POWER_ON, areaId=zone) 237 238 asserts.assertEqual(1, len(propValue["value"]["int32Values"])) 239 asserts.assertTrue( 240 propValue["value"]["int32Values"][0] in [0, 1], 241 "%d not a valid value for HVAC_POWER_ON" % 242 propValue["value"]["int32Values"][0]) 243 244 # Checks that HVAC_POWER_ON config string returns valid result. 245 requestConfig = [ 246 self.vtypes.Py2Pb("VehicleProperty", 247 self.vtypes.VehicleProperty.HVAC_POWER_ON) 248 ] 249 logging.info("HVAC power on config request: %s", requestConfig) 250 responseConfig = self.vehicle.getPropConfigs(requestConfig) 251 logging.info("HVAC power on config response: %s", responseConfig) 252 hvacTypes = set([ 253 self.vtypes.VehicleProperty.HVAC_FAN_SPEED, 254 self.vtypes.VehicleProperty.HVAC_FAN_DIRECTION, 255 self.vtypes.VehicleProperty.HVAC_TEMPERATURE_CURRENT, 256 self.vtypes.VehicleProperty.HVAC_TEMPERATURE_SET, 257 self.vtypes.VehicleProperty.HVAC_DEFROSTER, 258 self.vtypes.VehicleProperty.HVAC_AC_ON, 259 self.vtypes.VehicleProperty.HVAC_MAX_AC_ON, 260 self.vtypes.VehicleProperty.HVAC_MAX_DEFROST_ON, 261 self.vtypes.VehicleProperty.HVAC_RECIRC_ON, 262 self.vtypes.VehicleProperty.HVAC_DUAL_ON, 263 self.vtypes.VehicleProperty.HVAC_AUTO_ON, 264 self.vtypes.VehicleProperty.HVAC_ACTUAL_FAN_SPEED_RPM, 265 ]) 266 status = responseConfig[0] 267 asserts.assertEqual(self.vtypes.StatusCode.OK, status) 268 configString = responseConfig[1][0]["configString"] 269 configProps = [] 270 if configString != "": 271 for prop in configString.split(","): 272 configProps.append(int(prop, 16)) 273 for prop in configProps: 274 asserts.assertTrue(prop in hvacTypes, 275 "0x%X not an HVAC type" % prop) 276 277 # Turn power on. 278 self.setAndVerifyIntProperty( 279 self.vtypes.VehicleProperty.HVAC_POWER_ON, 1, areaId=zone) 280 281 # Check that properties that require power to be on can be set. 282 propVals = {} 283 for prop in configProps: 284 v = self.readVhalProperty(prop, areaId=zone)["value"] 285 self.setVhalProperty(prop, v, areaId=zone) 286 # Save the value for use later when trying to set the property when 287 # HVAC is off. 288 propVals[prop] = v 289 290 # Turn power off. 291 self.setAndVerifyIntProperty( 292 self.vtypes.VehicleProperty.HVAC_POWER_ON, 0, areaId=zone) 293 294 # Check that properties that require power to be on can't be set. 295 for prop in configProps: 296 self.setVhalProperty( 297 prop, 298 propVals[prop], 299 areaId=zone, 300 expectedStatus=self.vtypes.StatusCode.NOT_AVAILABLE) 301 302 # Turn power on. 303 self.setAndVerifyIntProperty( 304 self.vtypes.VehicleProperty.HVAC_POWER_ON, 1, areaId=zone) 305 306 # Check that properties that require power to be on can be set. 307 for prop in configProps: 308 self.setVhalProperty(prop, propVals[prop], areaId=zone) 309 310 def testSetBoolPropResponseTime(self): 311 """Verifies that a PropertyEvent arrives in a reasonable time on Boolean Properties""" 312 313 # PropertyEvent is received 314 self._arrived = False 315 316 def onPropertyEvent(vehiclePropValues): 317 for vp in vehiclePropValues: 318 if vp["prop"] & self.vtypes.VehiclePropertyType.BOOLEAN != 0: 319 logging.info("onPropertyEvent received: %s", 320 vehiclePropValues) 321 self._arrived = True 322 323 def onPropertySetError(errorCode, propId, areaId): 324 logging.info( 325 "onPropertySetError, error: %d, prop: 0x%x, area: 0x%x", 326 errorCode, propId, areaId) 327 self._arrived = True 328 329 for c in self.configList: 330 if (c["access"] != self.vtypes.VehiclePropertyAccess.READ_WRITE or 331 c["changeMode"] != self.vtypes.VehiclePropertyChangeMode.ON_CHANGE or 332 c["prop"] & self.vtypes.VehiclePropertyType.MASK 333 != self.vtypes.VehiclePropertyType.BOOLEAN): 334 continue 335 336 self._arrived = False 337 338 # Register for on_change property 339 prop = c["prop"] 340 callback = self.vehicle.GetHidlCallbackInterface( 341 "IVehicleCallback", 342 onPropertyEvent=onPropertyEvent, 343 onPropertySetError=onPropertySetError 344 ) 345 subscribeOption = { 346 "propId": prop, 347 "sampleRate": 0.0, # ON_CHANGE 348 "flags": self.vtypes.SubscribeFlags.EVENTS_FROM_CAR, 349 } 350 pbSubscribeOption = self.vtypes.Py2Pb("SubscribeOptions", 351 subscribeOption) 352 statusCode = self.vehicle.subscribe(callback, [pbSubscribeOption]) 353 asserts.assertEqual(statusCode, 0, 354 "Must successfully subscribe to property 0x%x" % prop) 355 356 # Change value of properties 357 for area in c["areaConfigs"]: 358 currPropVal = self.readVhalProperty(prop, area["areaId"]) 359 updateVal = [0] 360 if (currPropVal["value"]["int32Values"] is None or 361 currPropVal["value"]["int32Values"] == [0]): 362 updateVal = [1] 363 propValue = self.emptyValueProperty(prop, area["areaId"]) 364 for index in propValue["value"]: 365 if index == "int32Values": 366 propValue["value"][index].extend(updateVal) 367 vp = self.vtypes.Py2Pb("VehiclePropValue", propValue) 368 status = self.vehicle.set(vp) 369 if status != 0: 370 logging.warning("Set value failed for Property 0x%x" % prop) 371 continue 372 373 # Check callback is received in 5 second 374 waitingTime = 5 375 checkTimes = 5 376 for _ in xrange(checkTimes): 377 if self._arrived: 378 logging.info( 379 "callback for Property: 0x%x is received" % 380 prop) 381 break 382 time.sleep(waitingTime/checkTimes) 383 384 if not self._arrived: 385 asserts.fail( 386 "callback is not received in 5 seconds for Property: 0x%x" 387 % prop) 388 self.vehicle.unsubscribe(callback, prop) 389 390 def testVehicleStaticProps(self): 391 """Verifies that static properties are configured correctly""" 392 staticProperties = set([ 393 self.vtypes.VehicleProperty.INFO_VIN, 394 self.vtypes.VehicleProperty.INFO_MAKE, 395 self.vtypes.VehicleProperty.INFO_MODEL, 396 self.vtypes.VehicleProperty.INFO_MODEL_YEAR, 397 self.vtypes.VehicleProperty.INFO_FUEL_CAPACITY, 398 self.vtypes.VehicleProperty.INFO_FUEL_TYPE, 399 self.vtypes.VehicleProperty.INFO_EV_BATTERY_CAPACITY, 400 self.vtypes.VehicleProperty.INFO_EV_CONNECTOR_TYPE, 401 self.vtypes.VehicleProperty.HVAC_FAN_DIRECTION_AVAILABLE, 402 self.vtypes.VehicleProperty.AP_POWER_BOOTUP_REASON, 403 self.vtypes.VehicleProperty.INFO_FUEL_DOOR_LOCATION, 404 self.vtypes.VehicleProperty.INFO_EV_PORT_LOCATION, 405 self.vtypes.VehicleProperty.INFO_DRIVER_SEAT, 406 ]) 407 for c in self.configList: 408 prop = c['prop'] 409 msg = "Prop 0x%x" % prop 410 if (c["prop"] in staticProperties): 411 asserts.assertEqual( 412 self.vtypes.VehiclePropertyChangeMode.STATIC, 413 c["changeMode"], msg) 414 asserts.assertEqual(self.vtypes.VehiclePropertyAccess.READ, 415 c["access"], msg) 416 for area in c["areaConfigs"]: 417 propValue = self.readVhalProperty(prop, area["areaId"]) 418 asserts.assertEqual(prop, propValue["prop"]) 419 self.setVhalProperty( 420 prop, 421 propValue["value"], 422 expectedStatus=self.vtypes.StatusCode.ACCESS_DENIED) 423 else: # Non-static property 424 asserts.assertNotEqual( 425 self.vtypes.VehiclePropertyChangeMode.STATIC, 426 c["changeMode"], msg) 427 428 def testPropertyRanges(self): 429 """Retrieve the property ranges for all areas. 430 431 This checks that the areas noted in the config all give valid area 432 configs. Once these are validated, the values for all these areas 433 retrieved from the HIDL must be within the ranges defined.""" 434 435 enumProperties = { 436 self.vtypes.VehicleProperty.ENGINE_OIL_LEVEL, 437 self.vtypes.VehicleProperty.GEAR_SELECTION, 438 self.vtypes.VehicleProperty.CURRENT_GEAR, 439 self.vtypes.VehicleProperty.TURN_SIGNAL_STATE, 440 self.vtypes.VehicleProperty.IGNITION_STATE, 441 self.vtypes.VehicleProperty.HVAC_FAN_DIRECTION, 442 self.vtypes.VehicleProperty.HVAC_FAN_DIRECTION_AVAILABLE, 443 self.vtypes.VehicleProperty.HAZARD_LIGHTS_STATE, 444 self.vtypes.VehicleProperty.FOG_LIGHTS_STATE, 445 self.vtypes.VehicleProperty.HEADLIGHTS_STATE, 446 self.vtypes.VehicleProperty.HIGH_BEAM_LIGHTS_STATE, 447 self.vtypes.VehicleProperty.HEADLIGHTS_SWITCH, 448 self.vtypes.VehicleProperty.HIGH_BEAM_LIGHTS_SWITCH, 449 self.vtypes.VehicleProperty.FOG_LIGHTS_SWITCH, 450 self.vtypes.VehicleProperty.HAZARD_LIGHTS_SWITCH, 451 self.vtypes.VehicleProperty.INFO_EV_PORT_LOCATION, 452 self.vtypes.VehicleProperty.INFO_FUEL_DOOR_LOCATION, 453 self.vtypes.VehicleProperty.INFO_DRIVER_SEAT, 454 } 455 456 for c in self.configList: 457 # Continuous properties need to have a sampling frequency. 458 if c["changeMode"] == self.vtypes.VehiclePropertyChangeMode.CONTINUOUS: 459 asserts.assertTrue( 460 c["minSampleRate"] >= 0.0 , 461 "minSampleRate should be >= 0. Config list: %s" % c) 462 asserts.assertLess( 463 0.0, c["maxSampleRate"], 464 "maxSampleRate should be > 0. Config list: %s" % c) 465 asserts.assertFalse( 466 c["minSampleRate"] > c["maxSampleRate"], 467 "Prop 0x%x minSampleRate > maxSampleRate" % c["prop"]) 468 469 if c["prop"] & self.vtypes.VehiclePropertyType.BOOLEAN != 0: 470 # Boolean types don't have ranges 471 continue 472 473 if (c["access"] != self.vtypes.VehiclePropertyAccess.READ_WRITE and 474 c["access"] != self.vtypes.VehiclePropertyAccess.READ): 475 # Skip the test if properties are not readable. 476 continue 477 478 if c["prop"] in enumProperties: 479 # This property does not use traditional min/max ranges 480 continue 481 482 asserts.assertTrue(c["areaConfigs"] != None, 483 "Prop 0x%x must have areaConfigs" % c["prop"]) 484 areasFound = 0 485 if c["prop"] == self.vtypes.VehicleProperty.HVAC_TEMPERATURE_DISPLAY_UNITS: 486 # This property doesn't have sensible min/max 487 continue 488 489 for a in c["areaConfigs"]: 490 # Make sure this doesn't override one of the other areas found. 491 asserts.assertEqual(0, areasFound & a["areaId"]) 492 areasFound |= a["areaId"] 493 494 # Do some basic checking the min and max aren't mixed up. 495 checks = [("minInt32Value", "maxInt32Value"), 496 ("minInt64Value", "maxInt64Value"), 497 ("minFloatValue", "maxFloatValue")] 498 for minName, maxName in checks: 499 asserts.assertFalse( 500 a[minName] > a[maxName], 501 "Prop 0x%x Area 0x%X %s > %s: %d > %d" % 502 (c["prop"], a["areaId"], minName, maxName, a[minName], 503 a[maxName])) 504 505 # Get a value and make sure it's within the bounds. 506 propVal = self.readVhalProperty(c["prop"], a["areaId"]) 507 # Some values may not be available, which is not an error. 508 if propVal is None: 509 continue 510 val = propVal["value"] 511 valTypes = { 512 "int32Values": ("minInt32Value", "maxInt32Value"), 513 "int64Values": ("minInt64Value", "maxInt64Value"), 514 "floatValues": ("minFloatValue", "maxFloatValue"), 515 } 516 for valType, valBoundNames in valTypes.items(): 517 for v in val[valType]: 518 # Make sure value isn't less than the minimum. 519 asserts.assertFalse( 520 v < a[valBoundNames[0]], 521 "Prop 0x%x Area 0x%X %s < min: %s < %s" % 522 (c["prop"], a["areaId"], valType, v, 523 a[valBoundNames[0]])) 524 # Make sure value isn't greater than the maximum. 525 asserts.assertFalse( 526 v > a[valBoundNames[1]], 527 "Prop 0x%x Area 0x%X %s > max: %s > %s" % 528 (c["prop"], a["areaId"], valType, v, 529 a[valBoundNames[1]])) 530 531 def getValueIfPropSupported(self, propertyId): 532 """Returns tuple of boolean (indicating value supported or not) and the value itself""" 533 if (propertyId in self.propToConfig): 534 propValue = self.readVhalProperty(propertyId) 535 asserts.assertNotEqual(None, propValue, 536 "expected value, prop: 0x%x" % propertyId) 537 asserts.assertEqual(propertyId, propValue['prop']) 538 return True, self.extractValue(propValue) 539 else: 540 return False, None 541 542 def testInfoVinMakeModel(self): 543 """Verifies INFO_VIN, INFO_MAKE, INFO_MODEL properties""" 544 stringProperties = set([ 545 self.vtypes.VehicleProperty.INFO_VIN, 546 self.vtypes.VehicleProperty.INFO_MAKE, 547 self.vtypes.VehicleProperty.INFO_MODEL 548 ]) 549 for prop in stringProperties: 550 supported, val = self.getValueIfPropSupported(prop) 551 if supported: 552 asserts.assertEqual(str, type(val), "prop: 0x%x" % prop) 553 asserts.assertTrue(0 <= (len(val)), "prop: 0x%x" % prop) 554 555 def testGlobalFloatProperties(self): 556 """Verifies that values of global float properties are in the correct range""" 557 floatProperties = { 558 self.vtypes.VehicleProperty.ENV_OUTSIDE_TEMPERATURE: (-50, 100), # celsius 559 self.vtypes.VehicleProperty.ENGINE_RPM : (0, 30000), # RPMs 560 self.vtypes.VehicleProperty.ENGINE_OIL_TEMP : (-50, 150), # celsius 561 self.vtypes.VehicleProperty.ENGINE_COOLANT_TEMP : (-50, 150), # 562 self.vtypes.VehicleProperty.PERF_VEHICLE_SPEED : (0, 150), # m/s, 150 m/s = 330 mph 563 self.vtypes.VehicleProperty.PERF_VEHICLE_SPEED_DISPLAY : (0, 150), # 150 m/s = 330 mph 564 self.vtypes.VehicleProperty.PERF_STEERING_ANGLE : (-180, 180), # degrees 565 self.vtypes.VehicleProperty.PERF_ODOMETER : (0, 1000000), # km 566 self.vtypes.VehicleProperty.INFO_FUEL_CAPACITY : (0, 1000000), # milliliter 567 } 568 569 for prop, validRange in floatProperties.iteritems(): 570 supported, val = self.getValueIfPropSupported(prop) 571 if supported: 572 asserts.assertEqual(float, type(val)) 573 self.assertValueInRangeForProp(val, validRange[0], 574 validRange[1], prop) 575 576 def testGlobalBoolProperties(self): 577 """Verifies that values of global boolean properties are in the correct range""" 578 booleanProperties = set([ 579 self.vtypes.VehicleProperty.PARKING_BRAKE_ON, 580 self.vtypes.VehicleProperty.FUEL_LEVEL_LOW, 581 self.vtypes.VehicleProperty.NIGHT_MODE, 582 self.vtypes.VehicleProperty.ABS_ACTIVE, 583 self.vtypes.VehicleProperty.FUEL_DOOR_OPEN, 584 self.vtypes.VehicleProperty.EV_CHARGE_PORT_OPEN, 585 self.vtypes.VehicleProperty.EV_CHARGE_PORT_CONNECTED, 586 ]) 587 for prop in booleanProperties: 588 self.verifyEnumPropIfSupported(prop, [0, 1]) 589 590 def testGlobalEnumProperties(self): 591 """Verifies that values of global enum properties are in the correct range""" 592 enumProperties = { 593 self.vtypes.VehicleProperty.ENGINE_OIL_LEVEL: 594 self.vtypes.VehicleOilLevel, 595 self.vtypes.VehicleProperty.GEAR_SELECTION: 596 self.vtypes.VehicleGear, 597 self.vtypes.VehicleProperty.CURRENT_GEAR: 598 self.vtypes.VehicleGear, 599 self.vtypes.VehicleProperty.TURN_SIGNAL_STATE: 600 self.vtypes.VehicleTurnSignal, 601 self.vtypes.VehicleProperty.IGNITION_STATE: 602 self.vtypes.VehicleIgnitionState, 603 } 604 for prop, enum in enumProperties.iteritems(): 605 self.verifyEnumPropIfSupported(prop, vars(enum).values()) 606 607 def testDebugDump(self): 608 """Verifies that call to IVehicle#debugDump is not failing""" 609 dumpStr = self.vehicle.debugDump() 610 asserts.assertNotEqual(None, dumpStr) 611 612 def extractValue(self, propValue): 613 """Extracts value depending on data type of the property""" 614 if propValue == None: 615 return None 616 617 # Extract data type 618 dataType = propValue['prop'] & self.vtypes.VehiclePropertyType.MASK 619 val = propValue['value'] 620 if self.vtypes.VehiclePropertyType.STRING == dataType: 621 asserts.assertNotEqual(None, val['stringValue']) 622 return val['stringValue'] 623 elif self.vtypes.VehiclePropertyType.INT32 == dataType or \ 624 self.vtypes.VehiclePropertyType.BOOLEAN == dataType: 625 asserts.assertEqual(1, len(val["int32Values"])) 626 return val["int32Values"][0] 627 elif self.vtypes.VehiclePropertyType.INT64 == dataType: 628 asserts.assertEqual(1, len(val["int64Values"])) 629 return val["int64Values"][0] 630 elif self.vtypes.VehiclePropertyType.FLOAT == dataType: 631 asserts.assertEqual(1, len(val["floatValues"])) 632 return val["floatValues"][0] 633 elif self.vtypes.VehiclePropertyType.INT32_VEC == dataType: 634 asserts.assertLess(0, len(val["int32Values"])) 635 return val["int32Values"] 636 elif self.vtypes.VehiclePropertyType.FLOAT_VEC == dataType: 637 asserts.assertLess(0, len(val["floatValues"])) 638 return val["floatValues"] 639 elif self.vtypes.VehiclePropertyType.BYTES == dataType: 640 asserts.assertLess(0, len(val["bytes"])) 641 return val["bytes"] 642 else: 643 return val 644 645 def verifyEnumPropIfSupported(self, propertyId, validValues): 646 """Verifies that if given property supported it is one of the value in validValues set""" 647 supported, val = self.getValueIfPropSupported(propertyId) 648 if supported: 649 asserts.assertEqual(int, type(val)) 650 self.assertIntValueInRangeForProp(val, validValues, propertyId) 651 652 def assertLessOrEqual(self, first, second, msg=None): 653 """Asserts that first <= second""" 654 if second < first: 655 fullMsg = "%s is not less or equal to %s" % (first, second) 656 if msg: 657 fullMsg = "%s %s" % (fullMsg, msg) 658 fail(fullMsg) 659 660 def assertIntValueInRangeForProp(self, value, validValues, prop): 661 """Asserts that given value is in the validValues range""" 662 asserts.assertTrue( 663 value in validValues, 664 "Invalid value %d for property: 0x%x, expected one of: %s" % 665 (value, prop, validValues)) 666 667 def assertValueInRangeForProp(self, value, rangeBegin, rangeEnd, prop): 668 """Asserts that given value is in the range [rangeBegin, rangeEnd]""" 669 msg = "Value %s is out of range [%s, %s] for property 0x%x" % ( 670 value, rangeBegin, rangeEnd, prop) 671 self.assertLessOrEqual(rangeBegin, value, msg) 672 self.assertLessOrEqual(value, rangeEnd, msg) 673 674 def getPropConfig(self, propertyId): 675 return self.propToConfig.get(propertyId) 676 677 678 def isPropSupported(self, propertyId): 679 return self.getPropConfig(propertyId) is not None 680 681 def testEngineOilTemp(self): 682 """tests engine oil temperature. 683 684 This also tests an HIDL async callback. 685 """ 686 self.onPropertyEventCalled = 0 687 self.onPropertySetErrorCalled = 0 688 689 def onPropertyEvent(vehiclePropValues): 690 logging.info("onPropertyEvent received: %s", vehiclePropValues) 691 self.onPropertyEventCalled += 1 692 693 def onPropertySetError(erroCode, propId, areaId): 694 logging.info( 695 "onPropertySetError, error: %d, prop: 0x%x, area: 0x%x", 696 erroCode, prop, area) 697 self.onPropertySetErrorCalled += 1 698 699 config = self.getPropConfig( 700 self.vtypes.VehicleProperty.ENGINE_OIL_TEMP) 701 if (config is None): 702 logging.info("ENGINE_OIL_TEMP property is not supported") 703 return # Property not supported, we are done here. 704 705 propValue = self.readVhalProperty( 706 self.vtypes.VehicleProperty.ENGINE_OIL_TEMP) 707 asserts.assertEqual(1, len(propValue['value']['floatValues'])) 708 oilTemp = propValue['value']['floatValues'][0] 709 logging.info("Current oil temperature: %f C", oilTemp) 710 asserts.assertLess(oilTemp, 200) # Check it is in reasinable range 711 asserts.assertLess(-50, oilTemp) 712 713 if (config["changeMode"] == 714 self.vtypes.VehiclePropertyChangeMode.CONTINUOUS): 715 logging.info( 716 "ENGINE_OIL_TEMP is continuous property, subscribing...") 717 callback = self.vehicle.GetHidlCallbackInterface( 718 "IVehicleCallback", 719 onPropertyEvent=onPropertyEvent, 720 onPropertySetError=onPropertySetError) 721 722 subscribeOptions = { 723 "propId": self.vtypes.VehicleProperty.ENGINE_OIL_TEMP, 724 "sampleRate": 1.0, # Hz 725 "flags": self.vtypes.SubscribeFlags.EVENTS_FROM_CAR, 726 } 727 pbSubscribeOptions = self.vtypes.Py2Pb("SubscribeOptions", 728 subscribeOptions) 729 730 statusCode = self.vehicle.subscribe(callback, [pbSubscribeOptions]) 731 if statusCode != 0: 732 asserts.fail("Can not register ENGINE_OIL_TEMP") 733 734 for _ in range(5): 735 if (self.onPropertyEventCalled > 0 736 or self.onPropertySetErrorCalled > 0): 737 self.vehicle.unsubscribe( 738 callback, self.vtypes.VehicleProperty.ENGINE_OIL_TEMP) 739 return 740 time.sleep(1) 741 asserts.fail("Callback not called in 5 seconds.") 742 743 def getDiagnosticSupportInfo(self): 744 """Check which of the OBD2 diagnostic properties are supported.""" 745 properties = [ 746 self.vtypes.VehicleProperty.OBD2_LIVE_FRAME, 747 self.vtypes.VehicleProperty.OBD2_FREEZE_FRAME, 748 self.vtypes.VehicleProperty.OBD2_FREEZE_FRAME_INFO, 749 self.vtypes.VehicleProperty.OBD2_FREEZE_FRAME_CLEAR 750 ] 751 return {x: self.isPropSupported(x) for x in properties} 752 753 class CheckRead(object): 754 """An object whose job it is to read a Vehicle HAL property and run 755 routine validation checks on the result.""" 756 757 def __init__(self, test, propertyId, areaId=0): 758 """Creates a CheckRead instance. 759 760 Args: 761 test: the containing testcase object. 762 propertyId: the numeric identifier of the vehicle property. 763 """ 764 self.test = test 765 self.propertyId = propertyId 766 self.areaId = 0 767 768 def validateGet(self, status, value): 769 """Validate the result of IVehicle.get. 770 771 Args: 772 status: the StatusCode returned from Vehicle HAL. 773 value: the VehiclePropValue returned from Vehicle HAL. 774 775 Returns: a VehiclePropValue instance, or None on failure.""" 776 asserts.assertEqual(self.test.vtypes.StatusCode.OK, status) 777 asserts.assertNotEqual(value, None) 778 asserts.assertEqual(self.propertyId, value['prop']) 779 return value 780 781 def prepareRequest(self, propValue): 782 """Setup this request with any property-specific data. 783 784 Args: 785 propValue: a dictionary in the format of a VehiclePropValue. 786 787 Returns: a dictionary in the format of a VehclePropValue.""" 788 return propValue 789 790 def __call__(self): 791 asserts.assertTrue( 792 self.test.isPropSupported(self.propertyId), "error") 793 request = { 794 'prop': self.propertyId, 795 'timestamp': 0, 796 'areaId': self.areaId, 797 'status': self.test.vtypes.VehiclePropertyStatus.AVAILABLE, 798 'value': { 799 'int32Values': [], 800 'floatValues': [], 801 'int64Values': [], 802 'bytes': [], 803 'stringValue': "" 804 } 805 } 806 request = self.prepareRequest(request) 807 requestPropValue = self.test.vtypes.Py2Pb("VehiclePropValue", 808 request) 809 status, responsePropValue = self.test.vehicle.get(requestPropValue) 810 return self.validateGet(status, responsePropValue) 811 812 class CheckWrite(object): 813 """An object whose job it is to write a Vehicle HAL property and run 814 routine validation checks on the result.""" 815 816 def __init__(self, test, propertyId, areaId=0): 817 """Creates a CheckWrite instance. 818 819 Args: 820 test: the containing testcase object. 821 propertyId: the numeric identifier of the vehicle property. 822 areaId: the numeric identifier of the vehicle area. 823 """ 824 self.test = test 825 self.propertyId = propertyId 826 self.areaId = 0 827 828 def validateSet(self, status): 829 """Validate the result of IVehicle.set. 830 Reading back the written-to property to ensure a consistent 831 value is fair game for this method. 832 833 Args: 834 status: the StatusCode returned from Vehicle HAL. 835 836 Returns: None.""" 837 asserts.assertEqual(self.test.vtypes.StatusCode.OK, status) 838 839 def prepareRequest(self, propValue): 840 """Setup this request with any property-specific data. 841 842 Args: 843 propValue: a dictionary in the format of a VehiclePropValue. 844 845 Returns: a dictionary in the format of a VehclePropValue.""" 846 return propValue 847 848 def __call__(self): 849 asserts.assertTrue( 850 self.test.isPropSupported(self.propertyId), "error") 851 request = { 852 'prop': self.propertyId, 853 'timestamp': 0, 854 'areaId': self.areaId, 855 'status': self.test.vtypes.VehiclePropertyStatus.AVAILABLE, 856 'value': { 857 'int32Values': [], 858 'floatValues': [], 859 'int64Values': [], 860 'bytes': [], 861 'stringValue': "" 862 } 863 } 864 request = self.prepareRequest(request) 865 requestPropValue = self.test.vtypes.Py2Pb("VehiclePropValue", 866 request) 867 status = self.test.vehicle.set(requestPropValue) 868 return self.validateSet(status) 869 870 def testReadObd2LiveFrame(self): 871 """Test that one can correctly read the OBD2 live frame.""" 872 supportInfo = self.getDiagnosticSupportInfo() 873 if supportInfo[self.vtypes.VehicleProperty.OBD2_LIVE_FRAME]: 874 checkRead = self.CheckRead( 875 self, self.vtypes.VehicleProperty.OBD2_LIVE_FRAME) 876 checkRead() 877 else: 878 # live frame not supported by this HAL implementation. done 879 logging.info("OBD2_LIVE_FRAME not supported.") 880 881 def testReadObd2FreezeFrameInfo(self): 882 """Test that one can read the list of OBD2 freeze timestamps.""" 883 supportInfo = self.getDiagnosticSupportInfo() 884 if supportInfo[self.vtypes.VehicleProperty.OBD2_FREEZE_FRAME_INFO]: 885 checkRead = self.CheckRead( 886 self, self.vtypes.VehicleProperty.OBD2_FREEZE_FRAME_INFO) 887 checkRead() 888 else: 889 # freeze frame info not supported by this HAL implementation. done 890 logging.info("OBD2_FREEZE_FRAME_INFO not supported.") 891 892 def testReadValidObd2FreezeFrame(self): 893 """Test that one can read the OBD2 freeze frame data.""" 894 895 class FreezeFrameCheckRead(self.CheckRead): 896 def __init__(self, test, timestamp): 897 self.test = test 898 self.propertyId = \ 899 self.test.vtypes.VehicleProperty.OBD2_FREEZE_FRAME 900 self.timestamp = timestamp 901 self.areaId = 0 902 903 def prepareRequest(self, propValue): 904 propValue['value']['int64Values'] = [self.timestamp] 905 return propValue 906 907 def validateGet(self, status, value): 908 # None is acceptable, as a newer fault could have overwritten 909 # the one we're trying to read 910 if value is not None: 911 asserts.assertEqual(self.test.vtypes.StatusCode.OK, status) 912 asserts.assertEqual(self.propertyId, value['prop']) 913 asserts.assertEqual(self.timestamp, value['timestamp']) 914 915 supportInfo = self.getDiagnosticSupportInfo() 916 if supportInfo[self.vtypes.VehicleProperty.OBD2_FREEZE_FRAME_INFO] \ 917 and supportInfo[self.vtypes.VehicleProperty.OBD2_FREEZE_FRAME]: 918 infoCheckRead = self.CheckRead( 919 self, self.vtypes.VehicleProperty.OBD2_FREEZE_FRAME_INFO) 920 frameInfos = infoCheckRead() 921 timestamps = frameInfos["value"]["int64Values"] 922 for timestamp in timestamps: 923 freezeCheckRead = FreezeFrameCheckRead(self, timestamp) 924 freezeCheckRead() 925 else: 926 # freeze frame not supported by this HAL implementation. done 927 logging.info("OBD2_FREEZE_FRAME and _INFO not supported.") 928 929 def testReadInvalidObd2FreezeFrame(self): 930 """Test that trying to read freeze frame at invalid timestamps 931 behaves correctly (i.e. returns an error code).""" 932 933 class FreezeFrameCheckRead(self.CheckRead): 934 def __init__(self, test, timestamp): 935 self.test = test 936 self.propertyId = self.test.vtypes.VehicleProperty.OBD2_FREEZE_FRAME 937 self.timestamp = timestamp 938 self.areaId = 0 939 940 def prepareRequest(self, propValue): 941 propValue['value']['int64Values'] = [self.timestamp] 942 return propValue 943 944 def validateGet(self, status, value): 945 asserts.assertEqual(self.test.vtypes.StatusCode.INVALID_ARG, 946 status) 947 948 supportInfo = self.getDiagnosticSupportInfo() 949 invalidTimestamps = [0, 482005800] 950 if supportInfo[self.vtypes.VehicleProperty.OBD2_FREEZE_FRAME]: 951 for timestamp in invalidTimestamps: 952 freezeCheckRead = FreezeFrameCheckRead(self, timestamp) 953 freezeCheckRead() 954 else: 955 # freeze frame not supported by this HAL implementation. done 956 logging.info("OBD2_FREEZE_FRAME not supported.") 957 958 def testClearValidObd2FreezeFrame(self): 959 """Test that deleting a diagnostic freeze frame works. 960 Given the timing behavor of OBD2_FREEZE_FRAME, the only sensible 961 definition of works here is that, after deleting a frame, trying to read 962 at its timestamp, will not be successful.""" 963 964 class FreezeFrameClearCheckWrite(self.CheckWrite): 965 def __init__(self, test, timestamp): 966 self.test = test 967 self.propertyId = self.test.vtypes.VehicleProperty.OBD2_FREEZE_FRAME_CLEAR 968 self.timestamp = timestamp 969 self.areaId = 0 970 971 def prepareRequest(self, propValue): 972 propValue['value']['int64Values'] = [self.timestamp] 973 return propValue 974 975 def validateSet(self, status): 976 asserts.assertTrue( 977 status in [ 978 self.test.vtypes.StatusCode.OK, 979 self.test.vtypes.StatusCode.INVALID_ARG 980 ], "error") 981 982 class FreezeFrameCheckRead(self.CheckRead): 983 def __init__(self, test, timestamp): 984 self.test = test 985 self.propertyId = \ 986 self.test.vtypes.VehicleProperty.OBD2_FREEZE_FRAME 987 self.timestamp = timestamp 988 self.areaId = 0 989 990 def prepareRequest(self, propValue): 991 propValue['value']['int64Values'] = [self.timestamp] 992 return propValue 993 994 def validateGet(self, status, value): 995 asserts.assertEqual(self.test.vtypes.StatusCode.INVALID_ARG, 996 status) 997 998 supportInfo = self.getDiagnosticSupportInfo() 999 if supportInfo[self.vtypes.VehicleProperty.OBD2_FREEZE_FRAME_INFO] \ 1000 and supportInfo[self.vtypes.VehicleProperty.OBD2_FREEZE_FRAME] \ 1001 and supportInfo[self.vtypes.VehicleProperty.OBD2_FREEZE_FRAME_CLEAR]: 1002 infoCheckRead = self.CheckRead( 1003 self, self.vtypes.VehicleProperty.OBD2_FREEZE_FRAME_INFO) 1004 frameInfos = infoCheckRead() 1005 timestamps = frameInfos["value"]["int64Values"] 1006 for timestamp in timestamps: 1007 checkWrite = FreezeFrameClearCheckWrite(self, timestamp) 1008 checkWrite() 1009 checkRead = FreezeFrameCheckRead(self, timestamp) 1010 checkRead() 1011 else: 1012 # freeze frame not supported by this HAL implementation. done 1013 logging.info("OBD2_FREEZE_FRAME, _CLEAR and _INFO not supported.") 1014 1015 def testClearInvalidObd2FreezeFrame(self): 1016 """Test that deleting an invalid freeze frame behaves correctly.""" 1017 1018 class FreezeFrameClearCheckWrite(self.CheckWrite): 1019 def __init__(self, test, timestamp): 1020 self.test = test 1021 self.propertyId = \ 1022 self.test.vtypes.VehicleProperty.OBD2_FREEZE_FRAME_CLEAR 1023 self.timestamp = timestamp 1024 self.areaId = 0 1025 1026 def prepareRequest(self, propValue): 1027 propValue['value']['int64Values'] = [self.timestamp] 1028 return propValue 1029 1030 def validateSet(self, status): 1031 asserts.assertEqual( 1032 self.test.vtypes.StatusCode.INVALID_ARG, status, 1033 "PropId: 0x%s, Timestamp: %d" % (self.propertyId, 1034 self.timestamp)) 1035 1036 supportInfo = self.getDiagnosticSupportInfo() 1037 if supportInfo[self.vtypes.VehicleProperty.OBD2_FREEZE_FRAME_CLEAR]: 1038 invalidTimestamps = [0, 482005800] 1039 for timestamp in invalidTimestamps: 1040 checkWrite = FreezeFrameClearCheckWrite(self, timestamp) 1041 checkWrite() 1042 else: 1043 # freeze frame not supported by this HAL implementation. done 1044 logging.info("OBD2_FREEZE_FRAME_CLEAR not supported.") 1045 1046 1047if __name__ == "__main__": 1048 test_runner.main() 1049