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 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 testObd2SensorProperties(self):
    203         """Test reading the live and freeze OBD2 frame properties.
    204 
    205         OBD2 (On-Board Diagnostics 2) is the industry standard protocol
    206         for retrieving diagnostic sensor information from vehicles.
    207         """
    208         class CheckRead(object):
    209             """This class wraps the logic of an actual property read.
    210 
    211             Attributes:
    212                 testobject: the test case this object is used on behalf of.
    213                 propertyId: the identifier of the Vehiche HAL property to read.
    214                 name: the engineer-readable name of this test operation.
    215             """
    216 
    217             def __init__(self, testobject, propertyId, name):
    218                 self.testobject = testobject
    219                 self.propertyId = propertyId
    220                 self.name = name
    221 
    222             def onReadSuccess(self, propValue):
    223                 """Override this to perform any post-read validation.
    224 
    225                 Args:
    226                     propValue: the property value obtained from Vehicle HAL.
    227                 """
    228                 pass
    229 
    230             def __call__(self):
    231                 """Reads the specified property and validates the result."""
    232                 propValue = self.testobject.readVhalProperty(self.propertyId)
    233                 asserts.assertNotEqual(propValue, None,
    234                                        msg="reading %s should not return None" %
    235                                        self.name)
    236                 logging.info("%s = %s", self.name, propValue)
    237                 self.onReadSuccess(propValue)
    238                 logging.info("%s pass" % self.name)
    239 
    240         def checkLiveFrameRead():
    241             """Validates reading the OBD2_LIVE_FRAME (if available)."""
    242             checker = CheckRead(self,
    243                                 self.vtypes.VehicleProperty.OBD2_LIVE_FRAME,
    244                                 "OBD2_LIVE_FRAME")
    245             checker()
    246 
    247         def checkFreezeFrameRead():
    248             """Validates reading the OBD2_FREEZE_FRAME (if available)."""
    249             checker = CheckRead(self,
    250                                 self.vtypes.VehicleProperty.OBD2_FREEZE_FRAME,
    251                                 "OBD2_FREEZE_FRAME")
    252             checker()
    253 
    254         isLiveSupported = self.vtypes.VehicleProperty.OBD2_LIVE_FRAME in self.propToConfig
    255         isFreezeSupported = self.vtypes.VehicleProperty.OBD2_FREEZE_FRAME in self.propToConfig
    256         logging.info("isLiveSupported = %s, isFreezeSupported = %s",
    257                      isLiveSupported, isFreezeSupported)
    258         if isLiveSupported:
    259             checkLiveFrameRead()
    260         if isFreezeSupported:
    261             checkFreezeFrameRead()
    262 
    263     def testDrivingStatus(self):
    264         """Checks that DRIVING_STATUS property returns correct result."""
    265         propValue = self.readVhalProperty(
    266             self.vtypes.VehicleProperty.DRIVING_STATUS)
    267         asserts.assertEqual(1, len(propValue["value"]["int32Values"]))
    268         drivingStatus = propValue["value"]["int32Values"][0]
    269 
    270         allStatuses = (self.vtypes.VehicleDrivingStatus.UNRESTRICTED
    271                        | self.vtypes.VehicleDrivingStatus.NO_VIDEO
    272                        | self.vtypes.VehicleDrivingStatus.NO_KEYBOARD_INPUT
    273                        | self.vtypes.VehicleDrivingStatus.NO_VOICE_INPUT
    274                        | self.vtypes.VehicleDrivingStatus.NO_CONFIG
    275                        | self.vtypes.VehicleDrivingStatus.LIMIT_MESSAGE_LEN)
    276 
    277         asserts.assertEqual(allStatuses, allStatuses | drivingStatus)
    278 
    279     def extractZonesAsList(self, supportedAreas):
    280         """Converts bitwise area flags to list of zones"""
    281         allZones = [
    282             self.vtypes.VehicleAreaZone.ROW_1_LEFT,
    283             self.vtypes.VehicleAreaZone.ROW_1_CENTER,
    284             self.vtypes.VehicleAreaZone.ROW_1_RIGHT,
    285             self.vtypes.VehicleAreaZone.ROW_1,
    286             self.vtypes.VehicleAreaZone.ROW_2_LEFT,
    287             self.vtypes.VehicleAreaZone.ROW_2_CENTER,
    288             self.vtypes.VehicleAreaZone.ROW_2_RIGHT,
    289             self.vtypes.VehicleAreaZone.ROW_2,
    290             self.vtypes.VehicleAreaZone.ROW_3_LEFT,
    291             self.vtypes.VehicleAreaZone.ROW_3_CENTER,
    292             self.vtypes.VehicleAreaZone.ROW_3_RIGHT,
    293             self.vtypes.VehicleAreaZone.ROW_3,
    294             self.vtypes.VehicleAreaZone.ROW_4_LEFT,
    295             self.vtypes.VehicleAreaZone.ROW_4_CENTER,
    296             self.vtypes.VehicleAreaZone.ROW_4_RIGHT,
    297             self.vtypes.VehicleAreaZone.ROW_4,
    298             self.vtypes.VehicleAreaZone.WHOLE_CABIN,
    299         ]
    300 
    301         extractedZones = []
    302         for zone in allZones:
    303             if (zone & supportedAreas == zone):
    304                 extractedZones.append(zone)
    305         return extractedZones
    306 
    307 
    308     def testHvacPowerOn(self):
    309         """Test power on/off and properties associated with it.
    310 
    311         Gets the list of properties that are affected by the HVAC power state
    312         and validates them.
    313 
    314         Turns power on to start in a defined state, verifies that power is on
    315         and properties are available.  State change from on->off and verifies
    316         that properties are no longer available, then state change again from
    317         off->on to verify properties are now available again.
    318         """
    319 
    320         # Checks that HVAC_POWER_ON property is supported and returns valid
    321         # result initially.
    322         hvacPowerOnConfig = self.propToConfig[self.vtypes.VehicleProperty.HVAC_POWER_ON]
    323         if hvacPowerOnConfig is None:
    324             logging.info("HVAC_POWER_ON not supported")
    325             return
    326 
    327         zones = self.extractZonesAsList(hvacPowerOnConfig['supportedAreas'])
    328         asserts.assertLess(0, len(zones), "supportedAreas for HVAC_POWER_ON property is invalid")
    329 
    330         # TODO(pavelm): consider to check for all zones
    331         zone = zones[0]
    332 
    333         propValue = self.readVhalProperty(
    334             self.vtypes.VehicleProperty.HVAC_POWER_ON, areaId=zone)
    335 
    336         asserts.assertEqual(1, len(propValue["value"]["int32Values"]))
    337         asserts.assertTrue(
    338             propValue["value"]["int32Values"][0] in [0, 1],
    339             "%d not a valid value for HVAC_POWER_ON" %
    340                 propValue["value"]["int32Values"][0]
    341             )
    342 
    343         # Checks that HVAC_POWER_ON config string returns valid result.
    344         requestConfig = [self.vtypes.Py2Pb(
    345             "VehicleProperty", self.vtypes.VehicleProperty.HVAC_POWER_ON)]
    346         logging.info("HVAC power on config request: %s", requestConfig)
    347         responseConfig = self.vehicle.getPropConfigs(requestConfig)
    348         logging.info("HVAC power on config response: %s", responseConfig)
    349         hvacTypes = set([
    350             self.vtypes.VehicleProperty.HVAC_FAN_SPEED,
    351             self.vtypes.VehicleProperty.HVAC_FAN_DIRECTION,
    352             self.vtypes.VehicleProperty.HVAC_TEMPERATURE_CURRENT,
    353             self.vtypes.VehicleProperty.HVAC_TEMPERATURE_SET,
    354             self.vtypes.VehicleProperty.HVAC_DEFROSTER,
    355             self.vtypes.VehicleProperty.HVAC_AC_ON,
    356             self.vtypes.VehicleProperty.HVAC_MAX_AC_ON,
    357             self.vtypes.VehicleProperty.HVAC_MAX_DEFROST_ON,
    358             self.vtypes.VehicleProperty.HVAC_RECIRC_ON,
    359             self.vtypes.VehicleProperty.HVAC_DUAL_ON,
    360             self.vtypes.VehicleProperty.HVAC_AUTO_ON,
    361             self.vtypes.VehicleProperty.HVAC_ACTUAL_FAN_SPEED_RPM,
    362         ])
    363         status = responseConfig[0]
    364         asserts.assertEqual(self.vtypes.StatusCode.OK, status)
    365         configString = responseConfig[1][0]["configString"]
    366         configProps = []
    367         if configString != "":
    368             for prop in configString.split(","):
    369                 configProps.append(int(prop, 16))
    370         for prop in configProps:
    371             asserts.assertTrue(prop in hvacTypes,
    372                                "0x%X not an HVAC type" % prop)
    373 
    374         # Turn power on.
    375         self.setAndVerifyIntProperty(
    376             self.vtypes.VehicleProperty.HVAC_POWER_ON, 1, areaId=zone)
    377 
    378         # Check that properties that require power to be on can be set.
    379         propVals = {}
    380         for prop in configProps:
    381             v = self.readVhalProperty(prop, areaId=zone)["value"]
    382             self.setVhalProperty(prop, v, areaId=zone)
    383             # Save the value for use later when trying to set the property when
    384             # HVAC is off.
    385             propVals[prop] = v
    386 
    387         # Turn power off.
    388         self.setAndVerifyIntProperty(
    389             self.vtypes.VehicleProperty.HVAC_POWER_ON, 0, areaId=zone)
    390 
    391         # Check that properties that require power to be on can't be set.
    392         for prop in configProps:
    393             self.setVhalProperty(
    394                 prop, propVals[prop],
    395                 areaId=zone,
    396                 expectedStatus=self.vtypes.StatusCode.NOT_AVAILABLE)
    397 
    398         # Turn power on.
    399         self.setAndVerifyIntProperty(
    400             self.vtypes.VehicleProperty.HVAC_POWER_ON, 1, areaId=zone)
    401 
    402         # Check that properties that require power to be on can be set.
    403         for prop in configProps:
    404             self.setVhalProperty(prop, propVals[prop], areaId=zone)
    405 
    406     def testVehicleStaticProps(self):
    407         """Verifies that static properties are configured correctly"""
    408         staticProperties = set([
    409             self.vtypes.VehicleProperty.INFO_VIN,
    410             self.vtypes.VehicleProperty.INFO_MAKE,
    411             self.vtypes.VehicleProperty.INFO_MODEL,
    412             self.vtypes.VehicleProperty.INFO_MODEL_YEAR,
    413             self.vtypes.VehicleProperty.INFO_FUEL_CAPACITY,
    414             self.vtypes.VehicleProperty.HVAC_FAN_DIRECTION_AVAILABLE,
    415             self.vtypes.VehicleProperty.AUDIO_HW_VARIANT,
    416             self.vtypes.VehicleProperty.AP_POWER_BOOTUP_REASON,
    417         ])
    418         for c in self.configList:
    419             prop = c['prop']
    420             msg = "Prop 0x%x" % prop
    421             if (c["prop"] in staticProperties):
    422                 asserts.assertEqual(self.vtypes.VehiclePropertyChangeMode.STATIC, c["changeMode"], msg)
    423                 asserts.assertEqual(self.vtypes.VehiclePropertyAccess.READ, c["access"], msg)
    424                 propValue = self.readVhalProperty(prop)
    425                 asserts.assertEqual(prop, propValue['prop'])
    426                 self.setVhalProperty(prop, propValue["value"],
    427                     expectedStatus=self.vtypes.StatusCode.ACCESS_DENIED)
    428             else:  # Non-static property
    429                 asserts.assertNotEqual(self.vtypes.VehiclePropertyChangeMode.STATIC, c["changeMode"], msg)
    430 
    431     def testPropertyRanges(self):
    432         """Retrieve the property ranges for all areas.
    433 
    434         This checks that the areas noted in the config all give valid area
    435         configs.  Once these are validated, the values for all these areas
    436         retrieved from the HIDL must be within the ranges defined."""
    437         for c in self.configList:
    438             # Continuous properties need to have a sampling frequency.
    439             if c["changeMode"] & self.vtypes.VehiclePropertyChangeMode.CONTINUOUS != 0:
    440                 asserts.assertLess(0.0, c["minSampleRate"],
    441                                    "minSampleRate should be > 0. Config list: %s" % c)
    442                 asserts.assertLess(0.0, c["maxSampleRate"],
    443                                    "maxSampleRate should be > 0. Config list: %s" % c)
    444                 asserts.assertFalse(c["minSampleRate"] > c["maxSampleRate"],
    445                                     "Prop 0x%x minSampleRate > maxSampleRate" %
    446                                         c["prop"])
    447 
    448             areasFound = 0
    449             for a in c["areaConfigs"]:
    450                 # Make sure this doesn't override one of the other areas found.
    451                 asserts.assertEqual(0, areasFound & a["areaId"])
    452                 areasFound |= a["areaId"]
    453 
    454                 # Do some basic checking the min and max aren't mixed up.
    455                 checks = [
    456                     ("minInt32Value", "maxInt32Value"),
    457                     ("minInt64Value", "maxInt64Value"),
    458                     ("minFloatValue", "maxFloatValue")
    459                 ]
    460                 for minName, maxName in checks:
    461                     asserts.assertFalse(
    462                         a[minName] > a[maxName],
    463                         "Prop 0x%x Area 0x%X %s > %s: %d > %d" %
    464                             (c["prop"], a["areaId"],
    465                              minName, maxName, a[minName], a[maxName]))
    466 
    467                 # Get a value and make sure it's within the bounds.
    468                 propVal = self.readVhalProperty(c["prop"], a["areaId"])
    469                 # Some values may not be available, which is not an error.
    470                 if propVal is None:
    471                     continue
    472                 val = propVal["value"]
    473                 valTypes = {
    474                     "int32Values": ("minInt32Value", "maxInt32Value"),
    475                     "int64Values": ("minInt64Value", "maxInt64Value"),
    476                     "floatValues": ("minFloatValue", "maxFloatValue"),
    477                 }
    478                 for valType, valBoundNames in valTypes.items():
    479                     for v in val[valType]:
    480                         # Make sure value isn't less than the minimum.
    481                         asserts.assertFalse(
    482                             v < a[valBoundNames[0]],
    483                             "Prop 0x%x Area 0x%X %s < min: %s < %s" %
    484                                 (c["prop"], a["areaId"],
    485                                  valType, v, a[valBoundNames[0]]))
    486                         # Make sure value isn't greater than the maximum.
    487                         asserts.assertFalse(
    488                             v > a[valBoundNames[1]],
    489                             "Prop 0x%x Area 0x%X %s > max: %s > %s" %
    490                                 (c["prop"], a["areaId"],
    491                                  valType, v, a[valBoundNames[1]]))
    492 
    493     def getValueIfPropSupported(self, propertyId):
    494         """Returns tuple of boolean (indicating value supported or not) and the value itself"""
    495         if (propertyId in self.propToConfig):
    496             propValue = self.readVhalProperty(propertyId)
    497             asserts.assertNotEqual(None, propValue, "expected value, prop: 0x%x" % propertyId)
    498             asserts.assertEqual(propertyId, propValue['prop'])
    499             return True, self.extractValue(propValue)
    500         else:
    501             return False, None
    502 
    503     def testInfoVinMakeModel(self):
    504         """Verifies INFO_VIN, INFO_MAKE, INFO_MODEL properties"""
    505         stringProperties = set([
    506             self.vtypes.VehicleProperty.INFO_VIN,
    507             self.vtypes.VehicleProperty.INFO_MAKE,
    508             self.vtypes.VehicleProperty.INFO_MODEL])
    509         for prop in stringProperties:
    510             supported, val = self.getValueIfPropSupported(prop)
    511             if supported:
    512                 asserts.assertEqual(str, type(val), "prop: 0x%x" % prop)
    513                 asserts.assertLess(0, (len(val)), "prop: 0x%x" % prop)
    514 
    515     def testGlobalFloatProperties(self):
    516         """Verifies that values of global float properties are in the correct range"""
    517         floatProperties = {
    518             self.vtypes.VehicleProperty.ENV_CABIN_TEMPERATURE: (-50, 100),  # celsius
    519             self.vtypes.VehicleProperty.ENV_OUTSIDE_TEMPERATURE: (-50, 100),  # celsius
    520             self.vtypes.VehicleProperty.ENGINE_RPM : (0, 30000),  # RPMs
    521             self.vtypes.VehicleProperty.ENGINE_OIL_TEMP : (-50, 150),  # celsius
    522             self.vtypes.VehicleProperty.ENGINE_COOLANT_TEMP : (-50, 150),  #
    523             self.vtypes.VehicleProperty.PERF_VEHICLE_SPEED : (0, 150),  # m/s, 150 m/s = 330 mph
    524             self.vtypes.VehicleProperty.PERF_ODOMETER : (0, 1000000),  # km
    525             self.vtypes.VehicleProperty.INFO_FUEL_CAPACITY : (0, 1000000),  # milliliter
    526             self.vtypes.VehicleProperty.INFO_MODEL_YEAR : (1901, 2101),  # year
    527         }
    528 
    529         for prop, validRange in floatProperties.iteritems():
    530             supported, val = self.getValueIfPropSupported(prop)
    531             if supported:
    532                 asserts.assertEqual(float, type(val))
    533                 self.assertValueInRangeForProp(val, validRange[0], validRange[1], prop)
    534 
    535     def testGlobalBoolProperties(self):
    536         """Verifies that values of global boolean properties are in the correct range"""
    537         booleanProperties = set([
    538             self.vtypes.VehicleProperty.PARKING_BRAKE_ON,
    539             self.vtypes.VehicleProperty.FUEL_LEVEL_LOW,
    540             self.vtypes.VehicleProperty.NIGHT_MODE,
    541             self.vtypes.VehicleProperty.DOOR_LOCK,
    542             self.vtypes.VehicleProperty.MIRROR_LOCK,
    543             self.vtypes.VehicleProperty.MIRROR_FOLD,
    544             self.vtypes.VehicleProperty.SEAT_BELT_BUCKLED,
    545             self.vtypes.VehicleProperty.WINDOW_LOCK,
    546         ])
    547         for prop in booleanProperties:
    548             self.verifyEnumPropIfSupported(prop, [0, 1])
    549 
    550     def testGlobalEnumProperties(self):
    551         """Verifies that values of global enum properties are in the correct range"""
    552         enumProperties = {
    553             self.vtypes.VehicleProperty.GEAR_SELECTION : self.vtypes.VehicleGear,
    554             self.vtypes.VehicleProperty.CURRENT_GEAR : self.vtypes.VehicleGear,
    555             self.vtypes.VehicleProperty.TURN_SIGNAL_STATE : self.vtypes.VehicleTurnSignal,
    556             self.vtypes.VehicleProperty.IGNITION_STATE : self.vtypes.VehicleIgnitionState,
    557         }
    558         for prop, enum in enumProperties.iteritems():
    559             self.verifyEnumPropIfSupported(prop, vars(enum).values())
    560 
    561     def testDebugDump(self):
    562         """Verifies that call to IVehicle#debugDump is not failing"""
    563         dumpStr = self.vehicle.debugDump()
    564         asserts.assertNotEqual(None, dumpStr)
    565 
    566     def extractValue(self, propValue):
    567         """Extracts value depending on data type of the property"""
    568         if propValue == None:
    569             return None
    570 
    571         # Extract data type
    572         dataType = propValue['prop'] & self.vtypes.VehiclePropertyType.MASK
    573         val = propValue['value']
    574         if self.vtypes.VehiclePropertyType.STRING == dataType:
    575             asserts.assertNotEqual(None, val['stringValue'])
    576             return val['stringValue']
    577         elif self.vtypes.VehiclePropertyType.INT32 == dataType or \
    578                 self.vtypes.VehiclePropertyType.BOOLEAN == dataType:
    579             asserts.assertEqual(1, len(val["int32Values"]))
    580             return val["int32Values"][0]
    581         elif self.vtypes.VehiclePropertyType.INT64 == dataType:
    582             asserts.assertEqual(1, len(val["int64Values"]))
    583             return val["int64Values"][0]
    584         elif self.vtypes.VehiclePropertyType.FLOAT == dataType:
    585             asserts.assertEqual(1, len(val["floatValues"]))
    586             return val["floatValues"][0]
    587         elif self.vtypes.VehiclePropertyType.INT32_VEC == dataType:
    588             asserts.assertLess(0, len(val["int32Values"]))
    589             return val["int32Values"]
    590         elif self.vtypes.VehiclePropertyType.FLOAT_VEC == dataType:
    591             asserts.assertLess(0, len(val["floatValues"]))
    592             return val["floatValues"]
    593         elif self.vtypes.VehiclePropertyType.BYTES == dataType:
    594             asserts.assertLess(0, len(val["bytes"]))
    595             return val["bytes"]
    596         else:
    597             return val
    598 
    599     def verifyEnumPropIfSupported(self, propertyId, validValues):
    600         """Verifies that if given property supported it is one of the value in validValues set"""
    601         supported, val = self.getValueIfPropSupported(propertyId)
    602         if supported:
    603             asserts.assertEqual(int, type(val))
    604             self.assertIntValueInRangeForProp(val, validValues, propertyId)
    605 
    606     def assertLessOrEqual(self, first, second, msg=None):
    607         """Asserts that first <= second"""
    608         if second < first:
    609             fullMsg = "%s is not less or equal to %s" % (first, second)
    610             if msg:
    611                 fullMsg = "%s %s" % (fullMsg, msg)
    612             fail(fullMsg)
    613 
    614     def assertIntValueInRangeForProp(self, value, validValues, prop):
    615         """Asserts that given value is in the validValues range"""
    616         asserts.assertTrue(value in validValues,
    617                 "Invalid value %d for property: 0x%x, expected one of: %s" % (value, prop, validValues))
    618 
    619     def assertValueInRangeForProp(self, value, rangeBegin, rangeEnd, prop):
    620         """Asserts that given value is in the range [rangeBegin, rangeEnd]"""
    621         msg = "Value %s is out of range [%s, %s] for property 0x%x" % (value, rangeBegin, rangeEnd, prop)
    622         self.assertLessOrEqual(rangeBegin, value, msg)
    623         self.assertLessOrEqual(value, rangeEnd,  msg)
    624 
    625     def getPropConfig(self, propertyId):
    626         return self.propToConfig[propertyId]
    627 
    628     def isPropSupported(self, propertyId):
    629         return self.getPropConfig(propertyId) is not None
    630 
    631     def testEngineOilTemp(self):
    632         """tests engine oil temperature.
    633 
    634         This also tests an HIDL async callback.
    635         """
    636         self.onPropertyEventCalled = 0
    637         self.onPropertySetCalled = 0
    638         self.onPropertySetErrorCalled = 0
    639 
    640         def onPropertyEvent(vehiclePropValues):
    641             logging.info("onPropertyEvent received: %s", vehiclePropValues)
    642             self.onPropertyEventCalled += 1
    643 
    644         def onPropertySet(vehiclePropValue):
    645             logging.info("onPropertySet notification received: %s", vehiclePropValue)
    646             self.onPropertySetCalled += 1
    647 
    648         def onPropertySetError(erroCode, propId, areaId):
    649             logging.info("onPropertySetError, error: %d, prop: 0x%x, area: 0x%x",
    650                          erroCode, prop, area)
    651             self.onPropertySetErrorCalled += 1
    652 
    653         config = self.getPropConfig(self.vtypes.VehicleProperty.ENGINE_OIL_TEMP)
    654         if (config is None):
    655             logging.info("ENGINE_OIL_TEMP property is not supported")
    656             return  # Property not supported, we are done here.
    657 
    658         propValue = self.readVhalProperty(self.vtypes.VehicleProperty.ENGINE_OIL_TEMP)
    659         asserts.assertEqual(1, len(propValue['value']['floatValues']))
    660         oilTemp = propValue['value']['floatValues'][0]
    661         logging.info("Current oil temperature: %f C", oilTemp)
    662         asserts.assertLess(oilTemp, 200)    # Check it is in reasinable range
    663         asserts.assertLess(-50, oilTemp)
    664 
    665         if (config["changeMode"] == self.vtypes.VehiclePropertyChangeMode.CONTINUOUS):
    666             logging.info("ENGINE_OIL_TEMP is continuous property, subscribing...")
    667             callback = self.vehicle.GetHidlCallbackInterface("IVehicleCallback",
    668                 onPropertyEvent=onPropertyEvent,
    669                 onPropertySet=onPropertySet,
    670                 onPropertySetError=onPropertySetError)
    671 
    672             subscribeOptions = {
    673                 "propId" : self.vtypes.VehicleProperty.ENGINE_OIL_TEMP,
    674                 "vehicleAreas" : 0,
    675                 "sampleRate" : 10.0,  # Hz
    676                 "flags" : self.vtypes.SubscribeFlags.HAL_EVENT,
    677             }
    678             pbSubscribeOptions = self.vtypes.Py2Pb("SubscribeOptions", subscribeOptions)
    679 
    680             self.vehicle.subscribe(callback, [pbSubscribeOptions])
    681             for _ in range(5):
    682                 if (self.onPropertyEventCalled > 0 or
    683                     self.onPropertySetCalled > 0 or
    684                     self.onPropertySetErrorCalled > 0):
    685                     return
    686                 time.sleep(1)
    687             asserts.fail("Callback not called in 5 seconds.")
    688 
    689 if __name__ == "__main__":
    690     test_runner.main()
    691