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