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