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