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 "SubscriptionManager.h"
18
19 #include <VehicleUtils.h>
20 #include <android-base/format.h>
21 #include <android-base/stringprintf.h>
22 #include <utils/Log.h>
23 #include <utils/SystemClock.h>
24
25 #include <inttypes.h>
26
27 namespace android {
28 namespace hardware {
29 namespace automotive {
30 namespace vehicle {
31
32 namespace {
33
34 using ::aidl::android::hardware::automotive::vehicle::IVehicleCallback;
35 using ::aidl::android::hardware::automotive::vehicle::StatusCode;
36 using ::aidl::android::hardware::automotive::vehicle::SubscribeOptions;
37 using ::aidl::android::hardware::automotive::vehicle::VehiclePropError;
38 using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
39 using ::android::base::Error;
40 using ::android::base::Result;
41 using ::android::base::StringPrintf;
42 using ::ndk::ScopedAStatus;
43
44 constexpr float EPSILON = 0.0000001;
45 constexpr float ONE_SECOND_IN_NANOS = 1'000'000'000.;
46
newSubscribeOptions(int32_t propId,int32_t areaId,float sampleRateHz,float resolution,bool enableVur)47 SubscribeOptions newSubscribeOptions(int32_t propId, int32_t areaId, float sampleRateHz,
48 float resolution, bool enableVur) {
49 SubscribeOptions subscribedOptions;
50 subscribedOptions.propId = propId;
51 subscribedOptions.areaIds = {areaId};
52 subscribedOptions.sampleRate = sampleRateHz;
53 subscribedOptions.resolution = resolution;
54 subscribedOptions.enableVariableUpdateRate = enableVur;
55
56 return subscribedOptions;
57 }
58
59 } // namespace
60
SubscriptionManager(IVehicleHardware * vehicleHardware)61 SubscriptionManager::SubscriptionManager(IVehicleHardware* vehicleHardware)
62 : mVehicleHardware(vehicleHardware) {}
63
~SubscriptionManager()64 SubscriptionManager::~SubscriptionManager() {
65 std::scoped_lock<std::mutex> lockGuard(mLock);
66
67 mClientsByPropIdAreaId.clear();
68 mSubscribedPropsByClient.clear();
69 mSupportedValueChangeClientsByPropIdAreaId.clear();
70 mSupportedValueChangePropIdAreaIdsByClient.clear();
71 }
72
checkSampleRateHz(float sampleRateHz)73 bool SubscriptionManager::checkSampleRateHz(float sampleRateHz) {
74 return getIntervalNanos(sampleRateHz).ok();
75 }
76
getIntervalNanos(float sampleRateHz)77 Result<int64_t> SubscriptionManager::getIntervalNanos(float sampleRateHz) {
78 int64_t intervalNanos = 0;
79 if (sampleRateHz <= 0) {
80 return Error() << "invalid sample rate, must be a positive number";
81 }
82 if (sampleRateHz <= (ONE_SECOND_IN_NANOS / static_cast<float>(INT64_MAX))) {
83 return Error() << "invalid sample rate: " << sampleRateHz << ", too small";
84 }
85 intervalNanos = static_cast<int64_t>(ONE_SECOND_IN_NANOS / sampleRateHz);
86 return intervalNanos;
87 }
88
checkResolution(float resolution)89 bool SubscriptionManager::checkResolution(float resolution) {
90 if (resolution == 0) {
91 return true;
92 }
93
94 float log = std::log10(resolution);
95 return std::abs(log - std::round(log)) < EPSILON;
96 }
97
refreshCombinedConfig()98 void ContSubConfigs::refreshCombinedConfig() {
99 float maxSampleRateHz = 0.;
100 float minRequiredResolution = std::numeric_limits<float>::max();
101 bool enableVur = true;
102 // This is not called frequently so a brute-focre is okay. More efficient way exists but this
103 // is simpler.
104 for (const auto& [_, subConfig] : mConfigByClient) {
105 if (subConfig.sampleRateHz > maxSampleRateHz) {
106 maxSampleRateHz = subConfig.sampleRateHz;
107 }
108 if (subConfig.resolution < minRequiredResolution) {
109 minRequiredResolution = subConfig.resolution;
110 }
111 if (!subConfig.enableVur) {
112 // If one client does not enable variable update rate, we cannot enable variable update
113 // rate in IVehicleHardware.
114 enableVur = false;
115 }
116 }
117 mMaxSampleRateHz = maxSampleRateHz;
118 mMinRequiredResolution = minRequiredResolution;
119 mEnableVur = enableVur;
120 }
121
addClient(const ClientIdType & clientId,const SubConfig & subConfig)122 void ContSubConfigs::addClient(const ClientIdType& clientId, const SubConfig& subConfig) {
123 mConfigByClient[clientId] = subConfig;
124 refreshCombinedConfig();
125 }
126
removeClient(const ClientIdType & clientId)127 void ContSubConfigs::removeClient(const ClientIdType& clientId) {
128 mConfigByClient.erase(clientId);
129 refreshCombinedConfig();
130 }
131
getMaxSampleRateHz() const132 float ContSubConfigs::getMaxSampleRateHz() const {
133 return mMaxSampleRateHz;
134 }
135
getMinRequiredResolution() const136 float ContSubConfigs::getMinRequiredResolution() const {
137 return mMinRequiredResolution;
138 }
139
isVurEnabled() const140 bool ContSubConfigs::isVurEnabled() const {
141 return mEnableVur;
142 }
143
isVurEnabledForClient(const ClientIdType & clientId) const144 bool ContSubConfigs::isVurEnabledForClient(const ClientIdType& clientId) const {
145 if (mConfigByClient.find(clientId) == mConfigByClient.end()) {
146 return false;
147 }
148 return mConfigByClient.at(clientId).enableVur;
149 }
150
getResolutionForClient(const ClientIdType & clientId) const151 float ContSubConfigs::getResolutionForClient(const ClientIdType& clientId) const {
152 if (mConfigByClient.find(clientId) == mConfigByClient.end()) {
153 return 0.0f;
154 }
155 return mConfigByClient.at(clientId).resolution;
156 }
157
addOnChangeSubscriberLocked(const PropIdAreaId & propIdAreaId)158 VhalResult<void> SubscriptionManager::addOnChangeSubscriberLocked(
159 const PropIdAreaId& propIdAreaId) {
160 if (mClientsByPropIdAreaId.find(propIdAreaId) != mClientsByPropIdAreaId.end()) {
161 // This propId, areaId is already subscribed, ignore the request.
162 return {};
163 }
164
165 int32_t propId = propIdAreaId.propId;
166 int32_t areaId = propIdAreaId.areaId;
167 if (auto status = mVehicleHardware->subscribe(
168 newSubscribeOptions(propId, areaId, /*updateRateHz=*/0, /*resolution*/ 0.0f,
169 /*enableVur*/ false));
170 status != StatusCode::OK) {
171 return StatusError(status)
172 << fmt::format("failed subscribe for propIdAreaId: {}", propIdAreaId);
173 }
174 return {};
175 }
176
addContinuousSubscriberLocked(const ClientIdType & clientId,const PropIdAreaId & propIdAreaId,float sampleRateHz,float resolution,bool enableVur)177 VhalResult<void> SubscriptionManager::addContinuousSubscriberLocked(
178 const ClientIdType& clientId, const PropIdAreaId& propIdAreaId, float sampleRateHz,
179 float resolution, bool enableVur) {
180 // Make a copy so that we don't modify 'mContSubConfigsByPropIdArea' on failure cases.
181 ContSubConfigs newConfig = mContSubConfigsByPropIdArea[propIdAreaId];
182 SubConfig subConfig = {
183 .sampleRateHz = sampleRateHz,
184 .resolution = resolution,
185 .enableVur = enableVur,
186 };
187 newConfig.addClient(clientId, subConfig);
188 return updateContSubConfigsLocked(propIdAreaId, newConfig);
189 }
190
removeContinuousSubscriberLocked(const ClientIdType & clientId,const PropIdAreaId & propIdAreaId)191 VhalResult<void> SubscriptionManager::removeContinuousSubscriberLocked(
192 const ClientIdType& clientId, const PropIdAreaId& propIdAreaId) {
193 // Make a copy so that we don't modify 'mContSubConfigsByPropIdArea' on failure cases.
194 ContSubConfigs newConfig = mContSubConfigsByPropIdArea[propIdAreaId];
195 newConfig.removeClient(clientId);
196 return updateContSubConfigsLocked(propIdAreaId, newConfig);
197 }
198
removeOnChangeSubscriberLocked(const PropIdAreaId & propIdAreaId)199 VhalResult<void> SubscriptionManager::removeOnChangeSubscriberLocked(
200 const PropIdAreaId& propIdAreaId) {
201 if (mClientsByPropIdAreaId[propIdAreaId].size() > 1) {
202 // After unsubscribing this client, there is still client subscribed, so do nothing.
203 return {};
204 }
205
206 int32_t propId = propIdAreaId.propId;
207 int32_t areaId = propIdAreaId.areaId;
208 if (auto status = mVehicleHardware->unsubscribe(propId, areaId); status != StatusCode::OK) {
209 return StatusError(status)
210 << StringPrintf("failed unsubscribe for prop: %s, areaId: %" PRId32,
211 propIdToString(propId).c_str(), areaId);
212 }
213 return {};
214 }
215
updateContSubConfigsLocked(const PropIdAreaId & propIdAreaId,const ContSubConfigs & newConfig)216 VhalResult<void> SubscriptionManager::updateContSubConfigsLocked(const PropIdAreaId& propIdAreaId,
217 const ContSubConfigs& newConfig) {
218 const auto& oldConfig = mContSubConfigsByPropIdArea[propIdAreaId];
219 float newRateHz = newConfig.getMaxSampleRateHz();
220 float oldRateHz = oldConfig.getMaxSampleRateHz();
221 float newResolution = newConfig.getMinRequiredResolution();
222 float oldResolution = oldConfig.getMinRequiredResolution();
223 if (newRateHz == oldRateHz && newResolution == oldResolution &&
224 newConfig.isVurEnabled() == oldConfig.isVurEnabled()) {
225 mContSubConfigsByPropIdArea[propIdAreaId] = newConfig;
226 return {};
227 }
228 int32_t propId = propIdAreaId.propId;
229 int32_t areaId = propIdAreaId.areaId;
230 if (newRateHz != oldRateHz) {
231 if (auto status = mVehicleHardware->updateSampleRate(propId, areaId, newRateHz);
232 status != StatusCode::OK) {
233 return StatusError(status)
234 << StringPrintf("failed to update sample rate for prop: %s, areaId: %" PRId32
235 ", sample rate: %f HZ",
236 propIdToString(propId).c_str(), areaId, newRateHz);
237 }
238 }
239 if (newRateHz != 0) {
240 if (auto status = mVehicleHardware->subscribe(newSubscribeOptions(
241 propId, areaId, newRateHz, newResolution, newConfig.isVurEnabled()));
242 status != StatusCode::OK) {
243 return StatusError(status) << StringPrintf(
244 "failed subscribe for prop: %s, areaId"
245 ": %" PRId32 ", sample rate: %f HZ",
246 propIdToString(propId).c_str(), areaId, newRateHz);
247 }
248 } else {
249 if (auto status = mVehicleHardware->unsubscribe(propId, areaId); status != StatusCode::OK) {
250 return StatusError(status) << StringPrintf(
251 "failed unsubscribe for prop: %s, areaId"
252 ": %" PRId32,
253 propIdToString(propId).c_str(), areaId);
254 }
255 }
256 mContSubConfigsByPropIdArea[propIdAreaId] = newConfig;
257 return {};
258 }
259
subscribe(const std::shared_ptr<IVehicleCallback> & callback,const std::vector<SubscribeOptions> & options,bool isContinuousProperty)260 VhalResult<void> SubscriptionManager::subscribe(const std::shared_ptr<IVehicleCallback>& callback,
261 const std::vector<SubscribeOptions>& options,
262 bool isContinuousProperty) {
263 std::scoped_lock<std::mutex> lockGuard(mLock);
264
265 for (const auto& option : options) {
266 float sampleRateHz = option.sampleRate;
267
268 if (isContinuousProperty) {
269 if (auto result = getIntervalNanos(sampleRateHz); !result.ok()) {
270 return StatusError(StatusCode::INVALID_ARG) << result.error().message();
271 }
272 if (!checkResolution(option.resolution)) {
273 return StatusError(StatusCode::INVALID_ARG) << StringPrintf(
274 "SubscribeOptions.resolution %f is not an integer power of 10",
275 option.resolution);
276 }
277 }
278
279 if (option.areaIds.empty()) {
280 ALOGE("area IDs to subscribe must not be empty");
281 return StatusError(StatusCode::INVALID_ARG)
282 << "area IDs to subscribe must not be empty";
283 }
284 }
285
286 ClientIdType clientId = callback->asBinder().get();
287
288 for (const auto& option : options) {
289 int32_t propId = option.propId;
290 const std::vector<int32_t>& areaIds = option.areaIds;
291 for (int32_t areaId : areaIds) {
292 PropIdAreaId propIdAreaId = {
293 .propId = propId,
294 .areaId = areaId,
295 };
296 VhalResult<void> result;
297 if (isContinuousProperty) {
298 result = addContinuousSubscriberLocked(clientId, propIdAreaId, option.sampleRate,
299 option.resolution,
300 option.enableVariableUpdateRate);
301 } else {
302 result = addOnChangeSubscriberLocked(propIdAreaId);
303 }
304
305 if (!result.ok()) {
306 return result;
307 }
308
309 mSubscribedPropsByClient[clientId].insert(propIdAreaId);
310 mClientsByPropIdAreaId[propIdAreaId][clientId] = callback;
311 }
312 }
313 return {};
314 }
315
unsubscribePropIdAreaIdLocked(SubscriptionManager::ClientIdType clientId,const PropIdAreaId & propIdAreaId)316 VhalResult<void> SubscriptionManager::unsubscribePropIdAreaIdLocked(
317 SubscriptionManager::ClientIdType clientId, const PropIdAreaId& propIdAreaId) {
318 if (mContSubConfigsByPropIdArea.find(propIdAreaId) != mContSubConfigsByPropIdArea.end()) {
319 // This is a subscribed continuous property.
320 if (auto result = removeContinuousSubscriberLocked(clientId, propIdAreaId); !result.ok()) {
321 return result;
322 }
323 } else {
324 if (mClientsByPropIdAreaId.find(propIdAreaId) == mClientsByPropIdAreaId.end()) {
325 ALOGW("Unsubscribe: The property: %s, areaId: %" PRId32
326 " was not previously subscribed, do nothing",
327 propIdToString(propIdAreaId.propId).c_str(), propIdAreaId.areaId);
328 return {};
329 }
330 // This is an on-change property.
331 if (auto result = removeOnChangeSubscriberLocked(propIdAreaId); !result.ok()) {
332 return result;
333 }
334 }
335
336 auto& clients = mClientsByPropIdAreaId[propIdAreaId];
337 clients.erase(clientId);
338 if (clients.empty()) {
339 mClientsByPropIdAreaId.erase(propIdAreaId);
340 mContSubConfigsByPropIdArea.erase(propIdAreaId);
341 }
342 return {};
343 }
344
unsubscribe(SubscriptionManager::ClientIdType clientId,const std::vector<int32_t> & propIds)345 VhalResult<void> SubscriptionManager::unsubscribe(SubscriptionManager::ClientIdType clientId,
346 const std::vector<int32_t>& propIds) {
347 std::scoped_lock<std::mutex> lockGuard(mLock);
348
349 if (mSubscribedPropsByClient.find(clientId) == mSubscribedPropsByClient.end()) {
350 ALOGW("No property was subscribed for the callback, unsubscribe does nothing");
351 return {};
352 }
353
354 std::vector<PropIdAreaId> propIdAreaIdsToUnsubscribe;
355 std::unordered_set<int32_t> propIdSet;
356 for (int32_t propId : propIds) {
357 propIdSet.insert(propId);
358 }
359 auto& subscribedPropIdsAreaIds = mSubscribedPropsByClient[clientId];
360 for (const auto& propIdAreaId : subscribedPropIdsAreaIds) {
361 if (propIdSet.find(propIdAreaId.propId) != propIdSet.end()) {
362 propIdAreaIdsToUnsubscribe.push_back(propIdAreaId);
363 }
364 }
365
366 for (const auto& propIdAreaId : propIdAreaIdsToUnsubscribe) {
367 if (auto result = unsubscribePropIdAreaIdLocked(clientId, propIdAreaId); !result.ok()) {
368 return result;
369 }
370 subscribedPropIdsAreaIds.erase(propIdAreaId);
371 }
372
373 if (subscribedPropIdsAreaIds.empty()) {
374 mSubscribedPropsByClient.erase(clientId);
375 }
376 return {};
377 }
378
unsubscribe(SubscriptionManager::ClientIdType clientId)379 VhalResult<void> SubscriptionManager::unsubscribe(SubscriptionManager::ClientIdType clientId) {
380 std::scoped_lock<std::mutex> lockGuard(mLock);
381
382 if (mSubscribedPropsByClient.find(clientId) == mSubscribedPropsByClient.end()) {
383 ALOGW("No property was subscribed for this client, unsubscribe does nothing");
384 } else {
385 auto& propIdAreaIds = mSubscribedPropsByClient[clientId];
386 for (auto const& propIdAreaId : propIdAreaIds) {
387 if (auto result = unsubscribePropIdAreaIdLocked(clientId, propIdAreaId); !result.ok()) {
388 return result;
389 }
390 }
391 mSubscribedPropsByClient.erase(clientId);
392 }
393
394 if (mSupportedValueChangePropIdAreaIdsByClient.find(clientId) ==
395 mSupportedValueChangePropIdAreaIdsByClient.end()) {
396 ALOGW("No supported value change was subscribed for this client, unsubscribe does nothing");
397 } else {
398 const auto& propIdAreaIds = mSupportedValueChangePropIdAreaIdsByClient[clientId];
399 if (auto result = unsubscribeSupportedValueChangeLocked(
400 clientId,
401 std::vector<PropIdAreaId>(propIdAreaIds.begin(), propIdAreaIds.end()));
402 !result.ok()) {
403 return result;
404 }
405 }
406 return {};
407 }
408
subscribeSupportedValueChange(const std::shared_ptr<IVehicleCallback> & callback,const std::vector<PropIdAreaId> & propIdAreaIds)409 VhalResult<void> SubscriptionManager::subscribeSupportedValueChange(
410 const std::shared_ptr<IVehicleCallback>& callback,
411 const std::vector<PropIdAreaId>& propIdAreaIds) {
412 // Need to make sure this whole operation is guarded by a lock so that our internal state is
413 // consistent with IVehicleHardware state.
414 std::scoped_lock<std::mutex> lockGuard(mLock);
415
416 ClientIdType clientId = callback->asBinder().get();
417
418 // It is possible that some of the [propId, areaId]s are already subscribed, IVehicleHardware
419 // will ignore them.
420 if (auto status = mVehicleHardware->subscribeSupportedValueChange(propIdAreaIds);
421 status != StatusCode::OK) {
422 return StatusError(status)
423 << fmt::format("failed to call subscribeSupportedValueChange for propIdAreaIds: {}",
424 propIdAreaIds);
425 }
426 for (const auto& propIdAreaId : propIdAreaIds) {
427 mSupportedValueChangeClientsByPropIdAreaId[propIdAreaId][clientId] = callback;
428 // mSupportedValueChangePropIdAreaIdsByClient[clientId] is a set so this will ignore
429 // duplicate [propId, areaId].
430 mSupportedValueChangePropIdAreaIdsByClient[clientId].insert(propIdAreaId);
431 }
432 return {};
433 }
434
unsubscribeSupportedValueChange(SubscriptionManager::ClientIdType clientId,const std::vector<PropIdAreaId> & propIdAreaIds)435 VhalResult<void> SubscriptionManager::unsubscribeSupportedValueChange(
436 SubscriptionManager::ClientIdType clientId,
437 const std::vector<PropIdAreaId>& propIdAreaIds) {
438 // Need to make sure this whole operation is guarded by a lock so that our internal state is
439 // consistent with IVehicleHardware state.
440 std::scoped_lock<std::mutex> lockGuard(mLock);
441
442 return unsubscribeSupportedValueChangeLocked(clientId, propIdAreaIds);
443 }
444
unsubscribeSupportedValueChangeLocked(SubscriptionManager::ClientIdType clientId,const std::vector<PropIdAreaId> & propIdAreaIds)445 VhalResult<void> SubscriptionManager::unsubscribeSupportedValueChangeLocked(
446 SubscriptionManager::ClientIdType clientId,
447 const std::vector<PropIdAreaId>& propIdAreaIds) {
448 std::vector<PropIdAreaId> propIdAreaIdsToUnsubscribe;
449
450 // Check which [propId, areaId] needs to be unsubscribed from the hardware.
451 for (const auto& propIdAreaId : propIdAreaIds) {
452 auto it = mSupportedValueChangeClientsByPropIdAreaId.find(propIdAreaId);
453 if (it != mSupportedValueChangeClientsByPropIdAreaId.end()) {
454 const auto& clients = it->second;
455 if (clients.size() == 1 && clients.find(clientId) != clients.end()) {
456 // This callback is the only client registered for [propId, areaId].
457 // Unregister it should unregister the [propId, areaId].
458 propIdAreaIdsToUnsubscribe.push_back(propIdAreaId);
459 }
460 }
461 }
462
463 // Send the unsubscribe request.
464 if (!propIdAreaIdsToUnsubscribe.empty()) {
465 if (auto status =
466 mVehicleHardware->unsubscribeSupportedValueChange(propIdAreaIdsToUnsubscribe);
467 status != StatusCode::OK) {
468 return StatusError(status) << fmt::format(
469 "failed to call unsubscribeSupportedValueChange for "
470 "propIdAreaIds: {}",
471 propIdAreaIdsToUnsubscribe);
472 }
473 }
474
475 // Remove internal book-keeping.
476 for (const auto& propIdAreaId : propIdAreaIds) {
477 if (mSupportedValueChangeClientsByPropIdAreaId.find(propIdAreaId) !=
478 mSupportedValueChangeClientsByPropIdAreaId.end()) {
479 mSupportedValueChangeClientsByPropIdAreaId[propIdAreaId].erase(clientId);
480 }
481 if (mSupportedValueChangeClientsByPropIdAreaId[propIdAreaId].empty()) {
482 mSupportedValueChangeClientsByPropIdAreaId.erase(propIdAreaId);
483 }
484 mSupportedValueChangePropIdAreaIdsByClient[clientId].erase(propIdAreaId);
485 if (mSupportedValueChangePropIdAreaIdsByClient[clientId].empty()) {
486 mSupportedValueChangePropIdAreaIdsByClient.erase(clientId);
487 }
488 }
489 return {};
490 }
491
isValueUpdatedLocked(const std::shared_ptr<IVehicleCallback> & callback,const VehiclePropValue & value)492 bool SubscriptionManager::isValueUpdatedLocked(const std::shared_ptr<IVehicleCallback>& callback,
493 const VehiclePropValue& value) {
494 const auto& it = mContSubValuesByCallback[callback].find(value);
495 if (it == mContSubValuesByCallback[callback].end()) {
496 mContSubValuesByCallback[callback].insert(value);
497 return true;
498 }
499
500 if (it->timestamp > value.timestamp) {
501 ALOGE("The updated property value: %s is outdated, ignored", value.toString().c_str());
502 return false;
503 }
504
505 if (it->value == value.value && it->status == value.status) {
506 // Even though the property value is the same, we need to store the new property event to
507 // update the timestamp.
508 mContSubValuesByCallback[callback].insert(value);
509 ALOGD("The updated property value for propId: %" PRId32 ", areaId: %" PRId32
510 " has the "
511 "same value and status, ignored if VUR is enabled",
512 it->prop, it->areaId);
513 return false;
514 }
515
516 mContSubValuesByCallback[callback].insert(value);
517 return true;
518 }
519
520 std::unordered_map<std::shared_ptr<IVehicleCallback>, std::vector<VehiclePropValue>>
getSubscribedClients(std::vector<VehiclePropValue> && updatedValues)521 SubscriptionManager::getSubscribedClients(std::vector<VehiclePropValue>&& updatedValues) {
522 std::scoped_lock<std::mutex> lockGuard(mLock);
523 std::unordered_map<std::shared_ptr<IVehicleCallback>, std::vector<VehiclePropValue>> clients;
524
525 for (auto& value : updatedValues) {
526 PropIdAreaId propIdAreaId{
527 .propId = value.prop,
528 .areaId = value.areaId,
529 };
530 if (mClientsByPropIdAreaId.find(propIdAreaId) == mClientsByPropIdAreaId.end()) {
531 continue;
532 }
533
534 for (const auto& [client, callback] : mClientsByPropIdAreaId[propIdAreaId]) {
535 // if propId is on-change, propIdAreaId will not exist in mContSubConfigsByPropIdArea,
536 // returning an empty ContSubConfigs value for subConfigs i.e. with resolution = 0 and
537 // enableVur = false.
538 auto& subConfigs = mContSubConfigsByPropIdArea[propIdAreaId];
539 // Clients must be sent different VehiclePropValues with different levels of granularity
540 // as requested by the client using resolution.
541 VehiclePropValue newValue = value;
542 sanitizeByResolution(&(newValue.value), subConfigs.getResolutionForClient(client));
543 // If client wants VUR (and VUR is supported as checked in DefaultVehicleHal), it is
544 // possible that VUR is not enabled in IVehicleHardware because another client does not
545 // enable VUR. We will implement VUR filtering here for the client that enables it.
546 if (subConfigs.isVurEnabledForClient(client) && !subConfigs.isVurEnabled()) {
547 if (isValueUpdatedLocked(callback, newValue)) {
548 clients[callback].push_back(newValue);
549 }
550 } else {
551 clients[callback].push_back(newValue);
552 }
553 }
554 }
555 return clients;
556 }
557
558 std::unordered_map<std::shared_ptr<IVehicleCallback>, std::vector<VehiclePropError>>
getSubscribedClientsForErrorEvents(const std::vector<SetValueErrorEvent> & errorEvents)559 SubscriptionManager::getSubscribedClientsForErrorEvents(
560 const std::vector<SetValueErrorEvent>& errorEvents) {
561 std::scoped_lock<std::mutex> lockGuard(mLock);
562 std::unordered_map<std::shared_ptr<IVehicleCallback>, std::vector<VehiclePropError>> clients;
563
564 for (const auto& errorEvent : errorEvents) {
565 PropIdAreaId propIdAreaId{
566 .propId = errorEvent.propId,
567 .areaId = errorEvent.areaId,
568 };
569 if (mClientsByPropIdAreaId.find(propIdAreaId) == mClientsByPropIdAreaId.end()) {
570 continue;
571 }
572
573 for (const auto& [_, client] : mClientsByPropIdAreaId[propIdAreaId]) {
574 clients[client].push_back({
575 .propId = errorEvent.propId,
576 .areaId = errorEvent.areaId,
577 .errorCode = errorEvent.errorCode,
578 });
579 }
580 }
581 return clients;
582 }
583
584 std::unordered_map<std::shared_ptr<IVehicleCallback>, std::vector<PropIdAreaId>>
getSubscribedClientsForSupportedValueChange(const std::vector<PropIdAreaId> & propIdAreaIds)585 SubscriptionManager::getSubscribedClientsForSupportedValueChange(
586 const std::vector<PropIdAreaId>& propIdAreaIds) {
587 std::scoped_lock<std::mutex> lockGuard(mLock);
588 std::unordered_map<std::shared_ptr<IVehicleCallback>, std::vector<PropIdAreaId>>
589 propIdAreaIdsByClient;
590
591 for (const auto& propIdAreaId : propIdAreaIds) {
592 const auto clientIter = mSupportedValueChangeClientsByPropIdAreaId.find(propIdAreaId);
593 if (clientIter == mSupportedValueChangeClientsByPropIdAreaId.end()) {
594 continue;
595 }
596 for (const auto& [_, client] : clientIter->second) {
597 propIdAreaIdsByClient[client].push_back(propIdAreaId);
598 }
599 }
600 return propIdAreaIdsByClient;
601 }
602
isEmpty()603 bool SubscriptionManager::isEmpty() {
604 std::scoped_lock<std::mutex> lockGuard(mLock);
605 return mSubscribedPropsByClient.empty() && mClientsByPropIdAreaId.empty() &&
606 mSupportedValueChangeClientsByPropIdAreaId.empty() &&
607 mSupportedValueChangePropIdAreaIdsByClient.empty();
608 }
609
countPropertyChangeClients()610 size_t SubscriptionManager::countPropertyChangeClients() {
611 std::scoped_lock<std::mutex> lockGuard(mLock);
612 return mSubscribedPropsByClient.size();
613 }
614
countSupportedValueChangeClients()615 size_t SubscriptionManager::countSupportedValueChangeClients() {
616 std::scoped_lock<std::mutex> lockGuard(mLock);
617 return mSupportedValueChangePropIdAreaIdsByClient.size();
618 }
619
620 } // namespace vehicle
621 } // namespace automotive
622 } // namespace hardware
623 } // namespace android
624