• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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