Home | History | Annotate | Download | only in host
      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