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