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::VehicleAreaConfig;
29 using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig;
30 using ::aidl::android::hardware::automotive::vehicle::VehicleProperty;
31 using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyGroup;
32 using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType;
33 using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
34 using ::android::base::Error;
35 using ::android::base::Result;
36 using ::ndk::ScopedAStatus;
37
38 namespace {
39
40 class PropertyIdByNameSingleton {
41 public:
getInstance()42 static PropertyIdByNameSingleton& getInstance() {
43 static PropertyIdByNameSingleton instance;
44 return instance;
45 }
46
getPropertyId(const std::string & name)47 Result<int32_t> getPropertyId(const std::string& name) {
48 auto it = mPropertyIdByName.find(name);
49 if (it == mPropertyIdByName.end()) {
50 return Error();
51 }
52 return it->second;
53 }
54
55 PropertyIdByNameSingleton(PropertyIdByNameSingleton const&) = delete;
56 void operator=(PropertyIdByNameSingleton const&) = delete;
57
58 private:
59 std::unordered_map<std::string, int32_t> mPropertyIdByName;
60
PropertyIdByNameSingleton()61 PropertyIdByNameSingleton() {
62 constexpr auto values = ndk::internal::enum_values<VehicleProperty>;
63 for (unsigned int i = 0; i < values.size(); i++) {
64 mPropertyIdByName.emplace(toString(values[i]), toInt(values[i]));
65 }
66 }
67 };
68
69 } // namespace
70
checkPropValue(const VehiclePropValue & value,const VehiclePropConfig * config)71 Result<void> checkPropValue(const VehiclePropValue& value, const VehiclePropConfig* config) {
72 int32_t property = value.prop;
73 VehiclePropertyType type = getPropType(property);
74 switch (type) {
75 case VehiclePropertyType::BOOLEAN:
76 [[fallthrough]];
77 case VehiclePropertyType::INT32:
78 if (value.value.int32Values.size() != 1) {
79 return Error() << "expect 1 int32Values for INT32 type";
80 }
81 break;
82 case VehiclePropertyType::INT32_VEC:
83 if (value.value.int32Values.size() < 1) {
84 return Error() << "expect >=1 int32Values for INT32_VEC type";
85 }
86 break;
87 case VehiclePropertyType::INT64:
88 if (value.value.int64Values.size() != 1) {
89 return Error() << "expect 1 int64Values for INT64 type";
90 }
91 break;
92 case VehiclePropertyType::INT64_VEC:
93 if (value.value.int64Values.size() < 1) {
94 return Error() << "expect >=1 int64Values for INT64_VEC type";
95 }
96 break;
97 case VehiclePropertyType::FLOAT:
98 if (value.value.floatValues.size() != 1) {
99 return Error() << "expect 1 floatValues for FLOAT type";
100 }
101 break;
102 case VehiclePropertyType::FLOAT_VEC:
103 if (value.value.floatValues.size() < 1) {
104 return Error() << "expect >=1 floatValues for FLOAT_VEC type";
105 }
106 break;
107 case VehiclePropertyType::BYTES:
108 // We allow setting an empty bytes array.
109 break;
110 case VehiclePropertyType::STRING:
111 // We allow setting an empty string.
112 break;
113 case VehiclePropertyType::MIXED:
114 if (getPropGroup(property) == VehiclePropertyGroup::VENDOR) {
115 // We only checks vendor mixed properties.
116 return checkVendorMixedPropValue(value, config);
117 }
118 break;
119 default:
120 return Error() << "unknown property type: " << toInt(type);
121 }
122 return {};
123 }
124
checkVendorMixedPropValue(const VehiclePropValue & value,const VehiclePropConfig * config)125 Result<void> checkVendorMixedPropValue(const VehiclePropValue& value,
126 const VehiclePropConfig* config) {
127 auto configArray = config->configArray;
128 // configArray[0], 1 indicates the property has a String value, we allow the string value to
129 // be empty.
130
131 size_t int32Count = 0;
132 // configArray[1], 1 indicates the property has a Boolean value.
133 if (configArray[1] == 1) {
134 int32Count++;
135 }
136 // configArray[2], 1 indicates the property has an Integer value.
137 if (configArray[2] == 1) {
138 int32Count++;
139 }
140 // configArray[3], the number indicates the size of Integer[] in the property.
141 int32Count += static_cast<size_t>(configArray[3]);
142 size_t int32Size = value.value.int32Values.size();
143 if (int32Size != int32Count) {
144 return Error() << "invalid mixed property, got " << int32Size << " int32Values, expect "
145 << int32Count;
146 }
147
148 size_t int64Count = 0;
149 // configArray[4], 1 indicates the property has a Long value.
150 if (configArray[4] == 1) {
151 int64Count++;
152 }
153 // configArray[5], the number indicates the size of Long[] in the property.
154 int64Count += static_cast<size_t>(configArray[5]);
155 size_t int64Size = value.value.int64Values.size();
156 if (int64Size != int64Count) {
157 return Error() << "invalid mixed property, got " << int64Size << " int64Values, expect "
158 << int64Count;
159 }
160
161 size_t floatCount = 0;
162 // configArray[6], 1 indicates the property has a Float value.
163 if (configArray[6] == 1) {
164 floatCount++;
165 }
166 // configArray[7], the number indicates the size of Float[] in the property.
167 floatCount += static_cast<size_t>(configArray[7]);
168 size_t floatSize = value.value.floatValues.size();
169 if (floatSize != floatCount) {
170 return Error() << "invalid mixed property, got " << floatSize << " floatValues, expect "
171 << floatCount;
172 }
173
174 // configArray[8], the number indicates the size of byte[] in the property.
175 size_t byteSize = value.value.byteValues.size();
176 size_t byteCount = static_cast<size_t>(configArray[8]);
177 if (byteCount != 0 && byteSize != byteCount) {
178 return Error() << "invalid mixed property, got " << byteSize << " byteValues, expect "
179 << byteCount;
180 }
181 return {};
182 }
183
checkValueRange(const VehiclePropValue & value,const VehicleAreaConfig * areaConfig)184 Result<void> checkValueRange(const VehiclePropValue& value, const VehicleAreaConfig* areaConfig) {
185 if (areaConfig == nullptr) {
186 return {};
187 }
188 int32_t property = value.prop;
189 VehiclePropertyType type = getPropType(property);
190 switch (type) {
191 case VehiclePropertyType::INT32:
192 [[fallthrough]];
193 case VehiclePropertyType::INT32_VEC:
194 if (areaConfig->minInt32Value == 0 && areaConfig->maxInt32Value == 0) {
195 break;
196 }
197 for (int32_t int32Value : value.value.int32Values) {
198 if (int32Value < areaConfig->minInt32Value ||
199 int32Value > areaConfig->maxInt32Value) {
200 return Error() << "int32Value: " << int32Value
201 << " out of range, min: " << areaConfig->minInt32Value
202 << " max: " << areaConfig->maxInt32Value;
203 }
204 }
205 break;
206 case VehiclePropertyType::INT64:
207 [[fallthrough]];
208 case VehiclePropertyType::INT64_VEC:
209 if (areaConfig->minInt64Value == 0 && areaConfig->maxInt64Value == 0) {
210 break;
211 }
212 for (int64_t int64Value : value.value.int64Values) {
213 if (int64Value < areaConfig->minInt64Value ||
214 int64Value > areaConfig->maxInt64Value) {
215 return Error() << "int64Value: " << int64Value
216 << " out of range, min: " << areaConfig->minInt64Value
217 << " max: " << areaConfig->maxInt64Value;
218 }
219 }
220 break;
221 case VehiclePropertyType::FLOAT:
222 [[fallthrough]];
223 case VehiclePropertyType::FLOAT_VEC:
224 if (areaConfig->minFloatValue == 0.f && areaConfig->maxFloatValue == 0.f) {
225 break;
226 }
227 for (float floatValue : value.value.floatValues) {
228 if (floatValue < areaConfig->minFloatValue ||
229 floatValue > areaConfig->maxFloatValue) {
230 return Error() << "floatValue: " << floatValue
231 << " out of range, min: " << areaConfig->minFloatValue
232 << " max: " << areaConfig->maxFloatValue;
233 }
234 }
235 break;
236 default:
237 // We don't check the rest of property types. Additional logic needs to be added if
238 // required in VehicleHardware, e.g. you might want to check the range for mixed
239 // property.
240 break;
241 }
242 return {};
243 }
244
value() const245 StatusCode VhalError::value() const {
246 return mCode;
247 }
248
print() const249 std::string VhalError::print() const {
250 return aidl::android::hardware::automotive::vehicle::toString(mCode);
251 }
252
stringToPropId(const std::string & propName)253 Result<int32_t> stringToPropId(const std::string& propName) {
254 return PropertyIdByNameSingleton::getInstance().getPropertyId(propName);
255 }
256
257 } // namespace vehicle
258 } // namespace automotive
259 } // namespace hardware
260 } // namespace android
261