• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python
2#
3# Copyright (C) 2016 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#      http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16#
17
18import logging
19import time
20
21from vts.runners.host import asserts
22from vts.runners.host import base_test
23from vts.runners.host import const
24from vts.runners.host import keys
25from vts.runners.host import test_runner
26from vts.utils.python.controllers import android_device
27from vts.utils.python.precondition import precondition_utils
28
29
30class 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
689if __name__ == "__main__":
690    test_runner.main()
691