1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.car.hal;
18 
19 import android.annotation.Nullable;
20 import android.car.VehicleAreaType;
21 import android.car.feature.Flags;
22 import android.car.hardware.CarPropertyConfig;
23 import android.car.hardware.property.AreaIdConfig;
24 import android.hardware.automotive.vehicle.VehicleArea;
25 import android.hardware.automotive.vehicle.VehicleProperty;
26 import android.hardware.automotive.vehicle.VehiclePropertyAccess;
27 import android.hardware.automotive.vehicle.VehiclePropertyChangeMode;
28 import android.hardware.automotive.vehicle.VehiclePropertyType;
29 
30 import com.android.car.hal.property.PropertyHalServiceConfigs;
31 
32 import java.util.ArrayList;
33 import java.util.List;
34 import java.util.Set;
35 
36 /**
37  * HalPropConfig represents a vehicle property config.
38  */
39 public abstract class HalPropConfig {
40     private static final Set<Integer> CONFIG_ARRAY_DEFINES_SUPPORTED_ENUM_VALUES =
41             Set.of(
42                     VehicleProperty.GEAR_SELECTION,
43                     VehicleProperty.CURRENT_GEAR,
44                     VehicleProperty.DISTANCE_DISPLAY_UNITS,
45                     VehicleProperty.EV_BATTERY_DISPLAY_UNITS,
46                     VehicleProperty.TIRE_PRESSURE_DISPLAY_UNITS,
47                     VehicleProperty.FUEL_VOLUME_DISPLAY_UNITS,
48                     VehicleProperty.HVAC_TEMPERATURE_DISPLAY_UNITS,
49                     VehicleProperty.VEHICLE_SPEED_DISPLAY_UNITS);
50 
51     /**
52      * Get the property ID.
53      */
getPropId()54     public abstract int getPropId();
55 
56     /**
57      * Get the access mode.
58      */
getAccess()59     public abstract int getAccess();
60 
61     /**
62      * Get the change mode.
63      */
getChangeMode()64     public abstract int getChangeMode();
65 
66     /**
67      * Get the area configs.
68      */
getAreaConfigs()69     public abstract HalAreaConfig[] getAreaConfigs();
70 
71     /**
72      * Get the config array.
73      */
getConfigArray()74     public abstract int[] getConfigArray();
75 
76     /**
77      * Get the config string.
78      */
getConfigString()79     public abstract String getConfigString();
80 
81     /**
82      * Get the min sample rate.
83      */
getMinSampleRate()84     public abstract float getMinSampleRate();
85 
86     /**
87      * Get the max sample rate.
88      */
getMaxSampleRate()89     public abstract float getMaxSampleRate();
90 
91     /**
92      * Converts to AIDL or HIDL VehiclePropConfig.
93      */
toVehiclePropConfig()94     public abstract Object toVehiclePropConfig();
95 
96     /**
97      * Converts {@link HalPropConfig} to {@link CarPropertyConfig}.
98      *
99      * @param mgrPropertyId The Property ID used by Car Property Manager, different from the
100      *                      property ID used by VHAL.
101      */
toCarPropertyConfig(int mgrPropertyId, PropertyHalServiceConfigs propertyHalServiceConfigs)102     public CarPropertyConfig<?> toCarPropertyConfig(int mgrPropertyId,
103             PropertyHalServiceConfigs propertyHalServiceConfigs) {
104         int propId = getPropId();
105         int areaType = getVehicleAreaType(propId & VehicleArea.MASK);
106         Class<?> clazz = CarPropertyUtils.getJavaClass(propId & VehiclePropertyType.MASK);
107 
108         int access = getAccess();
109         CarPropertyConfig.Builder carPropertyConfigBuilder = CarPropertyConfig.newBuilder(clazz,
110                 mgrPropertyId, areaType).setAccess(access).setChangeMode(
111                 getChangeMode()).setConfigString(getConfigString());
112 
113         float maxSampleRate = 0f;
114         float minSampleRate = 0f;
115         if (getChangeMode() == CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_CONTINUOUS) {
116             maxSampleRate = getMaxSampleRate();
117             minSampleRate = getMinSampleRate();
118         }
119         carPropertyConfigBuilder.setMinSampleRate(minSampleRate).setMaxSampleRate(maxSampleRate);
120 
121         int[] configIntArray = getConfigArray();
122         ArrayList<Integer> configArray = new ArrayList<>(configIntArray.length);
123         long[] supportedEnumValues = null;
124         boolean shouldConfigArrayDefineSupportedEnumValues =
125                 CONFIG_ARRAY_DEFINES_SUPPORTED_ENUM_VALUES.contains(propId);
126         if (shouldConfigArrayDefineSupportedEnumValues) {
127             supportedEnumValues = new long[configIntArray.length];
128         }
129         for (int i = 0; i < configIntArray.length; i++) {
130             configArray.add(configIntArray[i]);
131             if (shouldConfigArrayDefineSupportedEnumValues) {
132                 supportedEnumValues[i] = (long) configIntArray[i];
133             }
134         }
135         carPropertyConfigBuilder.setConfigArray(configArray);
136 
137         HalAreaConfig[] halAreaConfigs = getAreaConfigs();
138         var allPossibleEnumValues = propertyHalServiceConfigs
139                 .getAllPossibleSupportedEnumValues(getPropId());
140         if (halAreaConfigs.length == 0) {
141             carPropertyConfigBuilder.addAreaIdConfig(generateAreaIdConfig(clazz,
142                     allPossibleEnumValues, /* areaId= */ 0,
143                     /* minInt32Value= */ 0, /* maxInt32Value= */ 0,
144                     /* minFloatValue= */ 0, /* maxFloatValue= */ 0,
145                     /* minInt64Value= */ 0, /* maxInt64Value= */ 0,
146                     supportedEnumValues, /* supportVariableUpdateRate= */ false, access));
147         } else {
148             for (HalAreaConfig halAreaConfig : halAreaConfigs) {
149                 if (!shouldConfigArrayDefineSupportedEnumValues) {
150                     supportedEnumValues = halAreaConfig.getSupportedEnumValues();
151                 }
152                 int areaAccess = (halAreaConfig.getAccess() == VehiclePropertyAccess.NONE)
153                         ? access : halAreaConfig.getAccess();
154                 carPropertyConfigBuilder.addAreaIdConfig(
155                         generateAreaIdConfig(clazz, allPossibleEnumValues,
156                                 halAreaConfig.getAreaId(),
157                                 halAreaConfig.getMinInt32Value(), halAreaConfig.getMaxInt32Value(),
158                                 halAreaConfig.getMinFloatValue(), halAreaConfig.getMaxFloatValue(),
159                                 halAreaConfig.getMinInt64Value(), halAreaConfig.getMaxInt64Value(),
160                                 supportedEnumValues, halAreaConfig.isVariableUpdateRateSupported(),
161                                 areaAccess));
162             }
163         }
164         return carPropertyConfigBuilder.build();
165     }
166 
generateAreaIdConfig(Class<?> clazz, @Nullable Set<Integer> allPossibleEnumValues, int areaId, int minInt32Value, int maxInt32Value, float minFloatValue, float maxFloatValue, long minInt64Value, long maxInt64Value, long[] supportedEnumValues, boolean supportVariableUpdateRate, int access)167     private AreaIdConfig generateAreaIdConfig(Class<?> clazz,
168             @Nullable Set<Integer> allPossibleEnumValues, int areaId, int minInt32Value,
169             int maxInt32Value, float minFloatValue, float maxFloatValue, long minInt64Value,
170             long maxInt64Value, long[] supportedEnumValues, boolean supportVariableUpdateRate,
171             int access) {
172         AreaIdConfig.Builder areaIdConfigBuilder = Flags.areaIdConfigAccess()
173                 ? new AreaIdConfig.Builder(access, areaId)
174                 : new AreaIdConfig.Builder(areaId);
175         if (classMatched(Integer.class, clazz)) {
176             if ((minInt32Value != 0 || maxInt32Value != 0)) {
177                 areaIdConfigBuilder.setMinValue(minInt32Value).setMaxValue(maxInt32Value);
178             }
179             // The supported enum values for {@code HVAC_FAN_DIRECTION} are specified by
180             // {@code HVAC_FAN_DIRECTION_AVAILABLE} and the supportedEnumValues are never populated.
181             if (getChangeMode() == VehiclePropertyChangeMode.ON_CHANGE &&
182                     getPropId() != VehicleProperty.HVAC_FAN_DIRECTION) {
183                 if (supportedEnumValues != null && supportedEnumValues.length > 0) {
184                     List<Integer> managerSupportedEnumValues = new ArrayList<>(
185                             supportedEnumValues.length);
186                     for (int i = 0; i < supportedEnumValues.length; i++) {
187                         managerSupportedEnumValues.add((int) supportedEnumValues[i]);
188                     }
189                     areaIdConfigBuilder.setSupportedEnumValues(managerSupportedEnumValues);
190                 } else if (allPossibleEnumValues != null) {
191                     areaIdConfigBuilder.setSupportedEnumValues(
192                             new ArrayList(allPossibleEnumValues));
193                 }
194             }
195         } else if (classMatched(Float.class, clazz) && (minFloatValue != 0 || maxFloatValue != 0)) {
196             areaIdConfigBuilder.setMinValue(minFloatValue).setMaxValue(maxFloatValue);
197         } else if (classMatched(Long.class, clazz) && (minInt64Value != 0 || maxInt64Value != 0)) {
198             areaIdConfigBuilder.setMinValue(minInt64Value).setMaxValue(maxInt64Value);
199         }
200         areaIdConfigBuilder.setSupportVariableUpdateRate(supportVariableUpdateRate);
201         return areaIdConfigBuilder.build();
202     }
203 
getVehicleAreaType(int halArea)204     private static @VehicleAreaType.VehicleAreaTypeValue int getVehicleAreaType(int halArea) {
205         switch (halArea) {
206             case VehicleArea.GLOBAL:
207                 return VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL;
208             case VehicleArea.SEAT:
209                 return VehicleAreaType.VEHICLE_AREA_TYPE_SEAT;
210             case VehicleArea.DOOR:
211                 return VehicleAreaType.VEHICLE_AREA_TYPE_DOOR;
212             case VehicleArea.WINDOW:
213                 return VehicleAreaType.VEHICLE_AREA_TYPE_WINDOW;
214             case VehicleArea.MIRROR:
215                 return VehicleAreaType.VEHICLE_AREA_TYPE_MIRROR;
216             case VehicleArea.WHEEL:
217                 return VehicleAreaType.VEHICLE_AREA_TYPE_WHEEL;
218             default:
219                 if (Flags.androidVicVehicleProperties()) {
220                     if (halArea == VehicleArea.VENDOR) {
221                         return VehicleAreaType.VEHICLE_AREA_TYPE_VENDOR;
222                     }
223                 }
224                 throw new RuntimeException("Unsupported area type " + halArea);
225         }
226     }
227 
classMatched(Class<?> class1, Class<?> class2)228     private static boolean classMatched(Class<?> class1, Class<?> class2) {
229         return class1 == class2 || class1.getComponentType() == class2;
230     }
231 }
232