| /* |
| * 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. |
| */ |
| |
| #define LOG_TAG "DefaultVehicleHal" |
| |
| #include <DefaultVehicleHal.h> |
| |
| #include <LargeParcelableBase.h> |
| #include <VehicleHalTypes.h> |
| #include <VehicleUtils.h> |
| |
| #include <android-base/result.h> |
| #include <android-base/stringprintf.h> |
| #include <utils/Log.h> |
| #include <utils/SystemClock.h> |
| |
| #include <inttypes.h> |
| #include <set> |
| #include <unordered_set> |
| |
| namespace android { |
| namespace hardware { |
| namespace automotive { |
| namespace vehicle { |
| |
| namespace { |
| |
| using ::aidl::android::hardware::automotive::vehicle::GetValueRequest; |
| using ::aidl::android::hardware::automotive::vehicle::GetValueRequests; |
| using ::aidl::android::hardware::automotive::vehicle::GetValueResult; |
| using ::aidl::android::hardware::automotive::vehicle::GetValueResults; |
| using ::aidl::android::hardware::automotive::vehicle::SetValueRequest; |
| using ::aidl::android::hardware::automotive::vehicle::SetValueRequests; |
| using ::aidl::android::hardware::automotive::vehicle::SetValueResult; |
| using ::aidl::android::hardware::automotive::vehicle::SetValueResults; |
| using ::aidl::android::hardware::automotive::vehicle::StatusCode; |
| using ::aidl::android::hardware::automotive::vehicle::SubscribeOptions; |
| using ::aidl::android::hardware::automotive::vehicle::VehicleAreaConfig; |
| using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig; |
| using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfigs; |
| using ::aidl::android::hardware::automotive::vehicle::VehicleProperty; |
| using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyAccess; |
| using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyChangeMode; |
| using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyStatus; |
| using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue; |
| using ::android::automotive::car_binder_lib::LargeParcelableBase; |
| using ::android::base::Error; |
| using ::android::base::expected; |
| using ::android::base::Result; |
| using ::android::base::StringPrintf; |
| |
| using ::ndk::ScopedAIBinder_DeathRecipient; |
| using ::ndk::ScopedAStatus; |
| |
| std::string toString(const std::unordered_set<int64_t>& values) { |
| std::string str = ""; |
| for (auto it = values.begin(); it != values.end(); it++) { |
| str += std::to_string(*it); |
| if (std::next(it, 1) != values.end()) { |
| str += ", "; |
| } |
| } |
| return str; |
| } |
| |
| } // namespace |
| |
| std::shared_ptr<SubscriptionClient> DefaultVehicleHal::SubscriptionClients::getClient( |
| const CallbackType& callback) { |
| std::scoped_lock<std::mutex> lockGuard(mLock); |
| return getOrCreateClient(&mClients, callback, mPendingRequestPool); |
| } |
| |
| int64_t DefaultVehicleHal::SubscribeIdByClient::getId(const CallbackType& callback) { |
| std::scoped_lock<std::mutex> lockGuard(mLock); |
| // This would be initialized to 0 if callback does not exist in the map. |
| int64_t subscribeId = (mIds[callback->asBinder().get()])++; |
| return subscribeId; |
| } |
| |
| void DefaultVehicleHal::SubscriptionClients::removeClient(const AIBinder* clientId) { |
| std::scoped_lock<std::mutex> lockGuard(mLock); |
| mClients.erase(clientId); |
| } |
| |
| size_t DefaultVehicleHal::SubscriptionClients::countClients() { |
| std::scoped_lock<std::mutex> lockGuard(mLock); |
| return mClients.size(); |
| } |
| |
| DefaultVehicleHal::DefaultVehicleHal(std::unique_ptr<IVehicleHardware> hardware) |
| : mVehicleHardware(std::move(hardware)), |
| mPendingRequestPool(std::make_shared<PendingRequestPool>(TIMEOUT_IN_NANO)) { |
| auto configs = mVehicleHardware->getAllPropertyConfigs(); |
| for (auto& config : configs) { |
| mConfigsByPropId[config.prop] = config; |
| } |
| VehiclePropConfigs vehiclePropConfigs; |
| vehiclePropConfigs.payloads = std::move(configs); |
| auto result = LargeParcelableBase::parcelableToStableLargeParcelable(vehiclePropConfigs); |
| if (!result.ok()) { |
| ALOGE("failed to convert configs to shared memory file, error: %s, code: %d", |
| getErrorMsg(result).c_str(), getIntErrorCode(result)); |
| return; |
| } |
| |
| if (result.value() != nullptr) { |
| mConfigFile = std::move(result.value()); |
| } |
| |
| mSubscriptionClients = std::make_shared<SubscriptionClients>(mPendingRequestPool); |
| |
| auto subscribeIdByClient = std::make_shared<SubscribeIdByClient>(); |
| // Make a weak copy of IVehicleHardware because subscriptionManager uses IVehicleHardware and |
| // IVehicleHardware uses subscriptionManager. We want to avoid cyclic reference. |
| std::weak_ptr<IVehicleHardware> hardwareCopy = mVehicleHardware; |
| SubscriptionManager::GetValueFunc getValueFunc = std::bind( |
| &DefaultVehicleHal::getValueFromHardwareCallCallback, hardwareCopy, subscribeIdByClient, |
| mSubscriptionClients, std::placeholders::_1, std::placeholders::_2); |
| mSubscriptionManager = std::make_shared<SubscriptionManager>(std::move(getValueFunc)); |
| |
| std::weak_ptr<SubscriptionManager> subscriptionManagerCopy = mSubscriptionManager; |
| mVehicleHardware->registerOnPropertyChangeEvent( |
| std::make_unique<IVehicleHardware::PropertyChangeCallback>( |
| [subscriptionManagerCopy](std::vector<VehiclePropValue> updatedValues) { |
| onPropertyChangeEvent(subscriptionManagerCopy, updatedValues); |
| })); |
| |
| // Register heartbeat event. |
| mRecurrentTimer.registerTimerCallback( |
| HEART_BEAT_INTERVAL_IN_NANO, |
| std::make_shared<std::function<void()>>([hardwareCopy, subscriptionManagerCopy]() { |
| checkHealth(hardwareCopy, subscriptionManagerCopy); |
| })); |
| |
| mLinkToDeathImpl = std::make_unique<AIBinderLinkToDeathImpl>(); |
| mDeathRecipient = ScopedAIBinder_DeathRecipient( |
| AIBinder_DeathRecipient_new(&DefaultVehicleHal::onBinderDied)); |
| AIBinder_DeathRecipient_setOnUnlinked(mDeathRecipient.get(), |
| &DefaultVehicleHal::onBinderUnlinked); |
| } |
| |
| DefaultVehicleHal::~DefaultVehicleHal() { |
| // Delete the deathRecipient so that onBinderDied would not be called to reference 'this'. |
| mDeathRecipient = ScopedAIBinder_DeathRecipient(); |
| } |
| |
| void DefaultVehicleHal::onPropertyChangeEvent( |
| std::weak_ptr<SubscriptionManager> subscriptionManager, |
| const std::vector<VehiclePropValue>& updatedValues) { |
| auto manager = subscriptionManager.lock(); |
| if (manager == nullptr) { |
| ALOGW("the SubscriptionManager is destroyed, DefaultVehicleHal is ending"); |
| return; |
| } |
| auto updatedValuesByClients = manager->getSubscribedClients(updatedValues); |
| for (const auto& [callback, valuePtrs] : updatedValuesByClients) { |
| std::vector<VehiclePropValue> values; |
| for (const VehiclePropValue* valuePtr : valuePtrs) { |
| values.push_back(*valuePtr); |
| } |
| SubscriptionClient::sendUpdatedValues(callback, std::move(values)); |
| } |
| } |
| |
| template <class T> |
| std::shared_ptr<T> DefaultVehicleHal::getOrCreateClient( |
| std::unordered_map<const AIBinder*, std::shared_ptr<T>>* clients, |
| const CallbackType& callback, std::shared_ptr<PendingRequestPool> pendingRequestPool) { |
| const AIBinder* clientId = callback->asBinder().get(); |
| if (clients->find(clientId) == clients->end()) { |
| (*clients)[clientId] = std::make_shared<T>(pendingRequestPool, callback); |
| } |
| return (*clients)[clientId]; |
| } |
| |
| void DefaultVehicleHal::monitorBinderLifeCycle(const CallbackType& callback) { |
| AIBinder* clientId = callback->asBinder().get(); |
| { |
| std::scoped_lock<std::mutex> lockGuard(mLock); |
| if (mOnBinderDiedContexts.find(clientId) != mOnBinderDiedContexts.end()) { |
| // Already registered. |
| return; |
| } |
| } |
| |
| std::unique_ptr<OnBinderDiedContext> context = std::make_unique<OnBinderDiedContext>( |
| OnBinderDiedContext{.vhal = this, .clientId = clientId}); |
| binder_status_t status = mLinkToDeathImpl->linkToDeath(clientId, mDeathRecipient.get(), |
| static_cast<void*>(context.get())); |
| if (status == STATUS_OK) { |
| std::scoped_lock<std::mutex> lockGuard(mLock); |
| // Insert into a map to keep the context object alive. |
| mOnBinderDiedContexts[clientId] = std::move(context); |
| } else { |
| ALOGE("failed to call linkToDeath on client binder, status: %d", static_cast<int>(status)); |
| } |
| } |
| |
| void DefaultVehicleHal::onBinderDied(void* cookie) { |
| OnBinderDiedContext* context = reinterpret_cast<OnBinderDiedContext*>(cookie); |
| context->vhal->onBinderDiedWithContext(context->clientId); |
| } |
| |
| void DefaultVehicleHal::onBinderDiedWithContext(const AIBinder* clientId) { |
| std::scoped_lock<std::mutex> lockGuard(mLock); |
| mSetValuesClients.erase(clientId); |
| mGetValuesClients.erase(clientId); |
| mSubscriptionClients->removeClient(clientId); |
| mSubscriptionManager->unsubscribe(clientId); |
| } |
| |
| void DefaultVehicleHal::onBinderUnlinked(void* cookie) { |
| // Delete the context associated with this cookie. |
| OnBinderDiedContext* context = reinterpret_cast<OnBinderDiedContext*>(cookie); |
| context->vhal->onBinderUnlinkedWithContext(context->clientId); |
| } |
| |
| void DefaultVehicleHal::onBinderUnlinkedWithContext(const AIBinder* clientId) { |
| std::scoped_lock<std::mutex> lockGuard(mLock); |
| mOnBinderDiedContexts.erase(clientId); |
| } |
| |
| template std::shared_ptr<DefaultVehicleHal::GetValuesClient> |
| DefaultVehicleHal::getOrCreateClient<DefaultVehicleHal::GetValuesClient>( |
| std::unordered_map<const AIBinder*, std::shared_ptr<GetValuesClient>>* clients, |
| const CallbackType& callback, std::shared_ptr<PendingRequestPool> pendingRequestPool); |
| template std::shared_ptr<DefaultVehicleHal::SetValuesClient> |
| DefaultVehicleHal::getOrCreateClient<DefaultVehicleHal::SetValuesClient>( |
| std::unordered_map<const AIBinder*, std::shared_ptr<SetValuesClient>>* clients, |
| const CallbackType& callback, std::shared_ptr<PendingRequestPool> pendingRequestPool); |
| template std::shared_ptr<SubscriptionClient> |
| DefaultVehicleHal::getOrCreateClient<SubscriptionClient>( |
| std::unordered_map<const AIBinder*, std::shared_ptr<SubscriptionClient>>* clients, |
| const CallbackType& callback, std::shared_ptr<PendingRequestPool> pendingRequestPool); |
| |
| void DefaultVehicleHal::getValueFromHardwareCallCallback( |
| std::weak_ptr<IVehicleHardware> vehicleHardware, |
| std::shared_ptr<SubscribeIdByClient> subscribeIdByClient, |
| std::shared_ptr<SubscriptionClients> subscriptionClients, const CallbackType& callback, |
| const VehiclePropValue& value) { |
| int64_t subscribeId = subscribeIdByClient->getId(callback); |
| auto client = subscriptionClients->getClient(callback); |
| if (auto addRequestResult = client->addRequests({subscribeId}); !addRequestResult.ok()) { |
| ALOGE("subscribe[%" PRId64 "]: too many pending requests, ignore the getValue request", |
| subscribeId); |
| return; |
| } |
| |
| std::vector<GetValueRequest> hardwareRequests = {{ |
| .requestId = subscribeId, |
| .prop = value, |
| }}; |
| |
| std::shared_ptr<IVehicleHardware> hardware = vehicleHardware.lock(); |
| if (hardware == nullptr) { |
| ALOGW("the IVehicleHardware is destroyed, DefaultVehicleHal is ending"); |
| return; |
| } |
| if (StatusCode status = hardware->getValues(client->getResultCallback(), hardwareRequests); |
| status != StatusCode::OK) { |
| // If the hardware returns error, finish all the pending requests for this request because |
| // we never expect hardware to call callback for these requests. |
| client->tryFinishRequests({subscribeId}); |
| ALOGE("subscribe[%" PRId64 "]: failed to get value from VehicleHardware, code: %d", |
| subscribeId, toInt(status)); |
| } |
| } |
| |
| void DefaultVehicleHal::setTimeout(int64_t timeoutInNano) { |
| mPendingRequestPool = std::make_unique<PendingRequestPool>(timeoutInNano); |
| } |
| |
| ScopedAStatus DefaultVehicleHal::getAllPropConfigs(VehiclePropConfigs* output) { |
| if (mConfigFile != nullptr) { |
| output->payloads.clear(); |
| output->sharedMemoryFd.set(dup(mConfigFile->get())); |
| return ScopedAStatus::ok(); |
| } |
| output->payloads.reserve(mConfigsByPropId.size()); |
| for (const auto& [_, config] : mConfigsByPropId) { |
| output->payloads.push_back(config); |
| } |
| return ScopedAStatus::ok(); |
| } |
| |
| Result<const VehiclePropConfig*> DefaultVehicleHal::getConfig(int32_t propId) const { |
| auto it = mConfigsByPropId.find(propId); |
| if (it == mConfigsByPropId.end()) { |
| return Error() << "no config for property, ID: " << propId; |
| } |
| return &(it->second); |
| } |
| |
| Result<void> DefaultVehicleHal::checkProperty(const VehiclePropValue& propValue) { |
| int32_t propId = propValue.prop; |
| auto result = getConfig(propId); |
| if (!result.ok()) { |
| return result.error(); |
| } |
| const VehiclePropConfig* config = result.value(); |
| const VehicleAreaConfig* areaConfig = getAreaConfig(propValue, *config); |
| if (!isGlobalProp(propId) && areaConfig == nullptr) { |
| // Ignore areaId for global property. For non global property, check whether areaId is |
| // allowed. areaId must appear in areaConfig. |
| return Error() << "invalid area ID: " << propValue.areaId << " for prop ID: " << propId |
| << ", not listed in config"; |
| } |
| if (auto result = checkPropValue(propValue, config); !result.ok()) { |
| return Error() << "invalid property value: " << propValue.toString() |
| << ", error: " << getErrorMsg(result); |
| } |
| if (auto result = checkValueRange(propValue, areaConfig); !result.ok()) { |
| return Error() << "property value out of range: " << propValue.toString() |
| << ", error: " << getErrorMsg(result); |
| } |
| return {}; |
| } |
| |
| ScopedAStatus DefaultVehicleHal::getValues(const CallbackType& callback, |
| const GetValueRequests& requests) { |
| monitorBinderLifeCycle(callback); |
| |
| expected<LargeParcelableBase::BorrowedOwnedObject<GetValueRequests>, ScopedAStatus> |
| deserializedResults = fromStableLargeParcelable(requests); |
| if (!deserializedResults.ok()) { |
| ALOGE("getValues: failed to parse getValues requests"); |
| return std::move(deserializedResults.error()); |
| } |
| const std::vector<GetValueRequest>& getValueRequests = |
| deserializedResults.value().getObject()->payloads; |
| |
| auto maybeRequestIds = checkDuplicateRequests(getValueRequests); |
| if (!maybeRequestIds.ok()) { |
| ALOGE("getValues: duplicate request ID"); |
| return toScopedAStatus(maybeRequestIds, StatusCode::INVALID_ARG); |
| } |
| |
| // A list of failed result we already know before sending to hardware. |
| std::vector<GetValueResult> failedResults; |
| // The list of requests that we would send to hardware. |
| std::vector<GetValueRequest> hardwareRequests; |
| |
| for (const auto& request : getValueRequests) { |
| if (auto result = checkReadPermission(request.prop); !result.ok()) { |
| ALOGW("property does not support reading: %s", getErrorMsg(result).c_str()); |
| failedResults.push_back(GetValueResult{ |
| .requestId = request.requestId, |
| .status = getErrorCode(result), |
| .prop = {}, |
| }); |
| } else { |
| hardwareRequests.push_back(request); |
| } |
| } |
| |
| // The set of request Ids that we would send to hardware. |
| std::unordered_set<int64_t> hardwareRequestIds; |
| for (const auto& request : hardwareRequests) { |
| hardwareRequestIds.insert(request.requestId); |
| } |
| |
| std::shared_ptr<GetValuesClient> client; |
| { |
| std::scoped_lock<std::mutex> lockGuard(mLock); |
| client = getOrCreateClient(&mGetValuesClients, callback, mPendingRequestPool); |
| } |
| // Register the pending hardware requests and also check for duplicate request Ids. |
| if (auto addRequestResult = client->addRequests(hardwareRequestIds); !addRequestResult.ok()) { |
| ALOGE("getValues[%s]: failed to add pending requests, error: %s", |
| toString(hardwareRequestIds).c_str(), getErrorMsg(addRequestResult).c_str()); |
| return toScopedAStatus(addRequestResult); |
| } |
| |
| if (!failedResults.empty()) { |
| // First send the failed results we already know back to the client. |
| client->sendResults(failedResults); |
| } |
| |
| if (hardwareRequests.empty()) { |
| return ScopedAStatus::ok(); |
| } |
| |
| if (StatusCode status = |
| mVehicleHardware->getValues(client->getResultCallback(), hardwareRequests); |
| status != StatusCode::OK) { |
| // If the hardware returns error, finish all the pending requests for this request because |
| // we never expect hardware to call callback for these requests. |
| client->tryFinishRequests(hardwareRequestIds); |
| ALOGE("getValues[%s]: failed to get value from VehicleHardware, status: %d", |
| toString(hardwareRequestIds).c_str(), toInt(status)); |
| return ScopedAStatus::fromServiceSpecificErrorWithMessage( |
| toInt(status), "failed to get value from VehicleHardware"); |
| } |
| return ScopedAStatus::ok(); |
| } |
| |
| ScopedAStatus DefaultVehicleHal::setValues(const CallbackType& callback, |
| const SetValueRequests& requests) { |
| monitorBinderLifeCycle(callback); |
| |
| expected<LargeParcelableBase::BorrowedOwnedObject<SetValueRequests>, ScopedAStatus> |
| deserializedResults = fromStableLargeParcelable(requests); |
| if (!deserializedResults.ok()) { |
| ALOGE("setValues: failed to parse setValues requests"); |
| return std::move(deserializedResults.error()); |
| } |
| const std::vector<SetValueRequest>& setValueRequests = |
| deserializedResults.value().getObject()->payloads; |
| |
| // A list of failed result we already know before sending to hardware. |
| std::vector<SetValueResult> failedResults; |
| // The list of requests that we would send to hardware. |
| std::vector<SetValueRequest> hardwareRequests; |
| |
| auto maybeRequestIds = checkDuplicateRequests(setValueRequests); |
| if (!maybeRequestIds.ok()) { |
| ALOGE("setValues: duplicate request ID"); |
| return toScopedAStatus(maybeRequestIds, StatusCode::INVALID_ARG); |
| } |
| |
| for (auto& request : setValueRequests) { |
| int64_t requestId = request.requestId; |
| if (auto result = checkWritePermission(request.value); !result.ok()) { |
| ALOGW("property does not support writing: %s", getErrorMsg(result).c_str()); |
| failedResults.push_back(SetValueResult{ |
| .requestId = requestId, |
| .status = getErrorCode(result), |
| }); |
| continue; |
| } |
| if (auto result = checkProperty(request.value); !result.ok()) { |
| ALOGW("setValues[%" PRId64 "]: property is not valid: %s", requestId, |
| getErrorMsg(result).c_str()); |
| failedResults.push_back(SetValueResult{ |
| .requestId = requestId, |
| .status = StatusCode::INVALID_ARG, |
| }); |
| continue; |
| } |
| |
| hardwareRequests.push_back(request); |
| } |
| |
| // The set of request Ids that we would send to hardware. |
| std::unordered_set<int64_t> hardwareRequestIds; |
| for (const auto& request : hardwareRequests) { |
| hardwareRequestIds.insert(request.requestId); |
| } |
| |
| std::shared_ptr<SetValuesClient> client; |
| { |
| std::scoped_lock<std::mutex> lockGuard(mLock); |
| client = getOrCreateClient(&mSetValuesClients, callback, mPendingRequestPool); |
| } |
| |
| // Register the pending hardware requests and also check for duplicate request Ids. |
| if (auto addRequestResult = client->addRequests(hardwareRequestIds); !addRequestResult.ok()) { |
| ALOGE("setValues[%s], failed to add pending requests, error: %s", |
| toString(hardwareRequestIds).c_str(), getErrorMsg(addRequestResult).c_str()); |
| return toScopedAStatus(addRequestResult, StatusCode::INVALID_ARG); |
| } |
| |
| if (!failedResults.empty()) { |
| // First send the failed results we already know back to the client. |
| client->sendResults(failedResults); |
| } |
| |
| if (hardwareRequests.empty()) { |
| return ScopedAStatus::ok(); |
| } |
| |
| if (StatusCode status = |
| mVehicleHardware->setValues(client->getResultCallback(), hardwareRequests); |
| status != StatusCode::OK) { |
| // If the hardware returns error, finish all the pending requests for this request because |
| // we never expect hardware to call callback for these requests. |
| client->tryFinishRequests(hardwareRequestIds); |
| ALOGE("setValues[%s], failed to set value to VehicleHardware, status: %d", |
| toString(hardwareRequestIds).c_str(), toInt(status)); |
| return ScopedAStatus::fromServiceSpecificErrorWithMessage( |
| toInt(status), "failed to set value to VehicleHardware"); |
| } |
| |
| return ScopedAStatus::ok(); |
| } |
| |
| #define CHECK_DUPLICATE_REQUESTS(PROP_NAME) \ |
| do { \ |
| std::vector<int64_t> requestIds; \ |
| std::set<::aidl::android::hardware::automotive::vehicle::VehiclePropValue> requestProps; \ |
| for (const auto& request : requests) { \ |
| const auto& prop = request.PROP_NAME; \ |
| if (requestProps.count(prop) != 0) { \ |
| return ::android::base::Error() \ |
| << "duplicate request for property: " << prop.toString(); \ |
| } \ |
| requestProps.insert(prop); \ |
| requestIds.push_back(request.requestId); \ |
| } \ |
| return requestIds; \ |
| } while (0); |
| |
| ::android::base::Result<std::vector<int64_t>> DefaultVehicleHal::checkDuplicateRequests( |
| const std::vector<GetValueRequest>& requests) { |
| CHECK_DUPLICATE_REQUESTS(prop); |
| } |
| |
| ::android::base::Result<std::vector<int64_t>> DefaultVehicleHal::checkDuplicateRequests( |
| const std::vector<SetValueRequest>& requests) { |
| CHECK_DUPLICATE_REQUESTS(value); |
| } |
| |
| #undef CHECK_DUPLICATE_REQUESTS |
| |
| ScopedAStatus DefaultVehicleHal::getPropConfigs(const std::vector<int32_t>& props, |
| VehiclePropConfigs* output) { |
| std::vector<VehiclePropConfig> configs; |
| for (int32_t prop : props) { |
| if (mConfigsByPropId.find(prop) != mConfigsByPropId.end()) { |
| configs.push_back(mConfigsByPropId[prop]); |
| } |
| } |
| return vectorToStableLargeParcelable(std::move(configs), output); |
| } |
| |
| Result<void> DefaultVehicleHal::checkSubscribeOptions( |
| const std::vector<SubscribeOptions>& options) { |
| for (const auto& option : options) { |
| int32_t propId = option.propId; |
| if (mConfigsByPropId.find(propId) == mConfigsByPropId.end()) { |
| return Error(toInt(StatusCode::INVALID_ARG)) |
| << StringPrintf("no config for property, ID: %" PRId32, propId); |
| } |
| const VehiclePropConfig& config = mConfigsByPropId[propId]; |
| |
| if (config.changeMode != VehiclePropertyChangeMode::ON_CHANGE && |
| config.changeMode != VehiclePropertyChangeMode::CONTINUOUS) { |
| return Error(toInt(StatusCode::INVALID_ARG)) |
| << "only support subscribing to ON_CHANGE or CONTINUOUS property"; |
| } |
| |
| if (config.access != VehiclePropertyAccess::READ && |
| config.access != VehiclePropertyAccess::READ_WRITE) { |
| return Error(toInt(StatusCode::ACCESS_DENIED)) |
| << StringPrintf("Property %" PRId32 " has no read access", propId); |
| } |
| |
| if (config.changeMode == VehiclePropertyChangeMode::CONTINUOUS) { |
| float sampleRate = option.sampleRate; |
| float minSampleRate = config.minSampleRate; |
| float maxSampleRate = config.maxSampleRate; |
| if (sampleRate < minSampleRate || sampleRate > maxSampleRate) { |
| return Error(toInt(StatusCode::INVALID_ARG)) |
| << StringPrintf("sample rate: %f out of range, must be within %f and %f", |
| sampleRate, minSampleRate, maxSampleRate); |
| } |
| if (!SubscriptionManager::checkSampleRate(sampleRate)) { |
| return Error(toInt(StatusCode::INVALID_ARG)) |
| << "invalid sample rate: " << sampleRate; |
| } |
| } |
| |
| if (isGlobalProp(propId)) { |
| continue; |
| } |
| |
| // Non-global property. |
| for (int32_t areaId : option.areaIds) { |
| if (auto areaConfig = getAreaConfig(propId, areaId, config); areaConfig == nullptr) { |
| return Error(toInt(StatusCode::INVALID_ARG)) |
| << StringPrintf("invalid area ID: %" PRId32 " for prop ID: %" PRId32 |
| ", not listed in config", |
| areaId, propId); |
| } |
| } |
| } |
| return {}; |
| } |
| |
| ScopedAStatus DefaultVehicleHal::subscribe(const CallbackType& callback, |
| const std::vector<SubscribeOptions>& options, |
| [[maybe_unused]] int32_t maxSharedMemoryFileCount) { |
| monitorBinderLifeCycle(callback); |
| |
| // TODO(b/205189110): Use shared memory file count. |
| if (auto result = checkSubscribeOptions(options); !result.ok()) { |
| ALOGE("subscribe: invalid subscribe options: %s", getErrorMsg(result).c_str()); |
| return toScopedAStatus(result); |
| } |
| |
| std::vector<SubscribeOptions> onChangeSubscriptions; |
| std::vector<SubscribeOptions> continuousSubscriptions; |
| for (const auto& option : options) { |
| int32_t propId = option.propId; |
| // We have already validate config exists. |
| const VehiclePropConfig& config = mConfigsByPropId[propId]; |
| |
| SubscribeOptions optionCopy = option; |
| // If areaIds is empty, subscribe to all areas. |
| if (optionCopy.areaIds.empty() && !isGlobalProp(propId)) { |
| for (const auto& areaConfig : config.areaConfigs) { |
| optionCopy.areaIds.push_back(areaConfig.areaId); |
| } |
| } |
| |
| if (isGlobalProp(propId)) { |
| optionCopy.areaIds = {0}; |
| } |
| |
| if (config.changeMode == VehiclePropertyChangeMode::CONTINUOUS) { |
| continuousSubscriptions.push_back(std::move(optionCopy)); |
| } else { |
| onChangeSubscriptions.push_back(std::move(optionCopy)); |
| } |
| } |
| // Since we have already check the sample rates, the following functions must succeed. |
| if (!onChangeSubscriptions.empty()) { |
| mSubscriptionManager->subscribe(callback, onChangeSubscriptions, |
| /*isContinuousProperty=*/false); |
| } |
| if (!continuousSubscriptions.empty()) { |
| mSubscriptionManager->subscribe(callback, continuousSubscriptions, |
| /*isContinuousProperty=*/true); |
| } |
| return ScopedAStatus::ok(); |
| } |
| |
| ScopedAStatus DefaultVehicleHal::unsubscribe(const CallbackType& callback, |
| const std::vector<int32_t>& propIds) { |
| return toScopedAStatus(mSubscriptionManager->unsubscribe(callback->asBinder().get(), propIds), |
| StatusCode::INVALID_ARG); |
| } |
| |
| ScopedAStatus DefaultVehicleHal::returnSharedMemory(const CallbackType&, int64_t) { |
| // TODO(b/200737967): implement this. |
| return ScopedAStatus::ok(); |
| } |
| |
| IVehicleHardware* DefaultVehicleHal::getHardware() { |
| return mVehicleHardware.get(); |
| } |
| |
| Result<void> DefaultVehicleHal::checkWritePermission(const VehiclePropValue& value) const { |
| int32_t propId = value.prop; |
| auto result = getConfig(propId); |
| if (!result.ok()) { |
| return Error(toInt(StatusCode::INVALID_ARG)) << getErrorMsg(result); |
| } |
| const VehiclePropConfig* config = result.value(); |
| |
| if (config->access != VehiclePropertyAccess::WRITE && |
| config->access != VehiclePropertyAccess::READ_WRITE) { |
| return Error(toInt(StatusCode::ACCESS_DENIED)) |
| << StringPrintf("Property %" PRId32 " has no write access", propId); |
| } |
| return {}; |
| } |
| |
| Result<void> DefaultVehicleHal::checkReadPermission(const VehiclePropValue& value) const { |
| int32_t propId = value.prop; |
| auto result = getConfig(propId); |
| if (!result.ok()) { |
| return Error(toInt(StatusCode::INVALID_ARG)) << getErrorMsg(result); |
| } |
| const VehiclePropConfig* config = result.value(); |
| |
| if (config->access != VehiclePropertyAccess::READ && |
| config->access != VehiclePropertyAccess::READ_WRITE) { |
| return Error(toInt(StatusCode::ACCESS_DENIED)) |
| << StringPrintf("Property %" PRId32 " has no read access", propId); |
| } |
| return {}; |
| } |
| |
| void DefaultVehicleHal::checkHealth(std::weak_ptr<IVehicleHardware> hardware, |
| std::weak_ptr<SubscriptionManager> subscriptionManager) { |
| auto hardwarePtr = hardware.lock(); |
| if (hardwarePtr == nullptr) { |
| ALOGW("the VehicleHardware is destroyed, DefaultVehicleHal is ending"); |
| return; |
| } |
| |
| StatusCode status = hardwarePtr->checkHealth(); |
| if (status != StatusCode::OK) { |
| ALOGE("VHAL check health returns non-okay status"); |
| return; |
| } |
| std::vector<VehiclePropValue> values = {{ |
| .prop = toInt(VehicleProperty::VHAL_HEARTBEAT), |
| .areaId = 0, |
| .status = VehiclePropertyStatus::AVAILABLE, |
| .value.int64Values = {uptimeMillis()}, |
| }}; |
| onPropertyChangeEvent(subscriptionManager, values); |
| return; |
| } |
| |
| binder_status_t DefaultVehicleHal::AIBinderLinkToDeathImpl::linkToDeath( |
| AIBinder* binder, AIBinder_DeathRecipient* recipient, void* cookie) { |
| return AIBinder_linkToDeath(binder, recipient, cookie); |
| } |
| |
| void DefaultVehicleHal::setLinkToDeathImpl(std::unique_ptr<ILinkToDeath> impl) { |
| mLinkToDeathImpl = std::move(impl); |
| } |
| |
| } // namespace vehicle |
| } // namespace automotive |
| } // namespace hardware |
| } // namespace android |