/* * 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 "VehicleHalManager.h" #include <cmath> #include <fstream> #include <android/log.h> #include <android/hardware/automotive/vehicle/2.0/BpHwVehicleCallback.h> #include "VehicleUtils.h" namespace android { namespace hardware { namespace automotive { namespace vehicle { namespace V2_0 { using namespace std::placeholders; constexpr std::chrono::milliseconds kHalEventBatchingTimeWindow(10); const VehiclePropValue kEmptyValue{}; /** * Indicates what's the maximum size of hidl_vec<VehiclePropValue> we want * to store in reusable object pool. */ constexpr auto kMaxHidlVecOfVehiclPropValuePoolSize = 20; Return<void> VehicleHalManager::getAllPropConfigs(getAllPropConfigs_cb _hidl_cb) { ALOGI("getAllPropConfigs called"); hidl_vec<VehiclePropConfig> hidlConfigs; auto& halConfig = mConfigIndex->getAllConfigs(); hidlConfigs.setToExternal( const_cast<VehiclePropConfig *>(halConfig.data()), halConfig.size()); _hidl_cb(hidlConfigs); return Void(); } Return<void> VehicleHalManager::getPropConfigs(const hidl_vec<int32_t> &properties, getPropConfigs_cb _hidl_cb) { std::vector<VehiclePropConfig> configs; for (size_t i = 0; i < properties.size(); i++) { auto prop = properties[i]; if (mConfigIndex->hasConfig(prop)) { configs.push_back(mConfigIndex->getConfig(prop)); } else { ALOGW("Requested config for undefined property: 0x%x", prop); _hidl_cb(StatusCode::INVALID_ARG, hidl_vec<VehiclePropConfig>()); } } _hidl_cb(StatusCode::OK, configs); return Void(); } Return<void> VehicleHalManager::get(const VehiclePropValue& requestedPropValue, get_cb _hidl_cb) { const auto* config = getPropConfigOrNull(requestedPropValue.prop); if (config == nullptr) { ALOGE("Failed to get value: config not found, property: 0x%x", requestedPropValue.prop); _hidl_cb(StatusCode::INVALID_ARG, kEmptyValue); return Void(); } if (!checkReadPermission(*config)) { _hidl_cb(StatusCode::ACCESS_DENIED, kEmptyValue); return Void(); } StatusCode status; auto value = mHal->get(requestedPropValue, &status); _hidl_cb(status, value.get() ? *value : kEmptyValue); return Void(); } Return<StatusCode> VehicleHalManager::set(const VehiclePropValue &value) { auto prop = value.prop; const auto* config = getPropConfigOrNull(prop); if (config == nullptr) { ALOGE("Failed to set value: config not found, property: 0x%x", prop); return StatusCode::INVALID_ARG; } if (!checkWritePermission(*config)) { return StatusCode::ACCESS_DENIED; } handlePropertySetEvent(value); auto status = mHal->set(value); return Return<StatusCode>(status); } Return<StatusCode> VehicleHalManager::subscribe(const sp<IVehicleCallback> &callback, const hidl_vec<SubscribeOptions> &options) { hidl_vec<SubscribeOptions> verifiedOptions(options); for (size_t i = 0; i < verifiedOptions.size(); i++) { SubscribeOptions& ops = verifiedOptions[i]; auto prop = ops.propId; const auto* config = getPropConfigOrNull(prop); if (config == nullptr) { ALOGE("Failed to subscribe: config not found, property: 0x%x", prop); return StatusCode::INVALID_ARG; } if (ops.flags == SubscribeFlags::UNDEFINED) { ALOGE("Failed to subscribe: undefined flag in options provided"); return StatusCode::INVALID_ARG; } if (!isSubscribable(*config, ops.flags)) { ALOGE("Failed to subscribe: property 0x%x is not subscribable", prop); return StatusCode::INVALID_ARG; } int32_t areas = isGlobalProp(prop) ? 0 : ops.vehicleAreas; if (areas != 0 && ((areas & config->supportedAreas) != areas)) { ALOGE("Failed to subscribe property 0x%x. Requested areas 0x%x are " "out of supported range of 0x%x", prop, ops.vehicleAreas, config->supportedAreas); return StatusCode::INVALID_ARG; } ops.vehicleAreas = areas; ops.sampleRate = checkSampleRate(*config, ops.sampleRate); } std::list<SubscribeOptions> updatedOptions; auto res = mSubscriptionManager.addOrUpdateSubscription(getClientId(callback), callback, verifiedOptions, &updatedOptions); if (StatusCode::OK != res) { ALOGW("%s failed to subscribe, error code: %d", __func__, res); return res; } for (auto opt : updatedOptions) { mHal->subscribe(opt.propId, opt.vehicleAreas, opt.sampleRate); } return StatusCode::OK; } Return<StatusCode> VehicleHalManager::unsubscribe(const sp<IVehicleCallback>& callback, int32_t propId) { mSubscriptionManager.unsubscribe(getClientId(callback), propId); return StatusCode::OK; } Return<void> VehicleHalManager::debugDump(IVehicle::debugDump_cb _hidl_cb) { _hidl_cb(""); return Void(); } void VehicleHalManager::init() { ALOGI("VehicleHalManager::init"); mHidlVecOfVehiclePropValuePool.resize(kMaxHidlVecOfVehiclPropValuePoolSize); mBatchingConsumer.run(&mEventQueue, kHalEventBatchingTimeWindow, std::bind(&VehicleHalManager::onBatchHalEvent, this, _1)); mHal->init(&mValueObjectPool, std::bind(&VehicleHalManager::onHalEvent, this, _1), std::bind(&VehicleHalManager::onHalPropertySetError, this, _1, _2, _3)); // Initialize index with vehicle configurations received from VehicleHal. auto supportedPropConfigs = mHal->listProperties(); mConfigIndex.reset(new VehiclePropConfigIndex(supportedPropConfigs)); std::vector<int32_t> supportedProperties( supportedPropConfigs.size()); for (const auto& config : supportedPropConfigs) { supportedProperties.push_back(config.prop); } } VehicleHalManager::~VehicleHalManager() { mBatchingConsumer.requestStop(); mEventQueue.deactivate(); // We have to wait until consumer thread is fully stopped because it may // be in a state of running callback (onBatchHalEvent). mBatchingConsumer.waitStopped(); ALOGI("VehicleHalManager::dtor"); } void VehicleHalManager::onHalEvent(VehiclePropValuePtr v) { mEventQueue.push(std::move(v)); } void VehicleHalManager::onHalPropertySetError(StatusCode errorCode, int32_t property, int32_t areaId) { const auto& clients = mSubscriptionManager.getSubscribedClients( property, 0, SubscribeFlags::HAL_EVENT); for (auto client : clients) { client->getCallback()->onPropertySetError(errorCode, property, areaId); } } void VehicleHalManager::onBatchHalEvent(const std::vector<VehiclePropValuePtr>& values) { const auto& clientValues = mSubscriptionManager.distributeValuesToClients( values, SubscribeFlags::HAL_EVENT); for (const HalClientValues& cv : clientValues) { auto vecSize = cv.values.size(); hidl_vec<VehiclePropValue> vec; if (vecSize < kMaxHidlVecOfVehiclPropValuePoolSize) { vec.setToExternal(&mHidlVecOfVehiclePropValuePool[0], vecSize); } else { vec.resize(vecSize); } int i = 0; for (VehiclePropValue* pValue : cv.values) { shallowCopy(&(vec)[i++], *pValue); } auto status = cv.client->getCallback()->onPropertyEvent(vec); if (!status.isOk()) { ALOGE("Failed to notify client %s, err: %s", toString(cv.client->getCallback()).c_str(), status.description().c_str()); } } } bool VehicleHalManager::isSampleRateFixed(VehiclePropertyChangeMode mode) { return (mode & VehiclePropertyChangeMode::ON_SET) || (mode & VehiclePropertyChangeMode::ON_CHANGE); } float VehicleHalManager::checkSampleRate(const VehiclePropConfig &config, float sampleRate) { if (isSampleRateFixed(config.changeMode)) { if (std::abs(sampleRate) > std::numeric_limits<float>::epsilon()) { ALOGW("Sample rate is greater than zero for on change type. " "Ignoring it."); } return 0.0; } else { if (sampleRate > config.maxSampleRate) { ALOGW("Sample rate %f is higher than max %f. Setting sampling rate " "to max.", sampleRate, config.maxSampleRate); return config.maxSampleRate; } if (sampleRate < config.minSampleRate) { ALOGW("Sample rate %f is lower than min %f. Setting sampling rate " "to min.", sampleRate, config.minSampleRate); return config.minSampleRate; } } return sampleRate; // Provided sample rate was good, no changes. } bool VehicleHalManager::isSubscribable(const VehiclePropConfig& config, SubscribeFlags flags) { bool isReadable = config.access & VehiclePropertyAccess::READ; if (!isReadable && (SubscribeFlags::HAL_EVENT & flags)) { ALOGW("Cannot subscribe, property 0x%x is not readable", config.prop); return false; } if (config.changeMode == VehiclePropertyChangeMode::STATIC) { ALOGW("Cannot subscribe, property 0x%x is static", config.prop); return false; } //TODO: extend to support event notification for set from android if (config.changeMode == VehiclePropertyChangeMode::POLL) { ALOGW("Cannot subscribe, property 0x%x is poll only", config.prop); return false; } return true; } bool VehicleHalManager::checkWritePermission(const VehiclePropConfig &config) const { if (!(config.access & VehiclePropertyAccess::WRITE)) { ALOGW("Property 0%x has no write access", config.prop); return false; } else { return true; } } bool VehicleHalManager::checkReadPermission(const VehiclePropConfig &config) const { if (!(config.access & VehiclePropertyAccess::READ)) { ALOGW("Property 0%x has no read access", config.prop); return false; } else { return true; } } void VehicleHalManager::handlePropertySetEvent(const VehiclePropValue& value) { auto clients = mSubscriptionManager.getSubscribedClients( value.prop, value.areaId, SubscribeFlags::SET_CALL); for (auto client : clients) { client->getCallback()->onPropertySet(value); } } const VehiclePropConfig* VehicleHalManager::getPropConfigOrNull( int32_t prop) const { return mConfigIndex->hasConfig(prop) ? &mConfigIndex->getConfig(prop) : nullptr; } void VehicleHalManager::onAllClientsUnsubscribed(int32_t propertyId) { mHal->unsubscribe(propertyId); } ClientId VehicleHalManager::getClientId(const sp<IVehicleCallback>& callback) { //TODO(b/32172906): rework this to get some kind of unique id for callback interface when this // feature is ready in HIDL. if (callback->isRemote()) { BpHwVehicleCallback* hwCallback = static_cast<BpHwVehicleCallback*>(callback.get()); return static_cast<ClientId>(reinterpret_cast<intptr_t>(hwCallback->onAsBinder())); } else { return static_cast<ClientId>(reinterpret_cast<intptr_t>(callback.get())); } } } // namespace V2_0 } // namespace vehicle } // namespace automotive } // namespace hardware } // namespace android