1 /*
2  * Copyright (C) 2023 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 android.car.cts.utils;
18 
19 import static android.Manifest.permission.ACCESS_FINE_LOCATION;
20 
21 import static com.google.common.truth.Truth.assertThat;
22 import static com.google.common.truth.Truth.assertWithMessage;
23 
24 import android.car.Car;
25 import android.car.VehicleAreaType;
26 import android.car.VehiclePropertyIds;
27 import android.car.VehicleUnit;
28 import android.car.feature.Flags;
29 import android.car.hardware.CarHvacFanDirection;
30 import android.car.hardware.CarPropertyConfig;
31 import android.car.hardware.CarPropertyValue;
32 import android.car.hardware.property.CarPropertyManager;
33 import android.car.hardware.property.LocationCharacterization;
34 import android.car.hardware.property.VehicleAutonomousState;
35 import android.car.hardware.property.VehicleSizeClass;
36 import android.car.hardware.property.VehicleTurnSignal;
37 import android.util.ArraySet;
38 
39 import com.google.common.collect.ImmutableList;
40 import com.google.common.collect.ImmutableSet;
41 import com.google.common.collect.Sets;
42 
43 import java.util.Arrays;
44 import java.util.List;
45 import java.util.stream.Collectors;
46 
47 /**
48  * Provides a list of verifiers for vehicle properties.
49  */
50 public class VehiclePropertyVerifiers {
51 
VehiclePropertyVerifiers()52     private VehiclePropertyVerifiers() {
53         throw new UnsupportedOperationException("Should only be used as a static class");
54     }
55 
56     private static final int LOCATION_CHARACTERIZATION_VALID_VALUES_MASK =
57             LocationCharacterization.PRIOR_LOCATIONS
58             | LocationCharacterization.GYROSCOPE_FUSION
59             | LocationCharacterization.ACCELEROMETER_FUSION
60             | LocationCharacterization.COMPASS_FUSION
61             | LocationCharacterization.WHEEL_SPEED_FUSION
62             | LocationCharacterization.STEERING_ANGLE_FUSION
63             | LocationCharacterization.CAR_SPEED_FUSION
64             | LocationCharacterization.DEAD_RECKONED
65             | LocationCharacterization.RAW_GNSS_ONLY;
66 
67     private static final ImmutableSet<Integer> HVAC_TEMPERATURE_DISPLAY_UNITS =
68             ImmutableSet.<Integer>builder().add(VehicleUnit.CELSIUS,
69                     VehicleUnit.FAHRENHEIT).build();
70 
71     private static final ImmutableSet<Integer> SINGLE_HVAC_FAN_DIRECTIONS =
72             ImmutableSet.of(
73                             CarHvacFanDirection.UNKNOWN,
74                             CarHvacFanDirection.FACE,
75                             CarHvacFanDirection.FLOOR,
76                             CarHvacFanDirection.DEFROST);
77 
78     private static final ImmutableSet<Integer> ALL_POSSIBLE_HVAC_FAN_DIRECTIONS =
79             generateAllPossibleHvacFanDirections();
80 
81     private static final ImmutableSet<Integer> CAR_HVAC_FAN_DIRECTION_UNWRITABLE_STATES =
82             ImmutableSet.<Integer>builder()
83                     .add(
84                             CarHvacFanDirection.UNKNOWN)
85                     .build();
86     private static final ImmutableSet<Integer> VEHICLE_SIZE_CLASSES =
87             ImmutableSet.<Integer>builder()
88                     .add(
89                             VehicleSizeClass.EPA_TWO_SEATER,
90                             VehicleSizeClass.EPA_MINICOMPACT,
91                             VehicleSizeClass.EPA_SUBCOMPACT,
92                             VehicleSizeClass.EPA_COMPACT,
93                             VehicleSizeClass.EPA_MIDSIZE,
94                             VehicleSizeClass.EPA_LARGE,
95                             VehicleSizeClass.EPA_SMALL_STATION_WAGON,
96                             VehicleSizeClass.EPA_MIDSIZE_STATION_WAGON,
97                             VehicleSizeClass.EPA_LARGE_STATION_WAGON,
98                             VehicleSizeClass.EPA_SMALL_PICKUP_TRUCK,
99                             VehicleSizeClass.EPA_STANDARD_PICKUP_TRUCK,
100                             VehicleSizeClass.EPA_VAN,
101                             VehicleSizeClass.EPA_MINIVAN,
102                             VehicleSizeClass.EPA_SMALL_SUV,
103                             VehicleSizeClass.EPA_STANDARD_SUV,
104                             VehicleSizeClass.EU_A_SEGMENT,
105                             VehicleSizeClass.EU_B_SEGMENT,
106                             VehicleSizeClass.EU_C_SEGMENT,
107                             VehicleSizeClass.EU_D_SEGMENT,
108                             VehicleSizeClass.EU_E_SEGMENT,
109                             VehicleSizeClass.EU_F_SEGMENT,
110                             VehicleSizeClass.EU_J_SEGMENT,
111                             VehicleSizeClass.EU_M_SEGMENT,
112                             VehicleSizeClass.EU_S_SEGMENT,
113                             VehicleSizeClass.JPN_KEI,
114                             VehicleSizeClass.JPN_SMALL_SIZE,
115                             VehicleSizeClass.JPN_NORMAL_SIZE,
116                             VehicleSizeClass.US_GVWR_CLASS_1_CV,
117                             VehicleSizeClass.US_GVWR_CLASS_2_CV,
118                             VehicleSizeClass.US_GVWR_CLASS_3_CV,
119                             VehicleSizeClass.US_GVWR_CLASS_4_CV,
120                             VehicleSizeClass.US_GVWR_CLASS_5_CV,
121                             VehicleSizeClass.US_GVWR_CLASS_6_CV,
122                             VehicleSizeClass.US_GVWR_CLASS_7_CV,
123                             VehicleSizeClass.US_GVWR_CLASS_8_CV)
124                     .build();
125     private static final ImmutableSet<Integer> TURN_SIGNAL_STATES =
126             ImmutableSet.<Integer>builder().add(VehicleTurnSignal.STATE_NONE,
127                     VehicleTurnSignal.STATE_RIGHT, VehicleTurnSignal.STATE_LEFT).build();
128     private static final ImmutableSet<Integer> VEHICLE_AUTONOMOUS_STATES =
129             ImmutableSet.<Integer>builder()
130                     .add(
131                             VehicleAutonomousState.LEVEL_0,
132                             VehicleAutonomousState.LEVEL_1,
133                             VehicleAutonomousState.LEVEL_2,
134                             VehicleAutonomousState.LEVEL_3,
135                             VehicleAutonomousState.LEVEL_4,
136                             VehicleAutonomousState.LEVEL_5)
137                     .build();
138 
139     /** Gets the verifier builder for PERF_STEERING_ANGLE. */
getPerfSteeringAngleVerifierBuilder()140     public static VehiclePropertyVerifier.Builder<Float> getPerfSteeringAngleVerifierBuilder() {
141         VehiclePropertyVerifier.Builder<Float> verifierBuilder =
142                 VehiclePropertyVerifier.newBuilder(
143                                 VehiclePropertyIds.PERF_STEERING_ANGLE,
144                                 CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ,
145                                 VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL,
146                                 CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_CONTINUOUS,
147                                 Float.class)
148                         .addReadPermission(Car.PERMISSION_READ_STEERING_STATE);
149 
150         return Flags.vehicleProperty25q23pPermissions()
151                 ? verifierBuilder.addReadPermission(Car.PERMISSION_READ_STEERING_STATE_3P)
152                 : verifierBuilder;
153     }
154 
155     /**
156      * Gets the verifier builder for LOCATION_CHARACTERIZATION.
157      */
158     public static VehiclePropertyVerifier.Builder<Integer>
getLocationCharacterizationVerifierBuilder()159             getLocationCharacterizationVerifierBuilder() {
160         return getLocationCharacterizationVerifierBuilder(
161                 /* carPropertyManager= */ null, VehiclePropertyIds.LOCATION_CHARACTERIZATION,
162                 ACCESS_FINE_LOCATION);
163     }
164 
165     /**
166      * Gets the verifier for LOCATION_CHARACTERIZATION.
167      */
getLocationCharacterizationVerifier( CarPropertyManager carPropertyManager)168     public static VehiclePropertyVerifier<Integer> getLocationCharacterizationVerifier(
169             CarPropertyManager carPropertyManager) {
170         return getLocationCharacterizationVerifier(
171             carPropertyManager,
172             VehiclePropertyIds.LOCATION_CHARACTERIZATION,
173             ACCESS_FINE_LOCATION);
174     }
175 
176     /**
177      * Gets the verifier for backported LOCATION_CHARACTERIZATION.
178      *
179      * @param carPropertyManager the car property manager instance.
180      * @param propertyId the backported property ID.
181      * @param readPermission the permission for the backported property.
182      */
getLocationCharacterizationVerifier( CarPropertyManager carPropertyManager, int propertyId, String readPermission)183     public static VehiclePropertyVerifier<Integer> getLocationCharacterizationVerifier(
184             CarPropertyManager carPropertyManager,
185             int propertyId, String readPermission) {
186         var builder = getLocationCharacterizationVerifierBuilder(
187                 carPropertyManager, propertyId, readPermission);
188         if (VehiclePropertyVerifier.isAtLeastU()) {
189             builder.requireProperty();
190         }
191         return builder.build();
192     }
193 
194     /**
195      * Gets the verifier for {@code HVAC_DEFROSTER}.
196      */
getHvacDefrosterVerifier( CarPropertyManager carPropertyManager)197     public static VehiclePropertyVerifier<Boolean> getHvacDefrosterVerifier(
198             CarPropertyManager carPropertyManager) {
199         return getHvacDefrosterVerifierBuilder().setCarPropertyManager(carPropertyManager).build();
200     }
201 
202     /**
203      * Gets the verifier builder for {@code HVAC_DEFROSTER}.
204      */
getHvacDefrosterVerifierBuilder()205     public static VehiclePropertyVerifier.Builder<Boolean> getHvacDefrosterVerifierBuilder() {
206         return VehiclePropertyVerifier.newBuilder(
207                         VehiclePropertyIds.HVAC_DEFROSTER,
208                         CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ_WRITE,
209                         VehicleAreaType.VEHICLE_AREA_TYPE_WINDOW,
210                         CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE,
211                         Boolean.class)
212                 .addReadPermission(Car.PERMISSION_CONTROL_CAR_CLIMATE)
213                 .addWritePermission(Car.PERMISSION_CONTROL_CAR_CLIMATE);
214     }
215 
216     /**
217      * Gets the verifier for {@code HVAC_SIDE_MIRROR_HEAT}.
218      */
getHvacSideMirrorHeatVerifier( CarPropertyManager carPropertyManager)219     public static VehiclePropertyVerifier<Integer> getHvacSideMirrorHeatVerifier(
220             CarPropertyManager carPropertyManager) {
221         return getHvacSideMirrorHeatVerifierBuilder().setCarPropertyManager(carPropertyManager)
222                 .build();
223     }
224 
225     /**
226      * Gets the verifier builder for {@code HVAC_SIDE_MIRROR_HEAT}.
227      */
getHvacSideMirrorHeatVerifierBuilder()228     public static VehiclePropertyVerifier.Builder<Integer> getHvacSideMirrorHeatVerifierBuilder() {
229         return VehiclePropertyVerifier.newBuilder(
230                         VehiclePropertyIds.HVAC_SIDE_MIRROR_HEAT,
231                         CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ_WRITE,
232                         VehicleAreaType.VEHICLE_AREA_TYPE_MIRROR,
233                         CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE,
234                         Integer.class)
235                 .requireMinMaxValues()
236                 .requireMinValuesToBeZero()
237                 .addReadPermission(Car.PERMISSION_CONTROL_CAR_CLIMATE)
238                 .addWritePermission(Car.PERMISSION_CONTROL_CAR_CLIMATE);
239     }
240 
241     /**
242      * Gets the verifier for {@code HVAC_STEERING_WHEEL_HEAT}.
243      */
getHvacSteeringWheelHeatVerifier( CarPropertyManager carPropertyManager)244     public static VehiclePropertyVerifier<Integer> getHvacSteeringWheelHeatVerifier(
245             CarPropertyManager carPropertyManager) {
246         return getHvacSteeringWheelHeatVerifierBuilder().setCarPropertyManager(carPropertyManager)
247                 .build();
248     }
249 
250     /**
251      * Gets the verifier builder for {@code HVAC_STEERING_WHEEL_HEAT}.
252      */
253     public static VehiclePropertyVerifier.Builder<Integer>
getHvacSteeringWheelHeatVerifierBuilder()254             getHvacSteeringWheelHeatVerifierBuilder() {
255         return VehiclePropertyVerifier.newBuilder(
256                         VehiclePropertyIds.HVAC_STEERING_WHEEL_HEAT,
257                         CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ_WRITE,
258                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL,
259                         CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE,
260                         Integer.class)
261                 .requireMinMaxValues()
262                 .requireZeroToBeContainedInMinMaxRanges()
263                 .addReadPermission(Car.PERMISSION_CONTROL_CAR_CLIMATE)
264                 .addWritePermission(Car.PERMISSION_CONTROL_CAR_CLIMATE);
265     }
266 
267     /**
268      * Gets the verifier for {@code HVAC_TEMPERATURE_DISPLAY_UNITS}.
269      */
getHvacTemperatureDisplayUnitsVerifier( CarPropertyManager carPropertyManager)270     public static VehiclePropertyVerifier<Integer> getHvacTemperatureDisplayUnitsVerifier(
271             CarPropertyManager carPropertyManager) {
272         return getHvacTemperatureDisplayUnitsVerifierBuilder()
273                 .setCarPropertyManager(carPropertyManager).build();
274     }
275 
276     /**
277      * Gets the verifier builder for {@code HVAC_TEMPERATURE_DISPLAY_UNITS}.
278      */
279     public static VehiclePropertyVerifier.Builder<Integer>
getHvacTemperatureDisplayUnitsVerifierBuilder()280             getHvacTemperatureDisplayUnitsVerifierBuilder() {
281         VehiclePropertyVerifier.Builder builder = VehiclePropertyVerifier.newBuilder(
282                         VehiclePropertyIds.HVAC_TEMPERATURE_DISPLAY_UNITS,
283                         CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ_WRITE,
284                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL,
285                         CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE,
286                         Integer.class)
287                 .setAllPossibleEnumValues(HVAC_TEMPERATURE_DISPLAY_UNITS)
288                 .setPossibleConfigArrayValues(HVAC_TEMPERATURE_DISPLAY_UNITS)
289                 .requirePropertyValueTobeInConfigArray()
290                 .verifySetterWithConfigArrayValues()
291                 .addReadPermission(Car.PERMISSION_CONTROL_CAR_CLIMATE)
292                 .addWritePermission(Car.PERMISSION_CONTROL_CAR_CLIMATE);
293 
294         if (VehiclePropertyVerifier.isAtLeastU()) {
295             builder.addReadPermission(Car.PERMISSION_READ_DISPLAY_UNITS);
296         }
297         return builder;
298     }
299 
300     /**
301      * Gets the verifier for {@code HVAC_TEMPERATURE_VALUE_SUGGESTION}.
302      */
getHvacTemperatureValueSuggestionVerifier( CarPropertyManager carPropertyManager)303     public static VehiclePropertyVerifier<Float[]> getHvacTemperatureValueSuggestionVerifier(
304             CarPropertyManager carPropertyManager) {
305         return getHvacTemperatureValueSuggestionVerifierBuilder()
306                 .setCarPropertyManager(carPropertyManager).build();
307     }
308 
309     /**
310      * Gets the verifier builder for {@code HVAC_TEMPERATURE_VALUE_SUGGESTION}.
311      */
312     public static VehiclePropertyVerifier.Builder<Float[]>
getHvacTemperatureValueSuggestionVerifierBuilder()313             getHvacTemperatureValueSuggestionVerifierBuilder() {
314         return VehiclePropertyVerifier.newBuilder(
315                         VehiclePropertyIds.HVAC_TEMPERATURE_VALUE_SUGGESTION,
316                         CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ_WRITE,
317                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL,
318                         CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE,
319                         Float[].class)
320                 .setCarPropertyConfigVerifier(
321                         (verifierContext, carPropertyConfig) -> {
322                             // HVAC_TEMPERATURE_VALUE_SUGGESTION's access must be read+write.
323                             assertThat((Flags.areaIdConfigAccess()
324                                     ? carPropertyConfig.getAreaIdConfig(0).getAccess()
325                                     : carPropertyConfig.getAccess())).isEqualTo(
326                                     CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ_WRITE);
327                         })
328                 .setCarPropertyValueVerifier(
329                         (verifierContext, carPropertyConfig, propertyId, areaId, timestampNanos,
330                                 temperatureSuggestion) -> {
331                             assertWithMessage(
332                                             "HVAC_TEMPERATURE_VALUE_SUGGESTION Float[] value"
333                                                 + " must be size 4.")
334                                     .that(temperatureSuggestion.length)
335                                     .isEqualTo(4);
336 
337                             Float requestedTempUnits = temperatureSuggestion[1];
338                             assertWithMessage(
339                                             "The value at index 1 must be one of"
340                                                 + " {VehicleUnit#CELSIUS, VehicleUnit#FAHRENHEIT}"
341                                                 + " which correspond to values {"
342                                                 + (float) VehicleUnit.CELSIUS
343                                                 + ", "
344                                                 + (float) VehicleUnit.FAHRENHEIT
345                                                 + "}.")
346                                     .that(requestedTempUnits)
347                                     .isIn(ImmutableList.of((float) VehicleUnit.CELSIUS,
348                                             (float) VehicleUnit.FAHRENHEIT));
349                         })
350                 .addReadPermission(Car.PERMISSION_CONTROL_CAR_CLIMATE)
351                 .addWritePermission(Car.PERMISSION_CONTROL_CAR_CLIMATE);
352     }
353 
354     /**
355      * Gets the verifier for {@code HVAC_POWER_ON}.
356      */
getHvacPowerOnVerifier( CarPropertyManager carPropertyManager)357     public static VehiclePropertyVerifier<Boolean> getHvacPowerOnVerifier(
358             CarPropertyManager carPropertyManager) {
359         return getHvacPowerOnVerifierBuilder().setCarPropertyManager(carPropertyManager)
360                 .build();
361     }
362 
363     /**
364      * Gets the verifier builder for {@code HVAC_POWER_ON}.
365      */
getHvacPowerOnVerifierBuilder()366     public static VehiclePropertyVerifier.Builder<Boolean> getHvacPowerOnVerifierBuilder() {
367         return VehiclePropertyVerifier.newBuilder(
368                         VehiclePropertyIds.HVAC_POWER_ON,
369                         CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ_WRITE,
370                         VehicleAreaType.VEHICLE_AREA_TYPE_SEAT,
371                         CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE,
372                         Boolean.class)
373                 .setConfigArrayVerifier(
374                         (verifierContext, configArray) -> {
375                             CarPropertyConfig<?> hvacPowerOnCarPropertyConfig =
376                                     verifierContext.getCarPropertyManager().getCarPropertyConfig(
377                                             VehiclePropertyIds.HVAC_POWER_ON);
378                             for (int powerDependentProperty : configArray) {
379                                 CarPropertyConfig<?> powerDependentCarPropertyConfig =
380                                         verifierContext.getCarPropertyManager()
381                                                 .getCarPropertyConfig(powerDependentProperty);
382                                 if (powerDependentCarPropertyConfig == null) {
383                                     continue;
384                                 }
385                                 assertWithMessage(
386                                                 "HVAC_POWER_ON configArray must only contain"
387                                                     + " VehicleAreaSeat type properties: "
388                                                         + VehiclePropertyIds.toString(
389                                                                 powerDependentProperty))
390                                         .that(powerDependentCarPropertyConfig.getAreaType())
391                                         .isEqualTo(VehicleAreaType.VEHICLE_AREA_TYPE_SEAT);
392 
393                                 for (int powerDependentAreaId :
394                                         powerDependentCarPropertyConfig.getAreaIds()) {
395                                     boolean powerDependentAreaIdIsContained = false;
396                                     for (int hvacPowerOnAreaId :
397                                             hvacPowerOnCarPropertyConfig.getAreaIds()) {
398                                         if ((powerDependentAreaId & hvacPowerOnAreaId)
399                                                 == powerDependentAreaId) {
400                                             powerDependentAreaIdIsContained = true;
401                                             break;
402                                         }
403                                     }
404                                     assertWithMessage(
405                                             "HVAC_POWER_ON's area IDs must contain the area IDs"
406                                                     + " of power dependent property: "
407                                                     + VehiclePropertyIds.toString(
408                                                     powerDependentProperty)).that(
409                                             powerDependentAreaIdIsContained).isTrue();
410                                 }
411                             }
412                         })
413                 .addReadPermission(Car.PERMISSION_CONTROL_CAR_CLIMATE)
414                 .addWritePermission(Car.PERMISSION_CONTROL_CAR_CLIMATE);
415     }
416 
417     /**
418      * Gets the verifier for {@code HVAC_FAN_SPEED}.
419      */
420     public static VehiclePropertyVerifier<Integer> getHvacFanSpeedVerifier(
421             CarPropertyManager carPropertyManager) {
422         return getHvacFanSpeedVerifierBuilder().setCarPropertyManager(carPropertyManager).build();
423     }
424 
425     /**
426      * Gets the verifier builder for {@code HVAC_FAN_SPEED}.
427      */
428     public static VehiclePropertyVerifier.Builder<Integer> getHvacFanSpeedVerifierBuilder() {
429         return VehiclePropertyVerifier.newBuilder(
430                         VehiclePropertyIds.HVAC_FAN_SPEED,
431                         CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ_WRITE,
432                         VehicleAreaType.VEHICLE_AREA_TYPE_SEAT,
433                         CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE,
434                         Integer.class)
435                 .requireMinMaxValues()
436                 .setPossiblyDependentOnHvacPowerOn()
437                 .addReadPermission(Car.PERMISSION_CONTROL_CAR_CLIMATE)
438                 .addWritePermission(Car.PERMISSION_CONTROL_CAR_CLIMATE);
439     }
440 
441     /**
442      * Gets the verifier for {@code HVAC_FAN_DIRECTION_AVAILABLE}.
443      */
444     public static VehiclePropertyVerifier<Integer[]> getHvacFanDirectionAvailableVerifier(
445             CarPropertyManager carPropertyManager) {
446         return getHvacFanDirectionAvailableVerifierBuilder()
447                 .setCarPropertyManager(carPropertyManager).build();
448     }
449 
450     /**
451      * Gets the verifier for {@code HVAC_FAN_DIRECTION_AVAILABLE}.
452      */
453     public static VehiclePropertyVerifier.Builder<Integer[]>
454             getHvacFanDirectionAvailableVerifierBuilder() {
455         return VehiclePropertyVerifier.newBuilder(
456                         VehiclePropertyIds.HVAC_FAN_DIRECTION_AVAILABLE,
457                         CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ,
458                         VehicleAreaType.VEHICLE_AREA_TYPE_SEAT,
459                         CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_STATIC,
460                         Integer[].class)
461                 .setPossiblyDependentOnHvacPowerOn()
462                 .setAreaIdsVerifier(
463                         (verifierContext, areaIds) -> {
464                             CarPropertyConfig<?> hvacFanDirectionCarPropertyConfig =
465                                     verifierContext.getCarPropertyManager().getCarPropertyConfig(
466                                             VehiclePropertyIds.HVAC_FAN_DIRECTION);
467                             assertWithMessage(
468                                             "HVAC_FAN_DIRECTION must be implemented if "
469                                                     + "HVAC_FAN_DIRECTION_AVAILABLE is implemented")
470                                     .that(hvacFanDirectionCarPropertyConfig)
471                                     .isNotNull();
472 
473                             assertWithMessage(
474                                             "HVAC_FAN_DIRECTION_AVAILABLE area IDs must match the"
475                                                 + " area IDs of HVAC_FAN_DIRECTION")
476                                     .that(
477                                             Arrays.stream(areaIds)
478                                                     .boxed()
479                                                     .collect(Collectors.toList()))
480                                     .containsExactlyElementsIn(
481                                             Arrays.stream(
482                                                             hvacFanDirectionCarPropertyConfig
483                                                                     .getAreaIds())
484                                                     .boxed()
485                                                     .collect(Collectors.toList()));
486                         })
487                 .setCarPropertyValueVerifier(
488                         (verifierContext, carPropertyConfig, propertyId, areaId, timestampNanos,
489                                 fanDirectionValues) -> {
490                             assertWithMessage(
491                                             "HVAC_FAN_DIRECTION_AVAILABLE area ID: "
492                                                     + areaId
493                                                     + " must have at least 1 fan direction defined")
494                                     .that(fanDirectionValues.length)
495                                     .isAtLeast(1);
496                             assertWithMessage(
497                                             "HVAC_FAN_DIRECTION_AVAILABLE area ID: "
498                                                     + areaId
499                                                     + " must have only unique fan direction"
500                                                     + " values: "
501                                                     + Arrays.toString(fanDirectionValues))
502                                     .that(fanDirectionValues.length)
503                                     .isEqualTo(ImmutableSet.copyOf(fanDirectionValues).size());
504                             for (Integer fanDirection : fanDirectionValues) {
505                                 assertWithMessage(
506                                                 "HVAC_FAN_DIRECTION_AVAILABLE's area ID: "
507                                                         + areaId
508                                                         + " must be a valid combination of fan"
509                                                         + " directions")
510                                         .that(fanDirection)
511                                         .isIn(ALL_POSSIBLE_HVAC_FAN_DIRECTIONS);
512                             }
513                         })
514                 .addReadPermission(Car.PERMISSION_CONTROL_CAR_CLIMATE);
515     }
516 
517     /**
518      * Gets the verifier for {@code HVAC_FAN_DIRECTION}.
519      */
520     public static VehiclePropertyVerifier<Integer> getHvacFanDirectionVerifier(
521             CarPropertyManager carPropertyManager) {
522         return getHvacFanDirectionVerifierBuilder().setCarPropertyManager(carPropertyManager)
523                 .build();
524     }
525 
526     /**
527      * Gets the verifier builder for {@code HVAC_FAN_DIRECTION}.
528      */
529     public static VehiclePropertyVerifier.Builder<Integer> getHvacFanDirectionVerifierBuilder() {
530         var builder = VehiclePropertyVerifier.newBuilder(
531                         VehiclePropertyIds.HVAC_FAN_DIRECTION,
532                         CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ_WRITE,
533                         VehicleAreaType.VEHICLE_AREA_TYPE_SEAT,
534                         CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE,
535                         Integer.class)
536                 .setPossiblyDependentOnHvacPowerOn()
537                 .setAreaIdsVerifier(
538                         (verifierContext, areaIds) -> {
539                             CarPropertyConfig<?> hvacFanDirectionAvailableConfig =
540                                     verifierContext.getCarPropertyManager().getCarPropertyConfig(
541                                             VehiclePropertyIds.HVAC_FAN_DIRECTION_AVAILABLE);
542                             assertWithMessage(
543                                             "HVAC_FAN_DIRECTION_AVAILABLE must be implemented if "
544                                                     + "HVAC_FAN_DIRECTION is implemented")
545                                     .that(hvacFanDirectionAvailableConfig)
546                                     .isNotNull();
547 
548                             assertWithMessage(
549                                             "HVAC_FAN_DIRECTION area IDs must match the area IDs of"
550                                                 + " HVAC_FAN_DIRECTION_AVAILABLE")
551                                     .that(
552                                             Arrays.stream(areaIds)
553                                                     .boxed()
554                                                     .collect(Collectors.toList()))
555                                     .containsExactlyElementsIn(
556                                             Arrays.stream(
557                                                             hvacFanDirectionAvailableConfig
558                                                                     .getAreaIds())
559                                                     .boxed()
560                                                     .collect(Collectors.toList()));
561                         })
562                 .setCarPropertyValueVerifier(
563                         (verifierContext, carPropertyConfig, propertyId, areaId, timestampNanos,
564                                 hvacFanDirection) -> {
565                             CarPropertyValue<Integer[]> hvacFanDirectionAvailableCarPropertyValue =
566                                     verifierContext.getCarPropertyManager().getProperty(
567                                             VehiclePropertyIds.HVAC_FAN_DIRECTION_AVAILABLE,
568                                             areaId);
569                             assertWithMessage(
570                                             "HVAC_FAN_DIRECTION_AVAILABLE value must be available")
571                                     .that(hvacFanDirectionAvailableCarPropertyValue)
572                                     .isNotNull();
573 
574                             assertWithMessage(
575                                             "HVAC_FAN_DIRECTION_AVAILABLE area ID: "
576                                                     + areaId
577                                                     + " must include all possible fan direction"
578                                                     + " values")
579                                     .that(hvacFanDirection)
580                                     .isIn(
581                                             Arrays.asList(
582                                                     hvacFanDirectionAvailableCarPropertyValue
583                                                             .getValue()));
584                         })
585                 .setAllPossibleUnwritableValues(CAR_HVAC_FAN_DIRECTION_UNWRITABLE_STATES)
586                 .addReadPermission(Car.PERMISSION_CONTROL_CAR_CLIMATE)
587                 .addWritePermission(Car.PERMISSION_CONTROL_CAR_CLIMATE);
588 
589         if (VehiclePropertyVerifier.isAtLeastU()) {
590             builder.setAllPossibleUnwritableValues(CAR_HVAC_FAN_DIRECTION_UNWRITABLE_STATES);
591         }
592         return builder;
593     }
594 
595     /**
596      * Gets the verifier for {@code HVAC_TEMPERATURE_CURRENT}.
597      */
598     public static VehiclePropertyVerifier<Float> getHvacTemperatureCurrentVerifier(
599             CarPropertyManager carPropertyManager) {
600         return getHvacTemperatureCurrentVerifierBuilder()
601                 .setCarPropertyManager(carPropertyManager).build();
602     }
603 
604     /**
605      * Gets the verifier builder for {@code HVAC_TEMPERATURE_CURRENT}.
606      */
607     public static VehiclePropertyVerifier.Builder<Float>
608             getHvacTemperatureCurrentVerifierBuilder() {
609         return VehiclePropertyVerifier.newBuilder(
610                         VehiclePropertyIds.HVAC_TEMPERATURE_CURRENT,
611                         CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ,
612                         VehicleAreaType.VEHICLE_AREA_TYPE_SEAT,
613                         CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE,
614                         Float.class)
615                 .setPossiblyDependentOnHvacPowerOn()
616                 .addReadPermission(Car.PERMISSION_CONTROL_CAR_CLIMATE);
617     }
618 
619     /**
620      * Gets the verifier for {@code HVAC_TEMPERATURE_SET}.
621      */
622     public static VehiclePropertyVerifier<Float> getHvacTemperatureSetVerifier(
623             CarPropertyManager carPropertyManager) {
624         return getHvacTemperatureSetVerifierBuilder().setCarPropertyManager(carPropertyManager)
625                 .build();
626     }
627 
628     /**
629      * Gets the verifier builder for {@code HVAC_TEMPERATURE_SET}.
630      */
631     public static VehiclePropertyVerifier.Builder<Float> getHvacTemperatureSetVerifierBuilder() {
632         return VehiclePropertyVerifier.newBuilder(
633                         VehiclePropertyIds.HVAC_TEMPERATURE_SET,
634                         CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ_WRITE,
635                         VehicleAreaType.VEHICLE_AREA_TYPE_SEAT,
636                         CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE,
637                         Float.class)
638                 .setPossiblyDependentOnHvacPowerOn()
639                 .requireMinMaxValues()
640                 .setCarPropertyConfigVerifier(
641                         (verifierContext, carPropertyConfig) -> {
642                             List<Integer> configArray = carPropertyConfig.getConfigArray();
643                             if (configArray.isEmpty()) {
644                                 return;
645                             }
646                             assertWithMessage("HVAC_TEMPERATURE_SET config array must be size 6")
647                                     .that(configArray.size())
648                                     .isEqualTo(6);
649 
650                             assertWithMessage(
651                                             "HVAC_TEMPERATURE_SET lower bound must be less"
652                                                     + " than the upper bound for the supported"
653                                                     + " temperatures in Celsius")
654                                     .that(configArray.get(0))
655                                     .isLessThan(configArray.get(1));
656                             assertWithMessage(
657                                             "HVAC_TEMPERATURE_SET increment in Celsius"
658                                                     + " must be greater than 0")
659                                     .that(configArray.get(2))
660                                     .isGreaterThan(0);
661                             assertWithMessage(
662                                             "HVAC_TEMPERATURE_SET increment in Celsius must"
663                                                     + " be less than the difference between the"
664                                                     + " upper and lower bound supported"
665                                                     + " temperatures")
666                                     .that(configArray.get(2))
667                                     .isLessThan(configArray.get(1) - configArray.get(0));
668                             assertWithMessage(
669                                             "HVAC_TEMPERATURE_SET increment in Celsius must"
670                                                     + " evenly space the gap between upper and"
671                                                     + " lower bound")
672                                     .that(
673                                             (configArray.get(1) - configArray.get(0))
674                                                     % configArray.get(2))
675                                     .isEqualTo(0);
676                             assertWithMessage(
677                                             "HVAC_TEMPERATURE_SET lower bound must be less"
678                                                     + " than the upper bound for the supported"
679                                                     + " temperatures in Fahrenheit")
680                                     .that(configArray.get(3))
681                                     .isLessThan(configArray.get(4));
682                             assertWithMessage(
683                                             "HVAC_TEMPERATURE_SET increment in Fahrenheit"
684                                                     + " must be greater than 0")
685                                     .that(configArray.get(5))
686                                     .isGreaterThan(0);
687                             assertWithMessage(
688                                             "HVAC_TEMPERATURE_SET increment in Fahrenheit"
689                                                     + " must be less than the difference"
690                                                     + " between the upper and lower bound"
691                                                     + " supported temperatures")
692                                     .that(configArray.get(5))
693                                     .isLessThan(configArray.get(4) - configArray.get(3));
694                             assertWithMessage(
695                                             "HVAC_TEMPERATURE_SET increment in Fahrenheit"
696                                                     + " must evenly space the gap between upper"
697                                                     + " and lower bound")
698                                     .that(
699                                             (configArray.get(4) - configArray.get(3))
700                                                     % configArray.get(5))
701                                     .isEqualTo(0);
702                             assertWithMessage(
703                                     "HVAC_TEMPERATURE_SET number of supported values for "
704                                             + "Celsius and Fahrenheit must be equal.").that(
705                                     (configArray.get(1) - configArray.get(0))
706                                             / configArray.get(2)).isEqualTo(
707                                     (configArray.get(4) - configArray.get(3))
708                                             / configArray.get(5));
709 
710                             int[] supportedAreaIds = carPropertyConfig.getAreaIds();
711                             int configMinValue = configArray.get(0);
712                             int configMaxValue = configArray.get(1);
713                             for (int i = 0; i < supportedAreaIds.length; i++) {
714                                 int areaId = supportedAreaIds[i];
715                                 Float minValueFloat = (Float) carPropertyConfig.getMinValue(areaId);
716                                 Integer minValueInt = (int) (minValueFloat * 10);
717                                 assertWithMessage(
718                                         "HVAC_TEMPERATURE_SET minimum value: " + minValueInt
719                                         + " at areaId: " + areaId + " must be equal to minimum"
720                                         + " value specified in config"
721                                         + " array: " + configMinValue)
722                                         .that(minValueInt)
723                                         .isEqualTo(configMinValue);
724 
725                                 Float maxValueFloat = (Float) carPropertyConfig.getMaxValue(areaId);
726                                 Integer maxValueInt = (int) (maxValueFloat * 10);
727                                 assertWithMessage(
728                                         "HVAC_TEMPERATURE_SET maximum value: " + maxValueInt
729                                         + " at areaId: " + areaId + " must be equal to maximum"
730                                         + " value specified in config"
731                                         + " array: " + configMaxValue)
732                                         .that(maxValueInt)
733                                         .isEqualTo(configMaxValue);
734                             }
735                         })
736                 .setCarPropertyValueVerifier(
737                         (verifierContext, carPropertyConfig, propertyId, areaId, timestampNanos,
738                                 tempInCelsius) -> {
739                             List<Integer> configArray = carPropertyConfig.getConfigArray();
740                             if (configArray.isEmpty()) {
741                                 return;
742                             }
743                             Integer minTempInCelsius = configArray.get(0);
744                             Integer maxTempInCelsius = configArray.get(1);
745                             Integer incrementInCelsius = configArray.get(2);
746                             VehiclePropertyVerifier.verifyHvacTemperatureIsValid(tempInCelsius,
747                                     minTempInCelsius, maxTempInCelsius, incrementInCelsius);
748                         })
749                 .addReadPermission(Car.PERMISSION_CONTROL_CAR_CLIMATE)
750                 .addWritePermission(Car.PERMISSION_CONTROL_CAR_CLIMATE);
751     }
752 
753     /**
754      * Gets the verifier for {@code HVAC_AC_ON}.
755      */
756     public static VehiclePropertyVerifier<Boolean> getHvacAcOnVerifier(
757             CarPropertyManager carPropertyManager) {
758         return getHvacAcOnVerifierBuilder().setCarPropertyManager(carPropertyManager).build();
759     }
760 
761     /**
762      * Gets the verifier for {@code HVAC_AC_ON}.
763      */
764     public static VehiclePropertyVerifier.Builder<Boolean> getHvacAcOnVerifierBuilder() {
765         return VehiclePropertyVerifier.newBuilder(
766                         VehiclePropertyIds.HVAC_AC_ON,
767                         CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ_WRITE,
768                         VehicleAreaType.VEHICLE_AREA_TYPE_SEAT,
769                         CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE,
770                         Boolean.class)
771                 .setPossiblyDependentOnHvacPowerOn()
772                 .addReadPermission(Car.PERMISSION_CONTROL_CAR_CLIMATE)
773                 .addWritePermission(Car.PERMISSION_CONTROL_CAR_CLIMATE);
774     }
775 
776     /**
777      * Gets the verifier for {@code HVAC_ELECTRIC_DEFROSTER_ON}.
778      */
779     public static VehiclePropertyVerifier<Boolean> getHvacMaxAcOnVerifier(
780             CarPropertyManager carPropertyManager) {
781         return getHvacMaxAcOnVerifierBuilder().setCarPropertyManager(carPropertyManager).build();
782     }
783 
784     /**
785      * Gets the verifier builder for {@code HVAC_ELECTRIC_DEFROSTER_ON}.
786      */
787     public static VehiclePropertyVerifier.Builder<Boolean> getHvacMaxAcOnVerifierBuilder() {
788         return VehiclePropertyVerifier.newBuilder(
789                         VehiclePropertyIds.HVAC_MAX_AC_ON,
790                         CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ_WRITE,
791                         VehicleAreaType.VEHICLE_AREA_TYPE_SEAT,
792                         CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE,
793                         Boolean.class)
794                 .setPossiblyDependentOnHvacPowerOn()
795                 .addReadPermission(Car.PERMISSION_CONTROL_CAR_CLIMATE)
796                 .addWritePermission(Car.PERMISSION_CONTROL_CAR_CLIMATE);
797     }
798 
799     /**
800      * Gets the verifier for {@code HVAC_MAX_DEFROST_ON}.
801      */
802     public static VehiclePropertyVerifier<Boolean> getHvacMaxDefrostOnVerifier(
803             CarPropertyManager carPropertyManager) {
804         return getHvacMaxDefrostOnVerifierBuilder().setCarPropertyManager(carPropertyManager)
805                 .build();
806     }
807 
808     /**
809      * Gets the verifier builder for {@code HVAC_MAX_DEFROST_ON}.
810      */
811     public static VehiclePropertyVerifier.Builder<Boolean> getHvacMaxDefrostOnVerifierBuilder() {
812         return VehiclePropertyVerifier.newBuilder(
813                         VehiclePropertyIds.HVAC_MAX_DEFROST_ON,
814                         CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ_WRITE,
815                         VehicleAreaType.VEHICLE_AREA_TYPE_SEAT,
816                         CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE,
817                         Boolean.class)
818                 .setPossiblyDependentOnHvacPowerOn()
819                 .addReadPermission(Car.PERMISSION_CONTROL_CAR_CLIMATE)
820                 .addWritePermission(Car.PERMISSION_CONTROL_CAR_CLIMATE);
821     }
822 
823     /**
824      * Gets the verifier for {@code HVAC_RECIRC_ON}.
825      */
826     public static VehiclePropertyVerifier<Boolean> getHvacRecircOnVerifier(
827             CarPropertyManager carPropertyManager) {
828         return getHvacRecircOnVerifierBuilder().setCarPropertyManager(carPropertyManager)
829                 .build();
830     }
831 
832     /**
833      * Gets the verifier builder for {@code HVAC_RECIRC_ON}.
834      */
835     public static VehiclePropertyVerifier.Builder<Boolean> getHvacRecircOnVerifierBuilder() {
836         return VehiclePropertyVerifier.newBuilder(
837                         VehiclePropertyIds.HVAC_RECIRC_ON,
838                         CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ_WRITE,
839                         VehicleAreaType.VEHICLE_AREA_TYPE_SEAT,
840                         CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE,
841                         Boolean.class)
842                 .setPossiblyDependentOnHvacPowerOn()
843                 .addReadPermission(Car.PERMISSION_CONTROL_CAR_CLIMATE)
844                 .addWritePermission(Car.PERMISSION_CONTROL_CAR_CLIMATE);
845     }
846 
847     /**
848      * Gets the verifier for {@code HVAC_AUTO_ON}.
849      */
850     public static VehiclePropertyVerifier<Boolean> getHvacAutoOnVerifier(
851             CarPropertyManager carPropertyManager) {
852         return getHvacAutoOnVerifierBuilder().setCarPropertyManager(carPropertyManager).build();
853     }
854 
855     /**
856      * Gets the verifier builder for {@code HVAC_AUTO_ON}.
857      */
858     public static VehiclePropertyVerifier.Builder<Boolean> getHvacAutoOnVerifierBuilder() {
859         return VehiclePropertyVerifier.newBuilder(
860                         VehiclePropertyIds.HVAC_AUTO_ON,
861                         CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ_WRITE,
862                         VehicleAreaType.VEHICLE_AREA_TYPE_SEAT,
863                         CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE,
864                         Boolean.class)
865                 .setPossiblyDependentOnHvacPowerOn()
866                 .addReadPermission(Car.PERMISSION_CONTROL_CAR_CLIMATE)
867                 .addWritePermission(Car.PERMISSION_CONTROL_CAR_CLIMATE);
868     }
869 
870     /**
871      * Gets the verifier for {@code HVAC_SEAT_TEMPERATURE}.
872      */
873     public static VehiclePropertyVerifier<Integer> getHvacSeatTemperatureVerifier(
874             CarPropertyManager carPropertyManager) {
875         return getHvacSeatTemperatureVerifierBuilder().setCarPropertyManager(carPropertyManager)
876                 .build();
877     }
878 
879     /**
880      * Gets the verifier builder for {@code HVAC_SEAT_TEMPERATURE}.
881      */
882     public static VehiclePropertyVerifier.Builder<Integer>
883             getHvacSeatTemperatureVerifierBuilder() {
884         return VehiclePropertyVerifier.newBuilder(
885                         VehiclePropertyIds.HVAC_SEAT_TEMPERATURE,
886                         CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ_WRITE,
887                         VehicleAreaType.VEHICLE_AREA_TYPE_SEAT,
888                         CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE,
889                         Integer.class)
890                 .setPossiblyDependentOnHvacPowerOn()
891                 .requireMinMaxValues()
892                 .requireZeroToBeContainedInMinMaxRanges()
893                 .addReadPermission(Car.PERMISSION_CONTROL_CAR_CLIMATE)
894                 .addWritePermission(Car.PERMISSION_CONTROL_CAR_CLIMATE);
895     }
896 
897     /**
898      * Gets the verifier for {@code HVAC_ACTUAL_FAN_SPEED_RPM}.
899      */
900     public static VehiclePropertyVerifier<Integer> getHvacActualFanSpeedRpmVerifier(
901             CarPropertyManager carPropertyManager) {
902         return getHvacActualFanSpeedRpmVerifierBuilder().setCarPropertyManager(carPropertyManager)
903                 .build();
904     }
905 
906     /**
907      * Gets the verifier builder for {@code HVAC_ACTUAL_FAN_SPEED_RPM}.
908      */
909     public static VehiclePropertyVerifier.Builder<Integer>
910             getHvacActualFanSpeedRpmVerifierBuilder() {
911         return VehiclePropertyVerifier.newBuilder(
912                         VehiclePropertyIds.HVAC_ACTUAL_FAN_SPEED_RPM,
913                         CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ,
914                         VehicleAreaType.VEHICLE_AREA_TYPE_SEAT,
915                         CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE,
916                         Integer.class)
917                 .setPossiblyDependentOnHvacPowerOn()
918                 .addReadPermission(Car.PERMISSION_CONTROL_CAR_CLIMATE);
919     }
920 
921     /**
922      * Gets the verifier for {@code HVAC_AUTO_RECIRC_ON}.
923      */
924     public static VehiclePropertyVerifier<Boolean> getHvacAutoRecircOnVerifier(
925             CarPropertyManager carPropertyManager) {
926         return getHvacAutoRecircOnVerifierBuilder().setCarPropertyManager(carPropertyManager)
927                 .build();
928     }
929 
930     /**
931      * Gets the verifier builder for {@code HVAC_AUTO_RECIRC_ON}.
932      */
933     public static VehiclePropertyVerifier.Builder<Boolean> getHvacAutoRecircOnVerifierBuilder() {
934         return VehiclePropertyVerifier.newBuilder(
935                         VehiclePropertyIds.HVAC_AUTO_RECIRC_ON,
936                         CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ_WRITE,
937                         VehicleAreaType.VEHICLE_AREA_TYPE_SEAT,
938                         CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE,
939                         Boolean.class)
940                 .setPossiblyDependentOnHvacPowerOn()
941                 .addReadPermission(Car.PERMISSION_CONTROL_CAR_CLIMATE)
942                 .addWritePermission(Car.PERMISSION_CONTROL_CAR_CLIMATE);
943     }
944 
945     /**
946      * Gets the verifier for {@code HVAC_SEAT_VENTILATION}.
947      */
948     public static VehiclePropertyVerifier<Integer> getHvacSeatVentilationVerifier(
949             CarPropertyManager carPropertyManager) {
950         return getHvacSeatVentilationVerifierBuilder().setCarPropertyManager(carPropertyManager)
951                 .build();
952     }
953 
954     /**
955      * Gets the verifier builder for {@code HVAC_SEAT_VENTILATION}.
956      */
957     public static VehiclePropertyVerifier.Builder<Integer>
958             getHvacSeatVentilationVerifierBuilder() {
959         return VehiclePropertyVerifier.newBuilder(
960                         VehiclePropertyIds.HVAC_SEAT_VENTILATION,
961                         CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ_WRITE,
962                         VehicleAreaType.VEHICLE_AREA_TYPE_SEAT,
963                         CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE,
964                         Integer.class)
965                 .setPossiblyDependentOnHvacPowerOn()
966                 .requireMinMaxValues()
967                 .requireMinValuesToBeZero()
968                 .addReadPermission(Car.PERMISSION_CONTROL_CAR_CLIMATE)
969                 .addWritePermission(Car.PERMISSION_CONTROL_CAR_CLIMATE);
970     }
971 
972     /**
973      * Gets the verifier for {@code HVAC_DUAL_ON}.
974      */
975     public static VehiclePropertyVerifier<Boolean> getHvacDualOnVerifier(
976             CarPropertyManager carPropertyManager) {
977         return getHvacDualOnVerifierBuilder().setCarPropertyManager(carPropertyManager).build();
978     }
979 
980     /**
981      * Gets the verifier builder for {@code HVAC_DUAL_ON}.
982      */
983     public static VehiclePropertyVerifier.Builder<Boolean> getHvacDualOnVerifierBuilder() {
984         return VehiclePropertyVerifier.newBuilder(
985                         VehiclePropertyIds.HVAC_DUAL_ON,
986                         CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ_WRITE,
987                         VehicleAreaType.VEHICLE_AREA_TYPE_SEAT,
988                         CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE,
989                         Boolean.class)
990                 .setPossiblyDependentOnHvacPowerOn()
991                 .setAreaIdsVerifier(
992                         (verifierContext, areaIds) -> {
993                             CarPropertyConfig<?> hvacTempSetCarPropertyConfig =
994                                     verifierContext.getCarPropertyManager().getCarPropertyConfig(
995                                             VehiclePropertyIds.HVAC_TEMPERATURE_SET);
996                             if (hvacTempSetCarPropertyConfig == null) {
997                                 return;
998                             }
999                             ImmutableSet<Integer> hvacTempSetAreaIds =
1000                                     ImmutableSet.copyOf(
1001                                             Arrays.stream(hvacTempSetCarPropertyConfig.getAreaIds())
1002                                                     .boxed()
1003                                                     .collect(Collectors.toList()));
1004                             ImmutableSet.Builder<Integer> allPossibleHvacDualOnAreaIdsBuilder =
1005                                     ImmutableSet.builder();
1006                             for (int i = 2; i <= hvacTempSetAreaIds.size(); i++) {
1007                                 allPossibleHvacDualOnAreaIdsBuilder.addAll(
1008                                         Sets.combinations(hvacTempSetAreaIds, i).stream()
1009                                                 .map(
1010                                                         areaIdCombo -> {
1011                                                             Integer possibleHvacDualOnAreaId = 0;
1012                                                             for (Integer areaId : areaIdCombo) {
1013                                                                 possibleHvacDualOnAreaId |= areaId;
1014                                                             }
1015                                                             return possibleHvacDualOnAreaId;
1016                                                         })
1017                                                 .collect(Collectors.toList()));
1018                             }
1019                             ImmutableSet<Integer> allPossibleHvacDualOnAreaIds =
1020                                     allPossibleHvacDualOnAreaIdsBuilder.build();
1021                             for (int areaId : areaIds) {
1022                                 assertWithMessage(
1023                                                 "HVAC_DUAL_ON area ID: "
1024                                                         + areaId
1025                                                         + " must be a combination of"
1026                                                         + " HVAC_TEMPERATURE_SET area IDs: "
1027                                                         + Arrays.toString(
1028                                                                 hvacTempSetCarPropertyConfig
1029                                                                         .getAreaIds()))
1030                                         .that(areaId)
1031                                         .isIn(allPossibleHvacDualOnAreaIds);
1032                             }
1033                         })
1034                 .addReadPermission(Car.PERMISSION_CONTROL_CAR_CLIMATE)
1035                 .addWritePermission(Car.PERMISSION_CONTROL_CAR_CLIMATE);
1036     }
1037 
1038     private static VehiclePropertyVerifier.Builder<Integer>
1039             getLocationCharacterizationVerifierBuilder(
1040                     CarPropertyManager carPropertyManager,
1041                     int locPropertyId, String readPermission) {
1042         return VehiclePropertyVerifier.newBuilder(
1043                         locPropertyId,
1044                         CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ,
1045                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL,
1046                         CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_STATIC,
1047                         Integer.class)
1048                 .setCarPropertyValueVerifier(
1049                         (verifierContext, carPropertyConfig, propertyId, areaId, timestampNanos,
1050                                 value) -> {
1051                             boolean deadReckonedIsSet = (value
1052                                     & LocationCharacterization.DEAD_RECKONED)
1053                                     == LocationCharacterization.DEAD_RECKONED;
1054                             boolean rawGnssOnlyIsSet = (value
1055                                     & LocationCharacterization.RAW_GNSS_ONLY)
1056                                     == LocationCharacterization.RAW_GNSS_ONLY;
1057                             assertWithMessage("LOCATION_CHARACTERIZATION must not be 0 "
1058                                     + "Found value: " + value)
1059                                     .that(value)
1060                                     .isNotEqualTo(0);
1061                             assertWithMessage("LOCATION_CHARACTERIZATION must not have any bits "
1062                                     + "set outside of the bit flags defined in "
1063                                     + "LocationCharacterization. Found value: " + value)
1064                                     .that(value & LOCATION_CHARACTERIZATION_VALID_VALUES_MASK)
1065                                     .isEqualTo(value);
1066                             assertWithMessage("LOCATION_CHARACTERIZATION must have one of "
1067                                     + "DEAD_RECKONED or RAW_GNSS_ONLY set. They both cannot be set "
1068                                     + "either. Found value: " + value)
1069                                     .that(deadReckonedIsSet ^ rawGnssOnlyIsSet)
1070                                     .isTrue();
1071                         })
1072                 .setCarPropertyManager(carPropertyManager)
1073                 .addReadPermission(readPermission);
1074     }
1075 
1076     private static ImmutableSet<Integer> generateAllPossibleHvacFanDirections() {
1077         ImmutableSet.Builder<Integer> allPossibleFanDirectionsBuilder = ImmutableSet.builder();
1078         for (int i = 1; i <= SINGLE_HVAC_FAN_DIRECTIONS.size(); i++) {
1079             allPossibleFanDirectionsBuilder.addAll(Sets.combinations(SINGLE_HVAC_FAN_DIRECTIONS,
1080                     i).stream().map(hvacFanDirectionCombo -> {
1081                         Integer possibleHvacFanDirection = 0;
1082                         for (Integer hvacFanDirection : hvacFanDirectionCombo) {
1083                             possibleHvacFanDirection |= hvacFanDirection;
1084                         }
1085                         return possibleHvacFanDirection;
1086                     }).collect(Collectors.toList()));
1087         }
1088         return allPossibleFanDirectionsBuilder.build();
1089     }
1090 
1091 
1092     /**
1093      * Gets the verifier for {@link VehiclePropertyIds#INFO_MODEL_TRIM}.
1094      */
1095     public static VehiclePropertyVerifier.Builder<String> getInfoModelTrimVerifierBuilder() {
1096         return VehiclePropertyVerifier.newBuilder(
1097                         VehiclePropertyIds.INFO_MODEL_TRIM,
1098                         CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ,
1099                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL,
1100                         CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_STATIC,
1101                         String.class)
1102                 .addReadPermission(Car.PERMISSION_CAR_INFO);
1103     }
1104 
1105     public static VehiclePropertyVerifier.Builder<Integer[]>
1106             getInfoVehicleSizeClassVerifierBuilder() {
1107         return VehiclePropertyVerifier.newBuilder(
1108                         VehiclePropertyIds.INFO_VEHICLE_SIZE_CLASS,
1109                         CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ,
1110                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL,
1111                         CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_STATIC,
1112                         Integer[].class)
1113                 .setCarPropertyValueVerifier(
1114                         (verifierContext, carPropertyConfig, propertyId, areaId, timestampNanos,
1115                          sizeClasses) -> {
1116                             ArraySet<Integer> presentStandards = new ArraySet<>();
1117                             for (int sizeClass : sizeClasses) {
1118                                 assertWithMessage("Size class " + sizeClass + " doesn't exist in "
1119                                         + "possible values: " + VEHICLE_SIZE_CLASSES)
1120                                         .that(VEHICLE_SIZE_CLASSES.contains(sizeClass)).isTrue();
1121                                 int standard = sizeClass & 0xf00;
1122                                 assertWithMessage("Multiple values from the standard of size class "
1123                                         + sizeClass + " are in use.")
1124                                         .that(presentStandards.contains(standard)).isFalse();
1125                                 presentStandards.add(standard);
1126                             }
1127                         })
1128                 .addReadPermission(Car.PERMISSION_CAR_INFO);
1129     }
1130 
1131     /**
1132      * Gets the verifier for {@link VehiclePropertyIds#TURN_SIGNAL_LIGHT_STATE}.
1133      */
1134     public static VehiclePropertyVerifier.Builder<Integer>
1135             getTurnSignalLightStateVerifierBuilder() {
1136         ImmutableSet<Integer> combinedCarPropertyValues = ImmutableSet.<Integer>builder()
1137                 .addAll(TURN_SIGNAL_STATES)
1138                 .add(VehicleTurnSignal.STATE_LEFT | VehicleTurnSignal.STATE_RIGHT)
1139                 .build();
1140 
1141         return VehiclePropertyVerifier.newBuilder(
1142                         VehiclePropertyIds.TURN_SIGNAL_LIGHT_STATE,
1143                         CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ,
1144                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL,
1145                         CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE,
1146                         Integer.class)
1147                 .setAllPossibleEnumValues(combinedCarPropertyValues)
1148                 .addReadPermission(Car.PERMISSION_READ_EXTERIOR_LIGHTS)
1149                 .addReadPermission(Car.PERMISSION_CONTROL_EXTERIOR_LIGHTS);
1150     }
1151 
1152     /**
1153      * Gets the verifier for {@link VehiclePropertyIds#TURN_SIGNAL_SWITCH}.
1154      */
1155     public static VehiclePropertyVerifier.Builder<Integer> getTurnSignalSwitchVerifierBuilder() {
1156         return VehiclePropertyVerifier.newBuilder(
1157                         VehiclePropertyIds.TURN_SIGNAL_SWITCH,
1158                         CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ_WRITE,
1159                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL,
1160                         CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE,
1161                         Integer.class)
1162                 .setAllPossibleEnumValues(TURN_SIGNAL_STATES)
1163                 .addReadPermission(Car.PERMISSION_READ_EXTERIOR_LIGHTS)
1164                 .addReadPermission(Car.PERMISSION_CONTROL_EXTERIOR_LIGHTS)
1165                 .addWritePermission(Car.PERMISSION_CONTROL_EXTERIOR_LIGHTS);
1166     }
1167 
1168     public static VehiclePropertyVerifier.Builder<Float>
1169             getInstantaneousFuelEconomyVerifierBuilder() {
1170         return VehiclePropertyVerifier.newBuilder(
1171                         VehiclePropertyIds.INSTANTANEOUS_FUEL_ECONOMY,
1172                         CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ,
1173                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL,
1174                         CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_CONTINUOUS,
1175                         Float.class)
1176                 .addReadPermission(Car.PERMISSION_MILEAGE_3P);
1177     }
1178 
1179     public static VehiclePropertyVerifier.Builder<Float>
1180             getInstantaneousEvEfficiencyVerifierBuilder() {
1181         return VehiclePropertyVerifier.newBuilder(
1182                         VehiclePropertyIds.INSTANTANEOUS_EV_EFFICIENCY,
1183                         CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ,
1184                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL,
1185                         CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_CONTINUOUS,
1186                         Float.class)
1187                 .addReadPermission(Car.PERMISSION_MILEAGE_3P);
1188     }
1189 
1190     public static VehiclePropertyVerifier.Builder<Boolean> getVehicleHornEngagedVerifierBuilder() {
1191         return VehiclePropertyVerifier.newBuilder(
1192                         VehiclePropertyIds.VEHICLE_HORN_ENGAGED,
1193                         CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ_WRITE,
1194                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL,
1195                         CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE,
1196                         Boolean.class)
1197                 .addReadPermission(Car.PERMISSION_READ_CAR_HORN)
1198                 .addReadPermission(Car.PERMISSION_CONTROL_CAR_HORN)
1199                 .addWritePermission(Car.PERMISSION_CONTROL_CAR_HORN);
1200     }
1201 
1202     public static VehiclePropertyVerifier.Builder<Integer>
1203             getVehicleDrivingAutomationTargetLevelVerifierBuilder() {
1204         return VehiclePropertyVerifier.newBuilder(
1205                         VehiclePropertyIds.VEHICLE_DRIVING_AUTOMATION_TARGET_LEVEL,
1206                         CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ,
1207                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL,
1208                         CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE,
1209                         Integer.class)
1210                 .setAllPossibleEnumValues(VEHICLE_AUTONOMOUS_STATES)
1211                 .addReadPermission(Car.PERMISSION_CAR_DRIVING_STATE);
1212     }
1213 
1214     public static VehiclePropertyVerifier.Builder<Float>
1215             getAcceleratorPedalCompressionPercentageVerifierBuilder() {
1216         return VehiclePropertyVerifier.newBuilder(
1217                         VehiclePropertyIds.ACCELERATOR_PEDAL_COMPRESSION_PERCENTAGE,
1218                         CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ,
1219                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL,
1220                         CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_CONTINUOUS,
1221                         Float.class)
1222                 .addReadPermission(Car.PERMISSION_READ_CAR_PEDALS);
1223     }
1224 
1225     public static VehiclePropertyVerifier.Builder<Float>
1226             getBrakePedalCompressionPercentageVerifierBuilder() {
1227         return VehiclePropertyVerifier.newBuilder(
1228                         VehiclePropertyIds.BRAKE_PEDAL_COMPRESSION_PERCENTAGE,
1229                         CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ,
1230                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL,
1231                         CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_CONTINUOUS,
1232                         Float.class)
1233                 .addReadPermission(Car.PERMISSION_READ_CAR_PEDALS);
1234     }
1235 
1236     public static VehiclePropertyVerifier.Builder<Float>
1237             getBrakePadWearPercentageVerifierBuilder() {
1238         return VehiclePropertyVerifier.newBuilder(
1239                         VehiclePropertyIds.BRAKE_PAD_WEAR_PERCENTAGE,
1240                         CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ,
1241                         VehicleAreaType.VEHICLE_AREA_TYPE_WHEEL,
1242                         CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE,
1243                         Float.class)
1244                 .addReadPermission(Car.PERMISSION_READ_BRAKE_INFO);
1245     }
1246 
1247     public static VehiclePropertyVerifier.Builder<Boolean>
1248             getBrakeFluidLevelLowVerifierBuilder() {
1249         return VehiclePropertyVerifier.newBuilder(
1250                         VehiclePropertyIds.BRAKE_FLUID_LEVEL_LOW,
1251                         CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ,
1252                         VehicleAreaType.VEHICLE_AREA_TYPE_GLOBAL,
1253                         CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_ONCHANGE,
1254                         Boolean.class)
1255                 .addReadPermission(Car.PERMISSION_READ_BRAKE_INFO);
1256     }
1257 
1258     public static VehiclePropertyVerifier.Builder<Integer>
1259             getVehiclePassiveSuspensionHeightVerifierBuilder() {
1260         return VehiclePropertyVerifier.newBuilder(
1261                         VehiclePropertyIds.VEHICLE_PASSIVE_SUSPENSION_HEIGHT,
1262                         CarPropertyConfig.VEHICLE_PROPERTY_ACCESS_READ,
1263                         VehicleAreaType.VEHICLE_AREA_TYPE_WHEEL,
1264                         CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_CONTINUOUS,
1265                         Integer.class)
1266                 .requireMinMaxValues()
1267                 .requireZeroToBeContainedInMinMaxRanges()
1268                 .addReadPermission(Car.PERMISSION_CAR_DYNAMICS_STATE);
1269     }
1270 }
1271