/*
* Copyright (C) 2016 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.
*/
#define LOG_TAG "automotive.vehicle@2.0-impl"
#include "SubscriptionManager.h"
#include <cmath>
#include <inttypes.h>
#include <android/log.h>
#include "VehicleUtils.h"
namespace android {
namespace hardware {
namespace automotive {
namespace vehicle {
namespace V2_0 {
bool mergeSubscribeOptions(const SubscribeOptions &oldOpts,
const SubscribeOptions &newOpts,
SubscribeOptions *outResult) {
int32_t updatedAreas = oldOpts.vehicleAreas;
if (updatedAreas != kAllSupportedAreas) {
updatedAreas = newOpts.vehicleAreas != kAllSupportedAreas
? updatedAreas | newOpts.vehicleAreas
: kAllSupportedAreas;
}
float updatedRate = std::max(oldOpts.sampleRate, newOpts.sampleRate);
SubscribeFlags updatedFlags = SubscribeFlags(oldOpts.flags | newOpts.flags);
bool updated = updatedRate > oldOpts.sampleRate
|| updatedAreas != oldOpts.vehicleAreas
|| updatedFlags != oldOpts.flags;
if (updated) {
*outResult = oldOpts;
outResult->vehicleAreas = updatedAreas;
outResult->sampleRate = updatedRate;
outResult->flags = updatedFlags;
}
return updated;
}
void HalClient::addOrUpdateSubscription(const SubscribeOptions &opts) {
ALOGI("%s opts.propId: 0x%x", __func__, opts.propId);
auto it = mSubscriptions.find(opts.propId);
if (it == mSubscriptions.end()) {
mSubscriptions.emplace(opts.propId, opts);
} else {
const SubscribeOptions& oldOpts = it->second;
SubscribeOptions updatedOptions;
if (mergeSubscribeOptions(oldOpts, opts, &updatedOptions)) {
mSubscriptions.erase(it);
mSubscriptions.emplace(opts.propId, updatedOptions);
}
}
}
bool HalClient::isSubscribed(int32_t propId,
int32_t areaId,
SubscribeFlags flags) {
auto it = mSubscriptions.find(propId);
if (it == mSubscriptions.end()) {
return false;
}
const SubscribeOptions& opts = it->second;
bool res = (opts.flags & flags)
&& (opts.vehicleAreas == 0 || areaId == 0 || opts.vehicleAreas & areaId);
return res;
}
std::vector<int32_t> HalClient::getSubscribedProperties() const {
std::vector<int32_t> props;
for (const auto& subscription : mSubscriptions) {
ALOGI("%s propId: 0x%x, propId: 0x%x", __func__, subscription.first, subscription.second.propId);
props.push_back(subscription.first);
}
return props;
}
StatusCode SubscriptionManager::addOrUpdateSubscription(
ClientId clientId,
const sp<IVehicleCallback> &callback,
const hidl_vec<SubscribeOptions> &optionList,
std::list<SubscribeOptions>* outUpdatedSubscriptions) {
outUpdatedSubscriptions->clear();
MuxGuard g(mLock);
ALOGI("SubscriptionManager::addOrUpdateSubscription, callback: %p", callback.get());
const sp<HalClient>& client = getOrCreateHalClientLocked(clientId, callback);
if (client.get() == nullptr) {
return StatusCode::INTERNAL_ERROR;
}
for (size_t i = 0; i < optionList.size(); i++) {
const SubscribeOptions& opts = optionList[i];
ALOGI("SubscriptionManager::addOrUpdateSubscription, prop: 0x%x", opts.propId);
client->addOrUpdateSubscription(opts);
addClientToPropMapLocked(opts.propId, client);
if (SubscribeFlags::HAL_EVENT & opts.flags) {
SubscribeOptions updated;
if (updateHalEventSubscriptionLocked(opts, &updated)) {
outUpdatedSubscriptions->push_back(updated);
}
}
}
return StatusCode::OK;
}
std::list<HalClientValues> SubscriptionManager::distributeValuesToClients(
const std::vector<recyclable_ptr<VehiclePropValue>>& propValues,
SubscribeFlags flags) const {
std::map<sp<HalClient>, std::list<VehiclePropValue*>> clientValuesMap;
{
MuxGuard g(mLock);
for (const auto& propValue: propValues) {
VehiclePropValue* v = propValue.get();
auto clients = getSubscribedClientsLocked(
v->prop, v->areaId, flags);
for (const auto& client : clients) {
clientValuesMap[client].push_back(v);
}
}
}
std::list<HalClientValues> clientValues;
for (const auto& entry : clientValuesMap) {
clientValues.push_back(HalClientValues {
.client = entry.first,
.values = entry.second
});
}
return clientValues;
}
std::list<sp<HalClient>> SubscriptionManager::getSubscribedClients(
int32_t propId, int32_t area, SubscribeFlags flags) const {
MuxGuard g(mLock);
return getSubscribedClientsLocked(propId, area, flags);
}
std::list<sp<HalClient>> SubscriptionManager::getSubscribedClientsLocked(
int32_t propId, int32_t area, SubscribeFlags flags) const {
std::list<sp<HalClient>> subscribedClients;
sp<HalClientVector> propClients = getClientsForPropertyLocked(propId);
if (propClients.get() != nullptr) {
for (size_t i = 0; i < propClients->size(); i++) {
const auto& client = propClients->itemAt(i);
if (client->isSubscribed(propId, area, flags)) {
subscribedClients.push_back(client);
}
}
}
return subscribedClients;
}
bool SubscriptionManager::updateHalEventSubscriptionLocked(
const SubscribeOptions &opts, SubscribeOptions *outUpdated) {
bool updated = false;
auto it = mHalEventSubscribeOptions.find(opts.propId);
if (it == mHalEventSubscribeOptions.end()) {
*outUpdated = opts;
mHalEventSubscribeOptions.emplace(opts.propId, opts);
updated = true;
} else {
const SubscribeOptions& oldOpts = it->second;
if (mergeSubscribeOptions(oldOpts, opts, outUpdated)) {
mHalEventSubscribeOptions.erase(opts.propId);
mHalEventSubscribeOptions.emplace(opts.propId, *outUpdated);
updated = true;
}
}
return updated;
}
void SubscriptionManager::addClientToPropMapLocked(
int32_t propId, const sp<HalClient> &client) {
auto it = mPropToClients.find(propId);
sp<HalClientVector> propClients;
if (it == mPropToClients.end()) {
propClients = new HalClientVector();
mPropToClients.insert(std::make_pair(propId, propClients));
} else {
propClients = it->second;
}
propClients->addOrUpdate(client);
}
sp<HalClientVector> SubscriptionManager::getClientsForPropertyLocked(
int32_t propId) const {
auto it = mPropToClients.find(propId);
return it == mPropToClients.end() ? nullptr : it->second;
}
sp<HalClient> SubscriptionManager::getOrCreateHalClientLocked(
ClientId clientId, const sp<IVehicleCallback>& callback) {
auto it = mClients.find(clientId);
if (it == mClients.end()) {
uint64_t cookie = reinterpret_cast<uint64_t>(clientId);
ALOGI("Creating new client and linking to death recipient, cookie: 0x%" PRIx64, cookie);
auto res = callback->linkToDeath(mCallbackDeathRecipient, cookie);
if (!res.isOk()) { // Client is already dead?
ALOGW("%s failed to link to death, client %p, err: %s",
__func__, callback.get(), res.description().c_str());
return nullptr;
}
sp<HalClient> client = new HalClient(callback);
mClients.insert({clientId, client});
return client;
} else {
return it->second;
}
}
void SubscriptionManager::unsubscribe(ClientId clientId,
int32_t propId) {
MuxGuard g(mLock);
auto propertyClients = getClientsForPropertyLocked(propId);
auto clientIter = mClients.find(clientId);
if (clientIter == mClients.end()) {
ALOGW("Unable to unsubscribe: no callback found, propId: 0x%x", propId);
} else {
auto client = clientIter->second;
if (propertyClients != nullptr) {
propertyClients->remove(client);
if (propertyClients->isEmpty()) {
mPropToClients.erase(propId);
}
}
bool isClientSubscribedToOtherProps = false;
for (const auto& propClient : mPropToClients) {
if (propClient.second->indexOf(client) >= 0) {
isClientSubscribedToOtherProps = true;
break;
}
}
if (!isClientSubscribedToOtherProps) {
auto res = client->getCallback()->unlinkToDeath(mCallbackDeathRecipient);
if (!res.isOk()) {
ALOGW("%s failed to unlink to death, client: %p, err: %s",
__func__, client->getCallback().get(), res.description().c_str());
}
mClients.erase(clientIter);
}
}
if (propertyClients == nullptr || propertyClients->isEmpty()) {
mHalEventSubscribeOptions.erase(propId);
mOnPropertyUnsubscribed(propId);
}
}
void SubscriptionManager::onCallbackDead(uint64_t cookie) {
ALOGI("%s, cookie: 0x%" PRIx64, __func__, cookie);
ClientId clientId = cookie;
std::vector<int32_t> props;
{
MuxGuard g(mLock);
const auto& it = mClients.find(clientId);
if (it == mClients.end()) {
return; // Nothing to do here, client wasn't subscribed to any properties.
}
const auto& halClient = it->second;
props = halClient->getSubscribedProperties();
}
for (int32_t propId : props) {
unsubscribe(clientId, propId);
}
}
} // namespace V2_0
} // namespace vehicle
} // namespace automotive
} // namespace hardware
} // namespace android