• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.builtin.util.Slogf;
22 import android.car.feature.Flags;
23 import android.car.hardware.CarPropertyConfig;
24 import android.car.hardware.property.AreaIdConfig;
25 import android.hardware.automotive.vehicle.AnnotationsForVehicleProperty;
26 import android.hardware.automotive.vehicle.HasSupportedValueInfo;
27 import android.hardware.automotive.vehicle.VehicleArea;
28 import android.hardware.automotive.vehicle.VehicleProperty;
29 import android.hardware.automotive.vehicle.VehiclePropertyAccess;
30 import android.hardware.automotive.vehicle.VehiclePropertyChangeMode;
31 import android.hardware.automotive.vehicle.VehiclePropertyType;
32 
33 import com.android.car.CarLog;
34 import com.android.car.hal.property.PropertyHalServiceConfigs;
35 import com.android.internal.annotations.VisibleForTesting;
36 
37 import java.util.ArrayList;
38 import java.util.List;
39 import java.util.Set;
40 
41 /**
42  * HalPropConfig represents a vehicle property config.
43  */
44 public abstract class HalPropConfig {
45     /**
46      *  The expected length for config array for HVAC_TEMPERATURE_SET.
47      */
48     public static final int HVAC_CONFIG_ARRAY_LENGTH = 6;
49 
50     /**
51      * The @legacy_supported_values_in_config annotation defined in VehicleProperty.aidl.
52      */
53     public static final String ANNOTATION_SUPPORTED_VALUES_IN_CONFIG =
54             "legacy_supported_values_in_config";
55 
56      /**
57      * The @data_enum annotation defined in VehicleProperty.aidl.
58      */
59     public static final String ANNOTATION_DATA_ENUM = "data_enum";
60 
61     private static final String TAG = CarLog.tagFor(HalPropConfig.class);
62 
63     /**
64      * Get the property ID.
65      */
getPropId()66     public abstract int getPropId();
67 
68     /**
69      * Get the access mode.
70      */
getAccess()71     public abstract int getAccess();
72 
73     /**
74      * Get the change mode.
75      */
getChangeMode()76     public abstract int getChangeMode();
77 
78     /**
79      * Get the area configs.
80      */
getAreaConfigs()81     public abstract HalAreaConfig[] getAreaConfigs();
82 
83     /**
84      * Get the config array.
85      */
getConfigArray()86     public abstract int[] getConfigArray();
87 
88     /**
89      * Get the config string.
90      */
getConfigString()91     public abstract String getConfigString();
92 
93     /**
94      * Get the min sample rate.
95      */
getMinSampleRate()96     public abstract float getMinSampleRate();
97 
98     /**
99      * Get the max sample rate.
100      */
getMaxSampleRate()101     public abstract float getMaxSampleRate();
102 
103     /**
104      * Converts to AIDL or HIDL VehiclePropConfig.
105      */
toVehiclePropConfig()106     public abstract Object toVehiclePropConfig();
107 
108     /**
109      * Converts {@link HalPropConfig} to {@link CarPropertyConfig}.
110      *
111      * @param mgrPropertyId The Property ID used by Car Property Manager, different from the
112      *                      property ID used by VHAL.
113      */
toCarPropertyConfig(int mgrPropertyId, PropertyHalServiceConfigs propertyHalServiceConfigs)114     public CarPropertyConfig<?> toCarPropertyConfig(int mgrPropertyId,
115             PropertyHalServiceConfigs propertyHalServiceConfigs) {
116         return toCarPropertyConfig(mgrPropertyId, propertyHalServiceConfigs,
117                 /* isVhalPropId= */ false);
118     }
119 
120     /**
121      * Converts {@link HalPropConfig} to {@link CarPropertyConfig}.
122      *
123      * @param mgrPropertyId The Property ID used by Car Property Manager, different from the
124      *                      property ID used by VHAL.
125      */
toCarPropertyConfig(int mgrPropertyId, PropertyHalServiceConfigs propertyHalServiceConfigs, boolean isVhalPropId)126     public CarPropertyConfig<?> toCarPropertyConfig(int mgrPropertyId,
127             PropertyHalServiceConfigs propertyHalServiceConfigs, boolean isVhalPropId) {
128         int propId = getPropId();
129         int areaType = getVehicleAreaType(propId & VehicleArea.MASK);
130         Class<?> clazz = CarPropertyUtils.getJavaClass(propId & VehiclePropertyType.MASK);
131 
132         int access = getAccess();
133         CarPropertyConfig.Builder carPropertyConfigBuilder = CarPropertyConfig.newBuilder(clazz,
134                 mgrPropertyId, areaType).setAccess(access).setChangeMode(
135                 getChangeMode()).setConfigString(getConfigString());
136 
137         float maxSampleRate = 0f;
138         float minSampleRate = 0f;
139         if (getChangeMode() == CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_CONTINUOUS) {
140             maxSampleRate = getMaxSampleRate();
141             minSampleRate = getMinSampleRate();
142         }
143         carPropertyConfigBuilder.setMinSampleRate(minSampleRate).setMaxSampleRate(maxSampleRate);
144 
145         int[] configIntArray = getConfigArray();
146         ArrayList<Integer> configArray = new ArrayList<>(configIntArray.length);
147         long[] supportedEnumValues = null;
148         boolean shouldConfigArrayDefineSupportedEnumValues =
149                 shouldConfigArrayDefineSupportedEnumValues(propId);
150         if (shouldConfigArrayDefineSupportedEnumValues) {
151             supportedEnumValues = new long[configIntArray.length];
152         }
153         for (int i = 0; i < configIntArray.length; i++) {
154             configArray.add(configIntArray[i]);
155             if (shouldConfigArrayDefineSupportedEnumValues) {
156                 supportedEnumValues[i] = (long) configIntArray[i];
157             }
158         }
159         carPropertyConfigBuilder.setConfigArray(configArray);
160 
161         HalAreaConfig[] halAreaConfigs = getAreaConfigs();
162         var allPossibleEnumValues = propertyHalServiceConfigs
163                 .getAllPossibleSupportedEnumValues(getPropId());
164         if (halAreaConfigs.length == 0) {
165             carPropertyConfigBuilder.addAreaIdConfig(generateAreaIdConfig(clazz,
166                     allPossibleEnumValues, /* areaId= */ 0,
167                     /* minInt32Value= */ 0, /* maxInt32Value= */ 0,
168                     /* minFloatValue= */ 0, /* maxFloatValue= */ 0,
169                     /* minInt64Value= */ 0, /* maxInt64Value= */ 0,
170                     supportedEnumValues, /* supportVariableUpdateRate= */ false, access,
171                     /* hasSupportedValueInfo= */ null));
172         } else {
173             for (HalAreaConfig halAreaConfig : halAreaConfigs) {
174                 if (!shouldConfigArrayDefineSupportedEnumValues) {
175                     supportedEnumValues = halAreaConfig.getSupportedEnumValues();
176                 }
177                 int areaAccess = (halAreaConfig.getAccess() == VehiclePropertyAccess.NONE)
178                         ? access : halAreaConfig.getAccess();
179                 carPropertyConfigBuilder.addAreaIdConfig(
180                         generateAreaIdConfig(clazz, allPossibleEnumValues,
181                                 halAreaConfig.getAreaId(),
182                                 halAreaConfig.getMinInt32Value(), halAreaConfig.getMaxInt32Value(),
183                                 halAreaConfig.getMinFloatValue(), halAreaConfig.getMaxFloatValue(),
184                                 halAreaConfig.getMinInt64Value(), halAreaConfig.getMaxInt64Value(),
185                                 supportedEnumValues, halAreaConfig.isVariableUpdateRateSupported(),
186                                 areaAccess, halAreaConfig.getHasSupportedValueInfo()));
187             }
188         }
189         carPropertyConfigBuilder.setPropertyIdIsSimulationPropId(isVhalPropId);
190         return carPropertyConfigBuilder.build();
191     }
192 
193     /**
194      * Whether the property is a enum property and config array should be used to define supported
195      * values.
196      */
197     @VisibleForTesting
shouldConfigArrayDefineSupportedEnumValues(int halPropId)198     public static boolean shouldConfigArrayDefineSupportedEnumValues(int halPropId) {
199         var annotations = AnnotationsForVehicleProperty.values.get(halPropId);
200         if (annotations == null) {
201             return false;
202         }
203         return annotations.contains(ANNOTATION_SUPPORTED_VALUES_IN_CONFIG)
204                 && annotations.contains(ANNOTATION_DATA_ENUM);
205     }
206 
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, @Nullable HasSupportedValueInfo hasSupportedValueInfo)207     private AreaIdConfig generateAreaIdConfig(Class<?> clazz,
208             @Nullable Set<Integer> allPossibleEnumValues, int areaId, int minInt32Value,
209             int maxInt32Value, float minFloatValue, float maxFloatValue, long minInt64Value,
210             long maxInt64Value, long[] supportedEnumValues, boolean supportVariableUpdateRate,
211             int access, @Nullable HasSupportedValueInfo hasSupportedValueInfo) {
212         AreaIdConfig.Builder areaIdConfigBuilder = Flags.areaIdConfigAccess()
213                 ? new AreaIdConfig.Builder(access, areaId)
214                 : new AreaIdConfig.Builder(areaId);
215         if (classMatched(Integer.class, clazz)) {
216             if ((minInt32Value != 0 || maxInt32Value != 0)) {
217                 areaIdConfigBuilder.setMinValue(minInt32Value).setMaxValue(maxInt32Value);
218             }
219             // The supported enum values for {@code HVAC_FAN_DIRECTION} are specified by
220             // {@code HVAC_FAN_DIRECTION_AVAILABLE} and the supportedEnumValues are never populated.
221             if (getChangeMode() == VehiclePropertyChangeMode.ON_CHANGE &&
222                     getPropId() != VehicleProperty.HVAC_FAN_DIRECTION) {
223                 if (supportedEnumValues != null && supportedEnumValues.length > 0) {
224                     List<Integer> managerSupportedEnumValues = new ArrayList<>(
225                             supportedEnumValues.length);
226                     for (int i = 0; i < supportedEnumValues.length; i++) {
227                         managerSupportedEnumValues.add((int) supportedEnumValues[i]);
228                     }
229                     areaIdConfigBuilder.setSupportedEnumValues(managerSupportedEnumValues);
230                 } else if (allPossibleEnumValues != null) {
231                     areaIdConfigBuilder.setSupportedEnumValues(
232                             new ArrayList(allPossibleEnumValues));
233                 }
234             }
235         } else if (classMatched(Float.class, clazz) && (minFloatValue != 0 || maxFloatValue != 0)) {
236             areaIdConfigBuilder.setMinValue(minFloatValue).setMaxValue(maxFloatValue);
237         } else if (classMatched(Long.class, clazz) && (minInt64Value != 0 || maxInt64Value != 0)) {
238             areaIdConfigBuilder.setMinValue(minInt64Value).setMaxValue(maxInt64Value);
239         }
240         areaIdConfigBuilder.setSupportVariableUpdateRate(supportVariableUpdateRate);
241         if (hasSupportedValueInfo != null) {
242             if (hasSupportedValueInfo.hasMinSupportedValue) {
243                 areaIdConfigBuilder.setHasMinSupportedValue(true);
244             }
245             if (hasSupportedValueInfo.hasMaxSupportedValue) {
246                 areaIdConfigBuilder.setHasMaxSupportedValue(true);
247             }
248             if (hasSupportedValueInfo.hasSupportedValuesList) {
249                 areaIdConfigBuilder.setHasSupportedValuesList(true);
250             }
251         } else {
252             // Special logic for properties whose min/max value or supported values list
253             // may be specified through some other way.
254             switch (getPropId()) {
255                 case VehicleProperty.HVAC_FAN_DIRECTION:
256                     // The supported values for {@code HVAC_FAN_DIRECTION} are specified by
257                     // {@code HVAC_FAN_DIRECTION_AVAILABLE}.
258                     // If HVAC_FAN_DIRECTION is supported, HVAC_FAN_DIRECTION_AVAILABLE must be
259                     // supported.
260                     areaIdConfigBuilder.setHasSupportedValuesList(true);
261                     break;
262                 case VehicleProperty.HVAC_TEMPERATURE_SET:
263                     // The supported values for {@code HVAC_TEMPERATURE_SET} might be specified by
264                     // config array.
265                     int configArrayLength = getConfigArray().length;
266                     if (configArrayLength == HVAC_CONFIG_ARRAY_LENGTH) {
267                         areaIdConfigBuilder.setHasSupportedValuesList(true);
268                     } else if (configArrayLength != 0) {
269                         Slogf.e(TAG, "Unexpected config array length for HVAC_TEMPERATURE_SET, "
270                                 + "expect: %d, actual config array: %s", HVAC_CONFIG_ARRAY_LENGTH,
271                                 getConfigArray());
272                     }
273                     break;
274                 case VehicleProperty.EV_CHARGE_CURRENT_DRAW_LIMIT:
275                     // The max value for {@code EV_CHARGE_CURRENT_DRAW_LIMIT} is specified by config
276                     // array, the min value is set to 0.
277                     if (getConfigArray().length > 0) {
278                         areaIdConfigBuilder.setHasMinSupportedValue(true);
279                         areaIdConfigBuilder.setHasMaxSupportedValue(true);
280                     } else {
281                         Slogf.e(TAG, "Expect at least one element in config array for "
282                                 + "EV_CHARGE_CURRENT_DRAW_LIMIT");
283                     }
284                     break;
285             }
286 
287             // If the property has annotation: legacy_supported_values_in_config, its supported
288             // values are specified by config array.
289             var annotations = AnnotationsForVehicleProperty.values.get(getPropId());
290             if (annotations != null && annotations.contains(
291                     ANNOTATION_SUPPORTED_VALUES_IN_CONFIG) && getConfigArray().length > 0) {
292                 areaIdConfigBuilder.setHasSupportedValuesList(true);
293             }
294         }
295         return areaIdConfigBuilder.build();
296     }
297 
getVehicleAreaType(int halArea)298     private static @VehicleAreaType.VehicleAreaTypeValue int getVehicleAreaType(int halArea) {
299         switch (halArea) {
300             case VehicleArea.GLOBAL:
301                 return VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL;
302             case VehicleArea.SEAT:
303                 return VehicleAreaType.VEHICLE_AREA_TYPE_SEAT;
304             case VehicleArea.DOOR:
305                 return VehicleAreaType.VEHICLE_AREA_TYPE_DOOR;
306             case VehicleArea.WINDOW:
307                 return VehicleAreaType.VEHICLE_AREA_TYPE_WINDOW;
308             case VehicleArea.MIRROR:
309                 return VehicleAreaType.VEHICLE_AREA_TYPE_MIRROR;
310             case VehicleArea.WHEEL:
311                 return VehicleAreaType.VEHICLE_AREA_TYPE_WHEEL;
312             case VehicleArea.VENDOR:
313                 return VehicleAreaType.VEHICLE_AREA_TYPE_VENDOR;
314             default:
315                 throw new RuntimeException("Unsupported area type " + halArea);
316         }
317     }
318 
classMatched(Class<?> class1, Class<?> class2)319     private static boolean classMatched(Class<?> class1, Class<?> class2) {
320         return class1 == class2 || class1.getComponentType() == class2;
321     }
322 }
323