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 #include "VehicleUtils.h"
18
19 #include <unordered_map>
20
21 namespace android {
22 namespace hardware {
23 namespace automotive {
24 namespace vehicle {
25
26 using ::aidl::android::hardware::automotive::vehicle::StatusCode;
27 using ::aidl::android::hardware::automotive::vehicle::toString;
28 using ::aidl::android::hardware::automotive::vehicle::VehicleArea;
29 using ::aidl::android::hardware::automotive::vehicle::VehicleAreaConfig;
30 using ::aidl::android::hardware::automotive::vehicle::VehicleAreaDoor;
31 using ::aidl::android::hardware::automotive::vehicle::VehicleAreaMirror;
32 using ::aidl::android::hardware::automotive::vehicle::VehicleAreaSeat;
33 using ::aidl::android::hardware::automotive::vehicle::VehicleAreaWheel;
34 using ::aidl::android::hardware::automotive::vehicle::VehicleAreaWindow;
35 using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig;
36 using ::aidl::android::hardware::automotive::vehicle::VehicleProperty;
37 using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyGroup;
38 using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType;
39 using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
40 using ::android::base::Error;
41 using ::android::base::Result;
42 using ::ndk::ScopedAStatus;
43
44 namespace {
45
46 class PropertyIdByNameSingleton {
47 public:
getInstance()48 static PropertyIdByNameSingleton& getInstance() {
49 static PropertyIdByNameSingleton instance;
50 return instance;
51 }
52
getPropertyId(const std::string & name) const53 Result<int32_t> getPropertyId(const std::string& name) const {
54 auto it = mPropertyIdByName.find(name);
55 if (it == mPropertyIdByName.end()) {
56 return Error();
57 }
58 return it->second;
59 }
60
61 PropertyIdByNameSingleton(PropertyIdByNameSingleton const&) = delete;
62 void operator=(PropertyIdByNameSingleton const&) = delete;
63
64 private:
65 std::unordered_map<std::string, int32_t> mPropertyIdByName;
66
PropertyIdByNameSingleton()67 PropertyIdByNameSingleton() {
68 constexpr auto values = ndk::internal::enum_values<VehicleProperty>;
69 for (unsigned int i = 0; i < values.size(); i++) {
70 mPropertyIdByName.emplace(toString(values[i]), toInt(values[i]));
71 }
72 }
73 };
74
75 class AreaByNameSingleton {
76 public:
getInstance()77 static AreaByNameSingleton& getInstance() {
78 static AreaByNameSingleton instance;
79 return instance;
80 }
81
getArea(const std::string & name,int32_t propId) const82 Result<int32_t> getArea(const std::string& name, int32_t propId) const {
83 VehicleArea areaType = getPropArea(propId);
84
85 auto mapIt = mAreaByNameByAreaType.find(areaType);
86 if (mapIt == mAreaByNameByAreaType.end()) {
87 return Error() << "Invalid area type for property ID: " << propIdToString(propId);
88 }
89
90 const auto& areaByName = mapIt->second;
91 auto it = areaByName.find(name);
92 if (it == areaByName.end()) {
93 return Error() << "Invalid area name for property " << propIdToString(propId) << ": "
94 << name;
95 }
96 return it->second;
97 }
98
99 AreaByNameSingleton(AreaByNameSingleton const&) = delete;
100 void operator=(AreaByNameSingleton const&) = delete;
101
102 private:
103 std::unordered_map<VehicleArea, std::unordered_map<std::string, int32_t>> mAreaByNameByAreaType;
104
AreaByNameSingleton()105 AreaByNameSingleton() {
106 populateMap(VehicleArea::WINDOW, ndk::internal::enum_values<VehicleAreaWindow>);
107 populateMap(VehicleArea::MIRROR, ndk::internal::enum_values<VehicleAreaMirror>);
108 populateMap(VehicleArea::SEAT, ndk::internal::enum_values<VehicleAreaSeat>);
109 populateMap(VehicleArea::DOOR, ndk::internal::enum_values<VehicleAreaDoor>);
110 populateMap(VehicleArea::WHEEL, ndk::internal::enum_values<VehicleAreaWheel>);
111 }
112
113 template <class T, std::size_t N>
populateMap(VehicleArea areaType,std::array<T,N> values)114 void populateMap(VehicleArea areaType, std::array<T, N> values) {
115 for (unsigned int i = 0; i < values.size(); i++) {
116 mAreaByNameByAreaType[areaType].emplace(toString(values[i]), toInt(values[i]));
117 }
118 }
119 };
120
121 } // namespace
122
checkPropValue(const VehiclePropValue & value,const VehiclePropConfig * config)123 Result<void> checkPropValue(const VehiclePropValue& value, const VehiclePropConfig* config) {
124 int32_t property = value.prop;
125 VehiclePropertyType type = getPropType(property);
126 switch (type) {
127 case VehiclePropertyType::BOOLEAN:
128 [[fallthrough]];
129 case VehiclePropertyType::INT32:
130 if (value.value.int32Values.size() != 1) {
131 return Error() << "expect 1 int32Values for INT32 type";
132 }
133 break;
134 case VehiclePropertyType::INT32_VEC:
135 if (value.value.int32Values.size() < 1) {
136 return Error() << "expect >=1 int32Values for INT32_VEC type";
137 }
138 break;
139 case VehiclePropertyType::INT64:
140 if (value.value.int64Values.size() != 1) {
141 return Error() << "expect 1 int64Values for INT64 type";
142 }
143 break;
144 case VehiclePropertyType::INT64_VEC:
145 if (value.value.int64Values.size() < 1) {
146 return Error() << "expect >=1 int64Values for INT64_VEC type";
147 }
148 break;
149 case VehiclePropertyType::FLOAT:
150 if (value.value.floatValues.size() != 1) {
151 return Error() << "expect 1 floatValues for FLOAT type";
152 }
153 break;
154 case VehiclePropertyType::FLOAT_VEC:
155 if (value.value.floatValues.size() < 1) {
156 return Error() << "expect >=1 floatValues for FLOAT_VEC type";
157 }
158 break;
159 case VehiclePropertyType::BYTES:
160 // We allow setting an empty bytes array.
161 break;
162 case VehiclePropertyType::STRING:
163 // We allow setting an empty string.
164 break;
165 case VehiclePropertyType::MIXED:
166 if (getPropGroup(property) == VehiclePropertyGroup::VENDOR) {
167 // We only checks vendor mixed properties.
168 return checkVendorMixedPropValue(value, config);
169 }
170 break;
171 default:
172 return Error() << "unknown property type: " << toInt(type);
173 }
174 return {};
175 }
176
checkVendorMixedPropValue(const VehiclePropValue & value,const VehiclePropConfig * config)177 Result<void> checkVendorMixedPropValue(const VehiclePropValue& value,
178 const VehiclePropConfig* config) {
179 auto configArray = config->configArray;
180 // configArray[0], 1 indicates the property has a String value, we allow the string value to
181 // be empty.
182
183 size_t int32Count = 0;
184 // configArray[1], 1 indicates the property has a Boolean value.
185 if (configArray[1] == 1) {
186 int32Count++;
187 }
188 // configArray[2], 1 indicates the property has an Integer value.
189 if (configArray[2] == 1) {
190 int32Count++;
191 }
192 // configArray[3], the number indicates the size of Integer[] in the property.
193 int32Count += static_cast<size_t>(configArray[3]);
194 size_t int32Size = value.value.int32Values.size();
195 if (int32Size != int32Count) {
196 return Error() << "invalid mixed property, got " << int32Size << " int32Values, expect "
197 << int32Count;
198 }
199
200 size_t int64Count = 0;
201 // configArray[4], 1 indicates the property has a Long value.
202 if (configArray[4] == 1) {
203 int64Count++;
204 }
205 // configArray[5], the number indicates the size of Long[] in the property.
206 int64Count += static_cast<size_t>(configArray[5]);
207 size_t int64Size = value.value.int64Values.size();
208 if (int64Size != int64Count) {
209 return Error() << "invalid mixed property, got " << int64Size << " int64Values, expect "
210 << int64Count;
211 }
212
213 size_t floatCount = 0;
214 // configArray[6], 1 indicates the property has a Float value.
215 if (configArray[6] == 1) {
216 floatCount++;
217 }
218 // configArray[7], the number indicates the size of Float[] in the property.
219 floatCount += static_cast<size_t>(configArray[7]);
220 size_t floatSize = value.value.floatValues.size();
221 if (floatSize != floatCount) {
222 return Error() << "invalid mixed property, got " << floatSize << " floatValues, expect "
223 << floatCount;
224 }
225
226 // configArray[8], the number indicates the size of byte[] in the property.
227 size_t byteSize = value.value.byteValues.size();
228 size_t byteCount = static_cast<size_t>(configArray[8]);
229 if (byteCount != 0 && byteSize != byteCount) {
230 return Error() << "invalid mixed property, got " << byteSize << " byteValues, expect "
231 << byteCount;
232 }
233 return {};
234 }
235
checkValueRange(const VehiclePropValue & value,const VehicleAreaConfig * areaConfig)236 Result<void> checkValueRange(const VehiclePropValue& value, const VehicleAreaConfig* areaConfig) {
237 if (areaConfig == nullptr) {
238 return {};
239 }
240 int32_t property = value.prop;
241 VehiclePropertyType type = getPropType(property);
242 switch (type) {
243 case VehiclePropertyType::INT32:
244 [[fallthrough]];
245 case VehiclePropertyType::INT32_VEC:
246 if (areaConfig->minInt32Value == 0 && areaConfig->maxInt32Value == 0) {
247 break;
248 }
249 for (int32_t int32Value : value.value.int32Values) {
250 if (int32Value < areaConfig->minInt32Value ||
251 int32Value > areaConfig->maxInt32Value) {
252 return Error() << "int32Value: " << int32Value
253 << " out of range, min: " << areaConfig->minInt32Value
254 << " max: " << areaConfig->maxInt32Value;
255 }
256 }
257 break;
258 case VehiclePropertyType::INT64:
259 [[fallthrough]];
260 case VehiclePropertyType::INT64_VEC:
261 if (areaConfig->minInt64Value == 0 && areaConfig->maxInt64Value == 0) {
262 break;
263 }
264 for (int64_t int64Value : value.value.int64Values) {
265 if (int64Value < areaConfig->minInt64Value ||
266 int64Value > areaConfig->maxInt64Value) {
267 return Error() << "int64Value: " << int64Value
268 << " out of range, min: " << areaConfig->minInt64Value
269 << " max: " << areaConfig->maxInt64Value;
270 }
271 }
272 break;
273 case VehiclePropertyType::FLOAT:
274 [[fallthrough]];
275 case VehiclePropertyType::FLOAT_VEC:
276 if (areaConfig->minFloatValue == 0.f && areaConfig->maxFloatValue == 0.f) {
277 break;
278 }
279 for (float floatValue : value.value.floatValues) {
280 if (floatValue < areaConfig->minFloatValue ||
281 floatValue > areaConfig->maxFloatValue) {
282 return Error() << "floatValue: " << floatValue
283 << " out of range, min: " << areaConfig->minFloatValue
284 << " max: " << areaConfig->maxFloatValue;
285 }
286 }
287 break;
288 default:
289 // We don't check the rest of property types. Additional logic needs to be added if
290 // required in VehicleHardware, e.g. you might want to check the range for mixed
291 // property.
292 break;
293 }
294 return {};
295 }
296
value() const297 StatusCode VhalError::value() const {
298 return mCode;
299 }
300
print() const301 std::string VhalError::print() const {
302 return aidl::android::hardware::automotive::vehicle::toString(mCode);
303 }
304
stringToPropId(const std::string & propName)305 Result<int32_t> stringToPropId(const std::string& propName) {
306 return PropertyIdByNameSingleton::getInstance().getPropertyId(propName);
307 }
308
stringToArea(const std::string & areaName,int32_t propId)309 Result<int32_t> stringToArea(const std::string& areaName, int32_t propId) {
310 return AreaByNameSingleton::getInstance().getArea(areaName, propId);
311 }
312
313 } // namespace vehicle
314 } // namespace automotive
315 } // namespace hardware
316 } // namespace android
317