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