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