• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 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.internal.property;
18 
19 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.PRIVATE_CONSTRUCTOR;
20 import static com.android.car.internal.util.ArrayUtils.convertToIntArray;
21 import static com.android.car.internal.util.DebugUtils.toAreaIdString;
22 
23 import android.car.VehiclePropertyIds;
24 import android.car.feature.FeatureFlags;
25 import android.car.hardware.CarPropertyConfig;
26 import android.car.hardware.property.CarPropertyManager;
27 import android.util.Log;
28 import android.util.Slog;
29 
30 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport;
31 import com.android.internal.annotations.VisibleForTesting;
32 import com.android.internal.util.Preconditions;
33 
34 import java.util.ArrayList;
35 import java.util.List;
36 
37 /**
38  * Common utility functions used by {@link CarPropertyManager} stack to sanitize input arguments.
39  */
40 public final class InputSanitizationUtils {
41 
42     private static final String TAG = InputSanitizationUtils.class.getSimpleName();
43     private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
44 
45     @ExcludeFromCodeCoverageGeneratedReport(reason = PRIVATE_CONSTRUCTOR)
InputSanitizationUtils()46     private InputSanitizationUtils() {
47     }
48 
49     /**
50      * Sanitizes the {@code updateRateHz} passed to {@link
51      * CarPropertyManager#registerCallback(CarPropertyManager.CarPropertyEventCallback, int, float)}
52      * and similar functions.
53      */
sanitizeUpdateRateHz( CarPropertyConfig<?> carPropertyConfig, float updateRateHz)54     public static float sanitizeUpdateRateHz(
55             CarPropertyConfig<?> carPropertyConfig, float updateRateHz) {
56         float sanitizedUpdateRateHz = updateRateHz;
57         if (carPropertyConfig.getChangeMode()
58                 != CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_CONTINUOUS) {
59             sanitizedUpdateRateHz = CarPropertyManager.SENSOR_RATE_ONCHANGE;
60         } else if (sanitizedUpdateRateHz > carPropertyConfig.getMaxSampleRate()) {
61             sanitizedUpdateRateHz = carPropertyConfig.getMaxSampleRate();
62         } else if (sanitizedUpdateRateHz < carPropertyConfig.getMinSampleRate()) {
63             sanitizedUpdateRateHz = carPropertyConfig.getMinSampleRate();
64         }
65         return sanitizedUpdateRateHz;
66     }
67 
68     /**
69      * Sets resolution to 0 if the feature flag for resolution is not enabled. Also calls
70      * {@link #requireIntegerPowerOf10Resolution(float)} to determine if the incoming resolution
71      * value is an integer power of 10.
72      */
sanitizeResolution(FeatureFlags featureFlags, CarPropertyConfig<?> carPropertyConfig, float resolution)73     public static float sanitizeResolution(FeatureFlags featureFlags,
74             CarPropertyConfig<?> carPropertyConfig, float resolution) {
75         if (!featureFlags.subscriptionWithResolution() || carPropertyConfig.getChangeMode()
76                 != CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_CONTINUOUS) {
77             return 0.0f;
78         }
79         requireIntegerPowerOf10Resolution(resolution);
80         return resolution;
81     }
82 
83     /**
84      * Verifies that the incoming resolution value takes on only integer power of 10 values. Also
85      * sets resolution to 0 if the feature flag for resolution is not enabled.
86      */
requireIntegerPowerOf10Resolution(float resolution)87     public static void requireIntegerPowerOf10Resolution(float resolution) {
88         if (resolution == 0.0f) {
89             return;
90         }
91         double log = Math.log10(resolution);
92         Preconditions.checkArgument(Math.abs(log - Math.round(log)) < 0.0000001f,
93                 "resolution must be an integer power of 10. Instead, got resolution: " + resolution
94                         + ", whose log10 value is: " + log);
95     }
96 
97     /**
98      * Returns whether VUR feature is enabled and property is continuous.
99      *
100      * Need to be public even though InputSanitizationUtilsUnitTest is in the same package because
101      * in CarServiceUnitTest, it is loaded using a different class loader.
102      */
103     @VisibleForTesting
104     public static boolean isVurAllowed(FeatureFlags featureFlags,
105             CarPropertyConfig<?> carPropertyConfig) {
106         if (!featureFlags.variableUpdateRate()) {
107             if (DBG) {
108                 Slog.d(TAG, "VUR feature is not enabled, VUR is always off");
109             }
110             return false;
111         }
112         if (carPropertyConfig.getChangeMode()
113                 != CarPropertyConfig.VEHICLE_PROPERTY_CHANGE_MODE_CONTINUOUS) {
114             if (DBG) {
115                 Slog.d(TAG, "VUR is always off for non-continuous property");
116             }
117             return false;
118         }
119         return true;
120     }
121 
122     /**
123      * Sanitizes the enableVur option in {@code CarSubscription}.
124      *
125      * <p>Overwrite it to false if:
126      *
127      * <ul>
128      * <li>Vur feature is disabled.</li>
129      * <li>Property is not continuous.</li>
130      * <li>Vur is not supported for the specific area.</li>
131      * </ul>
132      *
133      * @return a list of sanitized subscribe options. We may return more than one option because
134      * part of the areaIds support Vur, the rest does not.
135      */
136     public static List<CarSubscription> sanitizeEnableVariableUpdateRate(
137             FeatureFlags featureFlags, CarPropertyConfig<?> carPropertyConfig,
138             CarSubscription inputOption) throws IllegalArgumentException {
139         int[] areaIds = inputOption.areaIds;
140         Preconditions.checkArgument(areaIds != null && areaIds.length != 0,
141                 "areaIds must not be empty for property: "
142                 + VehiclePropertyIds.toString(inputOption.propertyId));
143         List<CarSubscription> sanitizedOptions = new ArrayList<>();
144         if (!inputOption.enableVariableUpdateRate) {
145             // We will only overwrite enableVur to off.
146             sanitizedOptions.add(inputOption);
147             return sanitizedOptions;
148         }
149         // If VUR feature is disabled, overwrite the VUR option to false.
150         if (!isVurAllowed(featureFlags, carPropertyConfig)) {
151             inputOption.enableVariableUpdateRate = false;
152             sanitizedOptions.add(inputOption);
153             return sanitizedOptions;
154         }
155         List<Integer> enabledAreaIds = new ArrayList<>();
156         List<Integer> disabledAreaIds = new ArrayList<>();
157         for (int areaId : areaIds) {
158             try {
159                 if (carPropertyConfig.getAreaIdConfig(areaId)
160                         .isVariableUpdateRateSupported()) {
161                     enabledAreaIds.add(areaId);
162                     continue;
163                 }
164             } catch (IllegalArgumentException e) {
165                 // Do nothing.
166             }
167             if (DBG) {
168                 Slog.d(TAG, "VUR is enabled but not supported for areaId: " + toAreaIdString(
169                         inputOption.propertyId, areaId));
170             }
171             disabledAreaIds.add(areaId);
172         }
173         CarSubscription disabledVurOption = new CarSubscription();
174         disabledVurOption.propertyId = inputOption.propertyId;
175         disabledVurOption.areaIds = convertToIntArray(disabledAreaIds);
176         disabledVurOption.updateRateHz = inputOption.updateRateHz;
177         disabledVurOption.enableVariableUpdateRate = false;
178         disabledVurOption.resolution = inputOption.resolution;
179 
180         CarSubscription enabledVurOption = new CarSubscription();
181         enabledVurOption.propertyId = inputOption.propertyId;
182         enabledVurOption.areaIds = convertToIntArray(enabledAreaIds);
183         enabledVurOption.updateRateHz = inputOption.updateRateHz;
184         enabledVurOption.enableVariableUpdateRate = true;
185         enabledVurOption.resolution = inputOption.resolution;
186 
187         sanitizedOptions.add(enabledVurOption);
188         sanitizedOptions.add(disabledVurOption);
189         return sanitizedOptions;
190     }
191 }
192