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