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 #define LOG_TAG "VehiclePropertyStore"
18 #include <utils/Log.h>
19 #include <utils/SystemClock.h>
20
21 #include "VehiclePropertyStore.h"
22
23 #include <VehicleHalTypes.h>
24 #include <VehicleUtils.h>
25 #include <android-base/stringprintf.h>
26 #include <math/HashCombine.h>
27
28 #include <inttypes.h>
29
30 namespace android {
31 namespace hardware {
32 namespace automotive {
33 namespace vehicle {
34
35 using ::aidl::android::hardware::automotive::vehicle::StatusCode;
36 using ::aidl::android::hardware::automotive::vehicle::VehicleAreaConfig;
37 using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig;
38 using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyStatus;
39 using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
40 using ::android::base::Result;
41 using ::android::base::StringPrintf;
42
operator ==(const VehiclePropertyStore::RecordId & other) const43 bool VehiclePropertyStore::RecordId::operator==(const VehiclePropertyStore::RecordId& other) const {
44 return area == other.area && token == other.token;
45 }
46
toString() const47 std::string VehiclePropertyStore::RecordId::toString() const {
48 return StringPrintf("RecordID{{.areaId=% " PRId32 ", .token=%" PRId64 "}", area, token);
49 }
50
operator ()(RecordId const & recordId) const51 size_t VehiclePropertyStore::RecordIdHash::operator()(RecordId const& recordId) const {
52 size_t res = 0;
53 hashCombine(res, recordId.area);
54 hashCombine(res, recordId.token);
55 return res;
56 }
57
~VehiclePropertyStore()58 VehiclePropertyStore::~VehiclePropertyStore() {
59 std::scoped_lock<std::mutex> lockGuard(mLock);
60
61 // Recycling record requires mValuePool, so need to recycle them before destroying mValuePool.
62 mRecordsByPropId.clear();
63 mValuePool.reset();
64 }
65
getRecordLocked(int32_t propId) const66 const VehiclePropertyStore::Record* VehiclePropertyStore::getRecordLocked(int32_t propId) const
67 REQUIRES(mLock) {
68 auto RecordIt = mRecordsByPropId.find(propId);
69 return RecordIt == mRecordsByPropId.end() ? nullptr : &RecordIt->second;
70 }
71
getRecordLocked(int32_t propId)72 VehiclePropertyStore::Record* VehiclePropertyStore::getRecordLocked(int32_t propId)
73 REQUIRES(mLock) {
74 auto RecordIt = mRecordsByPropId.find(propId);
75 return RecordIt == mRecordsByPropId.end() ? nullptr : &RecordIt->second;
76 }
77
getRecordIdLocked(const VehiclePropValue & propValue,const VehiclePropertyStore::Record & record) const78 VehiclePropertyStore::RecordId VehiclePropertyStore::getRecordIdLocked(
79 const VehiclePropValue& propValue, const VehiclePropertyStore::Record& record) const
80 REQUIRES(mLock) {
81 VehiclePropertyStore::RecordId recId{
82 .area = isGlobalProp(propValue.prop) ? 0 : propValue.areaId, .token = 0};
83
84 if (record.tokenFunction != nullptr) {
85 recId.token = record.tokenFunction(propValue);
86 }
87 return recId;
88 }
89
readValueLocked(const RecordId & recId,const Record & record) const90 VhalResult<VehiclePropValuePool::RecyclableType> VehiclePropertyStore::readValueLocked(
91 const RecordId& recId, const Record& record) const REQUIRES(mLock) {
92 if (auto it = record.values.find(recId); it != record.values.end()) {
93 return mValuePool->obtain(*(it->second));
94 }
95 return StatusError(StatusCode::NOT_AVAILABLE)
96 << "Record ID: " << recId.toString() << " is not found";
97 }
98
registerProperty(const VehiclePropConfig & config,VehiclePropertyStore::TokenFunction tokenFunc)99 void VehiclePropertyStore::registerProperty(const VehiclePropConfig& config,
100 VehiclePropertyStore::TokenFunction tokenFunc) {
101 std::scoped_lock<std::mutex> g(mLock);
102
103 mRecordsByPropId[config.prop] = Record{
104 .propConfig = config,
105 .tokenFunction = tokenFunc,
106 };
107 }
108
writeValue(VehiclePropValuePool::RecyclableType propValue,bool updateStatus,VehiclePropertyStore::EventMode eventMode,bool useCurrentTimestamp)109 VhalResult<void> VehiclePropertyStore::writeValue(VehiclePropValuePool::RecyclableType propValue,
110 bool updateStatus,
111 VehiclePropertyStore::EventMode eventMode,
112 bool useCurrentTimestamp) {
113 bool valueUpdated = true;
114 VehiclePropValue updatedValue;
115 OnValueChangeCallback onValueChangeCallback = nullptr;
116 OnValuesChangeCallback onValuesChangeCallback = nullptr;
117 int32_t propId;
118 int32_t areaId;
119 {
120 std::scoped_lock<std::mutex> g(mLock);
121
122 // Must set timestamp inside the lock to make sure no other writeValue will update the
123 // the timestamp to a newer one while we are writing this value.
124 if (useCurrentTimestamp) {
125 propValue->timestamp = elapsedRealtimeNano();
126 }
127
128 propId = propValue->prop;
129 areaId = propValue->areaId;
130
131 VehiclePropertyStore::Record* record = getRecordLocked(propId);
132 if (record == nullptr) {
133 return StatusError(StatusCode::INVALID_ARG)
134 << "property: " << propId << " not registered";
135 }
136
137 if (!isGlobalProp(propId) && getAreaConfig(*propValue, record->propConfig) == nullptr) {
138 return StatusError(StatusCode::INVALID_ARG)
139 << "no config for property: " << propId << " area ID: " << propValue->areaId;
140 }
141
142 VehiclePropertyStore::RecordId recId = getRecordIdLocked(*propValue, *record);
143 if (auto it = record->values.find(recId); it != record->values.end()) {
144 const VehiclePropValue* valueToUpdate = it->second.get();
145 int64_t oldTimestampNanos = valueToUpdate->timestamp;
146 VehiclePropertyStatus oldStatus = valueToUpdate->status;
147 // propValue is outdated and drops it.
148 if (oldTimestampNanos > propValue->timestamp) {
149 return StatusError(StatusCode::INVALID_ARG)
150 << "outdated timestampNanos: " << propValue->timestamp;
151 }
152 if (!updateStatus) {
153 propValue->status = oldStatus;
154 }
155
156 // areaId and propId must be the same between valueToUpdate and propValue.
157 valueUpdated = (valueToUpdate->value != propValue->value ||
158 valueToUpdate->status != propValue->status);
159 } else if (!updateStatus) {
160 propValue->status = VehiclePropertyStatus::AVAILABLE;
161 }
162
163 record->values[recId] = std::move(propValue);
164
165 if (eventMode == EventMode::NEVER) {
166 return {};
167 }
168 updatedValue = *(record->values[recId]);
169
170 onValuesChangeCallback = mOnValuesChangeCallback;
171 onValueChangeCallback = mOnValueChangeCallback;
172 }
173
174 if (onValuesChangeCallback == nullptr && onValueChangeCallback == nullptr) {
175 // No callback registered.
176 return {};
177 }
178
179 // Invoke the callback outside the lock to prevent dead-lock.
180 if (eventMode == EventMode::ALWAYS || valueUpdated) {
181 if (onValuesChangeCallback != nullptr) {
182 onValuesChangeCallback({updatedValue});
183 } else {
184 onValueChangeCallback(updatedValue);
185 }
186 }
187 return {};
188 }
189
refreshTimestamp(int32_t propId,int32_t areaId,EventMode eventMode)190 void VehiclePropertyStore::refreshTimestamp(int32_t propId, int32_t areaId, EventMode eventMode) {
191 std::unordered_map<PropIdAreaId, EventMode, PropIdAreaIdHash> eventModeByPropIdAreaId;
192 PropIdAreaId propIdAreaId = {
193 .propId = propId,
194 .areaId = areaId,
195 };
196 eventModeByPropIdAreaId[propIdAreaId] = eventMode;
197 refreshTimestamps(eventModeByPropIdAreaId);
198 }
199
refreshTimestamps(std::unordered_map<PropIdAreaId,EventMode,PropIdAreaIdHash> eventModeByPropIdAreaId)200 void VehiclePropertyStore::refreshTimestamps(
201 std::unordered_map<PropIdAreaId, EventMode, PropIdAreaIdHash> eventModeByPropIdAreaId) {
202 std::vector<VehiclePropValue> updatedValues;
203 OnValuesChangeCallback onValuesChangeCallback = nullptr;
204 OnValueChangeCallback onValueChangeCallback = nullptr;
205 {
206 std::scoped_lock<std::mutex> g(mLock);
207
208 onValuesChangeCallback = mOnValuesChangeCallback;
209 onValueChangeCallback = mOnValueChangeCallback;
210
211 for (const auto& [propIdAreaId, eventMode] : eventModeByPropIdAreaId) {
212 int32_t propId = propIdAreaId.propId;
213 int32_t areaId = propIdAreaId.areaId;
214 VehiclePropertyStore::Record* record = getRecordLocked(propId);
215 if (record == nullptr) {
216 continue;
217 }
218
219 VehiclePropValue propValue = {
220 .areaId = areaId,
221 .prop = propId,
222 .value = {},
223 };
224
225 VehiclePropertyStore::RecordId recId = getRecordIdLocked(propValue, *record);
226 if (auto it = record->values.find(recId); it != record->values.end()) {
227 it->second->timestamp = elapsedRealtimeNano();
228 if (eventMode == EventMode::ALWAYS) {
229 updatedValues.push_back(*(it->second));
230 }
231 } else {
232 continue;
233 }
234 }
235 }
236
237 // Invoke the callback outside the lock to prevent dead-lock.
238 if (updatedValues.empty()) {
239 return;
240 }
241 if (!onValuesChangeCallback && !onValueChangeCallback) {
242 // If no callback is set, then we don't have to do anything.
243 for (const auto& updateValue : updatedValues) {
244 ALOGW("No callback registered, ignoring property update for propId: %" PRId32
245 ", area ID: %" PRId32,
246 updateValue.prop, updateValue.areaId);
247 }
248 return;
249 }
250 if (onValuesChangeCallback != nullptr) {
251 onValuesChangeCallback(updatedValues);
252 } else {
253 // Fallback to use multiple onValueChangeCallback
254 for (const auto& updateValue : updatedValues) {
255 onValueChangeCallback(updateValue);
256 }
257 }
258 }
259
removeValue(const VehiclePropValue & propValue)260 void VehiclePropertyStore::removeValue(const VehiclePropValue& propValue) {
261 std::scoped_lock<std::mutex> g(mLock);
262
263 VehiclePropertyStore::Record* record = getRecordLocked(propValue.prop);
264 if (record == nullptr) {
265 return;
266 }
267
268 VehiclePropertyStore::RecordId recId = getRecordIdLocked(propValue, *record);
269 if (auto it = record->values.find(recId); it != record->values.end()) {
270 record->values.erase(it);
271 }
272 }
273
removeValuesForProperty(int32_t propId)274 void VehiclePropertyStore::removeValuesForProperty(int32_t propId) {
275 std::scoped_lock<std::mutex> g(mLock);
276
277 VehiclePropertyStore::Record* record = getRecordLocked(propId);
278 if (record == nullptr) {
279 return;
280 }
281
282 record->values.clear();
283 }
284
readAllValues() const285 std::vector<VehiclePropValuePool::RecyclableType> VehiclePropertyStore::readAllValues() const {
286 std::scoped_lock<std::mutex> g(mLock);
287
288 std::vector<VehiclePropValuePool::RecyclableType> allValues;
289
290 for (auto const& [_, record] : mRecordsByPropId) {
291 for (auto const& [_, value] : record.values) {
292 allValues.push_back(std::move(mValuePool->obtain(*value)));
293 }
294 }
295
296 return allValues;
297 }
298
readValuesForProperty(int32_t propId) const299 VehiclePropertyStore::ValuesResultType VehiclePropertyStore::readValuesForProperty(
300 int32_t propId) const {
301 std::scoped_lock<std::mutex> g(mLock);
302
303 std::vector<VehiclePropValuePool::RecyclableType> values;
304
305 const VehiclePropertyStore::Record* record = getRecordLocked(propId);
306 if (record == nullptr) {
307 return StatusError(StatusCode::INVALID_ARG) << "property: " << propId << " not registered";
308 }
309
310 for (auto const& [_, value] : record->values) {
311 values.push_back(std::move(mValuePool->obtain(*value)));
312 }
313 return values;
314 }
315
readValue(const VehiclePropValue & propValue) const316 VehiclePropertyStore::ValueResultType VehiclePropertyStore::readValue(
317 const VehiclePropValue& propValue) const {
318 std::scoped_lock<std::mutex> g(mLock);
319
320 int32_t propId = propValue.prop;
321 const VehiclePropertyStore::Record* record = getRecordLocked(propId);
322 if (record == nullptr) {
323 return StatusError(StatusCode::INVALID_ARG) << "property: " << propId << " not registered";
324 }
325
326 VehiclePropertyStore::RecordId recId = getRecordIdLocked(propValue, *record);
327 return readValueLocked(recId, *record);
328 }
329
readValue(int32_t propId,int32_t areaId,int64_t token) const330 VehiclePropertyStore::ValueResultType VehiclePropertyStore::readValue(int32_t propId,
331 int32_t areaId,
332 int64_t token) const {
333 std::scoped_lock<std::mutex> g(mLock);
334
335 const VehiclePropertyStore::Record* record = getRecordLocked(propId);
336 if (record == nullptr) {
337 return StatusError(StatusCode::INVALID_ARG) << "property: " << propId << " not registered";
338 }
339
340 VehiclePropertyStore::RecordId recId{.area = isGlobalProp(propId) ? 0 : areaId, .token = token};
341 return readValueLocked(recId, *record);
342 }
343
getAllConfigs() const344 std::vector<VehiclePropConfig> VehiclePropertyStore::getAllConfigs() const {
345 std::scoped_lock<std::mutex> g(mLock);
346
347 std::vector<VehiclePropConfig> configs;
348 configs.reserve(mRecordsByPropId.size());
349 for (auto& [_, config] : mRecordsByPropId) {
350 configs.push_back(config.propConfig);
351 }
352 return configs;
353 }
354
getConfig(int32_t propId) const355 VhalResult<const VehiclePropConfig*> VehiclePropertyStore::getConfig(int32_t propId) const {
356 std::scoped_lock<std::mutex> g(mLock);
357
358 const VehiclePropertyStore::Record* record = getRecordLocked(propId);
359 if (record == nullptr) {
360 return StatusError(StatusCode::INVALID_ARG) << "property: " << propId << " not registered";
361 }
362
363 return &record->propConfig;
364 }
365
getPropConfig(int32_t propId) const366 VhalResult<VehiclePropConfig> VehiclePropertyStore::getPropConfig(int32_t propId) const {
367 std::scoped_lock<std::mutex> g(mLock);
368
369 const VehiclePropertyStore::Record* record = getRecordLocked(propId);
370 if (record == nullptr) {
371 return StatusError(StatusCode::INVALID_ARG) << "property: " << propId << " not registered";
372 }
373
374 return record->propConfig;
375 }
376
setOnValueChangeCallback(const VehiclePropertyStore::OnValueChangeCallback & callback)377 void VehiclePropertyStore::setOnValueChangeCallback(
378 const VehiclePropertyStore::OnValueChangeCallback& callback) {
379 std::scoped_lock<std::mutex> g(mLock);
380
381 mOnValueChangeCallback = callback;
382 }
383
setOnValuesChangeCallback(const VehiclePropertyStore::OnValuesChangeCallback & callback)384 void VehiclePropertyStore::setOnValuesChangeCallback(
385 const VehiclePropertyStore::OnValuesChangeCallback& callback) {
386 std::scoped_lock<std::mutex> g(mLock);
387
388 mOnValuesChangeCallback = callback;
389 }
390
391 } // namespace vehicle
392 } // namespace automotive
393 } // namespace hardware
394 } // namespace android
395