/* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "SubscriptionManager.h" #include #include #include #include namespace android { namespace hardware { namespace automotive { namespace vehicle { namespace { constexpr float ONE_SECOND_IN_NANO = 1'000'000'000.; } // namespace using ::aidl::android::hardware::automotive::vehicle::IVehicleCallback; using ::aidl::android::hardware::automotive::vehicle::StatusCode; using ::aidl::android::hardware::automotive::vehicle::SubscribeOptions; using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue; using ::android::base::Error; using ::android::base::Result; using ::android::base::StringPrintf; using ::ndk::ScopedAStatus; SubscriptionManager::SubscriptionManager(IVehicleHardware* hardware) : mVehicleHardware(hardware) {} SubscriptionManager::~SubscriptionManager() { std::scoped_lock lockGuard(mLock); mClientsByPropIdArea.clear(); mSubscribedPropsByClient.clear(); } bool SubscriptionManager::checkSampleRate(float sampleRate) { return getInterval(sampleRate).ok(); } Result SubscriptionManager::getInterval(float sampleRate) { int64_t interval = 0; if (sampleRate <= 0) { return Error() << "invalid sample rate, must be a positive number"; } if (sampleRate <= (ONE_SECOND_IN_NANO / static_cast(INT64_MAX))) { return Error() << "invalid sample rate: " << sampleRate << ", too small"; } interval = static_cast(ONE_SECOND_IN_NANO / sampleRate); return interval; } void ContSubConfigs::refreshMaxSampleRate() { float maxSampleRate = 0.; // This is not called frequently so a brute-focre is okay. More efficient way exists but this // is simpler. for (const auto& [_, sampleRate] : mSampleRates) { if (sampleRate > maxSampleRate) { maxSampleRate = sampleRate; } } mMaxSampleRate = maxSampleRate; } void ContSubConfigs::addClient(const ClientIdType& clientId, float sampleRate) { mSampleRates[clientId] = sampleRate; refreshMaxSampleRate(); } void ContSubConfigs::removeClient(const ClientIdType& clientId) { mSampleRates.erase(clientId); refreshMaxSampleRate(); } float ContSubConfigs::getMaxSampleRate() { return mMaxSampleRate; } VhalResult SubscriptionManager::updateSampleRateLocked(const ClientIdType& clientId, const PropIdAreaId& propIdAreaId, float sampleRate) { // Make a copy so that we don't modify 'mContSubConfigsByPropIdArea' on failure cases. ContSubConfigs infoCopy = mContSubConfigsByPropIdArea[propIdAreaId]; infoCopy.addClient(clientId, sampleRate); if (infoCopy.getMaxSampleRate() == mContSubConfigsByPropIdArea[propIdAreaId].getMaxSampleRate()) { mContSubConfigsByPropIdArea[propIdAreaId] = infoCopy; return {}; } float newRate = infoCopy.getMaxSampleRate(); int32_t propId = propIdAreaId.propId; int32_t areaId = propIdAreaId.areaId; if (auto status = mVehicleHardware->updateSampleRate(propId, areaId, newRate); status != StatusCode::OK) { return StatusError(status) << StringPrintf("failed to update sample rate for prop: %" PRId32 ", area" ": %" PRId32 ", sample rate: %f", propId, areaId, newRate); } mContSubConfigsByPropIdArea[propIdAreaId] = infoCopy; return {}; } VhalResult SubscriptionManager::removeSampleRateLocked(const ClientIdType& clientId, const PropIdAreaId& propIdAreaId) { // Make a copy so that we don't modify 'mContSubConfigsByPropIdArea' on failure cases. ContSubConfigs infoCopy = mContSubConfigsByPropIdArea[propIdAreaId]; infoCopy.removeClient(clientId); if (infoCopy.getMaxSampleRate() == mContSubConfigsByPropIdArea[propIdAreaId].getMaxSampleRate()) { mContSubConfigsByPropIdArea[propIdAreaId] = infoCopy; return {}; } float newRate = infoCopy.getMaxSampleRate(); int32_t propId = propIdAreaId.propId; int32_t areaId = propIdAreaId.areaId; if (auto status = mVehicleHardware->updateSampleRate(propId, areaId, newRate); status != StatusCode::OK) { return StatusError(status) << StringPrintf("failed to update sample rate for prop: %" PRId32 ", area" ": %" PRId32 ", sample rate: %f", propId, areaId, newRate); } mContSubConfigsByPropIdArea[propIdAreaId] = infoCopy; return {}; } VhalResult SubscriptionManager::subscribe(const std::shared_ptr& callback, const std::vector& options, bool isContinuousProperty) { std::scoped_lock lockGuard(mLock); std::vector intervals; for (const auto& option : options) { float sampleRate = option.sampleRate; if (isContinuousProperty) { auto intervalResult = getInterval(sampleRate); if (!intervalResult.ok()) { return StatusError(StatusCode::INVALID_ARG) << intervalResult.error().message(); } } if (option.areaIds.empty()) { ALOGE("area IDs to subscribe must not be empty"); return StatusError(StatusCode::INVALID_ARG) << "area IDs to subscribe must not be empty"; } } ClientIdType clientId = callback->asBinder().get(); for (const auto& option : options) { int32_t propId = option.propId; const std::vector& areaIds = option.areaIds; for (int32_t areaId : areaIds) { PropIdAreaId propIdAreaId = { .propId = propId, .areaId = areaId, }; if (isContinuousProperty) { if (auto result = updateSampleRateLocked(clientId, propIdAreaId, option.sampleRate); !result.ok()) { return result; } } mSubscribedPropsByClient[clientId].insert(propIdAreaId); mClientsByPropIdArea[propIdAreaId][clientId] = callback; } } return {}; } VhalResult SubscriptionManager::unsubscribe(SubscriptionManager::ClientIdType clientId, const std::vector& propIds) { std::scoped_lock lockGuard(mLock); if (mSubscribedPropsByClient.find(clientId) == mSubscribedPropsByClient.end()) { return StatusError(StatusCode::INVALID_ARG) << "No property was subscribed for the callback"; } std::unordered_set subscribedPropIds; for (auto const& propIdAreaId : mSubscribedPropsByClient[clientId]) { subscribedPropIds.insert(propIdAreaId.propId); } for (int32_t propId : propIds) { if (subscribedPropIds.find(propId) == subscribedPropIds.end()) { return StatusError(StatusCode::INVALID_ARG) << "property ID: " << propId << " is not subscribed"; } } auto& propIdAreaIds = mSubscribedPropsByClient[clientId]; auto it = propIdAreaIds.begin(); while (it != propIdAreaIds.end()) { int32_t propId = it->propId; if (std::find(propIds.begin(), propIds.end(), propId) != propIds.end()) { if (auto result = removeSampleRateLocked(clientId, *it); !result.ok()) { return result; } auto& clients = mClientsByPropIdArea[*it]; clients.erase(clientId); if (clients.empty()) { mClientsByPropIdArea.erase(*it); mContSubConfigsByPropIdArea.erase(*it); } it = propIdAreaIds.erase(it); } else { it++; } } if (propIdAreaIds.empty()) { mSubscribedPropsByClient.erase(clientId); } return {}; } VhalResult SubscriptionManager::unsubscribe(SubscriptionManager::ClientIdType clientId) { std::scoped_lock lockGuard(mLock); if (mSubscribedPropsByClient.find(clientId) == mSubscribedPropsByClient.end()) { return StatusError(StatusCode::INVALID_ARG) << "No property was subscribed for this client"; } auto& subscriptions = mSubscribedPropsByClient[clientId]; for (auto const& propIdAreaId : subscriptions) { if (auto result = removeSampleRateLocked(clientId, propIdAreaId); !result.ok()) { return result; } auto& clients = mClientsByPropIdArea[propIdAreaId]; clients.erase(clientId); if (clients.empty()) { mClientsByPropIdArea.erase(propIdAreaId); mContSubConfigsByPropIdArea.erase(propIdAreaId); } } mSubscribedPropsByClient.erase(clientId); return {}; } std::unordered_map, std::vector> SubscriptionManager::getSubscribedClients(const std::vector& updatedValues) { std::scoped_lock lockGuard(mLock); std::unordered_map, std::vector> clients; for (const auto& value : updatedValues) { PropIdAreaId propIdAreaId{ .propId = value.prop, .areaId = value.areaId, }; if (mClientsByPropIdArea.find(propIdAreaId) == mClientsByPropIdArea.end()) { continue; } for (const auto& [_, client] : mClientsByPropIdArea[propIdAreaId]) { clients[client].push_back(&value); } } return clients; } bool SubscriptionManager::isEmpty() { std::scoped_lock lockGuard(mLock); return mSubscribedPropsByClient.empty() && mClientsByPropIdArea.empty(); } } // namespace vehicle } // namespace automotive } // namespace hardware } // namespace android