/* * Copyright (c) 2022 Project CHIP Authors * All rights reserved. * * 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 "InteractionModel.h" using namespace chip; using chip::app::AttributePathParams; using chip::app::CommandSender; using chip::app::ConcreteAttributePath; using chip::app::ConcreteCommandPath; using chip::app::ConcreteDataAttributePath; using chip::app::DataVersionFilter; using chip::app::EventHeader; using chip::app::EventPathParams; using chip::app::InteractionModelEngine; using chip::app::ReadClient; using chip::app::ReadPrepareParams; using chip::app::StatusIB; using chip::app::WriteClient; namespace chip { namespace test_utils { void BusyWaitMillis(uint16_t busyWaitForMs) { auto & clock = chip::System::SystemClock(); auto start = clock.GetMonotonicTimestamp(); chip::System::Clock::Milliseconds32 durationInMs(busyWaitForMs); while (clock.GetMonotonicTimestamp() - start < durationInMs) { // nothing to do. }; } } // namespace test_utils } // namespace chip CHIP_ERROR InteractionModel::ReadAttribute(const char * identity, EndpointId endpointId, ClusterId clusterId, AttributeId attributeId, bool fabricFiltered, const Optional & dataVersion) { DeviceProxy * device = GetDevice(identity); VerifyOrReturnError(device != nullptr, CHIP_ERROR_INCORRECT_STATE); std::vector endpointIds = { endpointId }; std::vector clusterIds = { clusterId }; std::vector attributeIds = { attributeId }; Optional> dataVersions = Optional>(); if (dataVersion.HasValue()) { dataVersions.Value().push_back(dataVersion.Value()); } InteractionModelReports::ResetOptions(); InteractionModelReports::SetFabricFiltered(fabricFiltered); InteractionModelReports::SetDataVersions(dataVersions); return InteractionModelReports::ReadAttribute(device, endpointIds, clusterIds, attributeIds); } CHIP_ERROR InteractionModel::ReadEvent(const char * identity, EndpointId endpointId, ClusterId clusterId, EventId eventId, bool fabricFiltered, const Optional & eventNumber) { DeviceProxy * device = GetDevice(identity); VerifyOrReturnError(device != nullptr, CHIP_ERROR_INCORRECT_STATE); std::vector endpointIds = { endpointId }; std::vector clusterIds = { clusterId }; std::vector eventIds = { eventId }; InteractionModelReports::ResetOptions(); InteractionModelReports::SetFabricFiltered(fabricFiltered); InteractionModelReports::SetEventNumber(eventNumber); return InteractionModelReports::ReadEvent(device, endpointIds, clusterIds, eventIds); } CHIP_ERROR InteractionModel::SubscribeAttribute(const char * identity, EndpointId endpointId, ClusterId clusterId, AttributeId attributeId, uint16_t minInterval, uint16_t maxInterval, bool fabricFiltered, const Optional & dataVersion, const Optional & keepSubscriptions, const Optional & autoResubscribe) { DeviceProxy * device = GetDevice(identity); VerifyOrReturnError(device != nullptr, CHIP_ERROR_INCORRECT_STATE); std::vector endpointIds = { endpointId }; std::vector clusterIds = { clusterId }; std::vector attributeIds = { attributeId }; Optional> dataVersions = Optional>(); if (dataVersion.HasValue()) { dataVersions.Value().push_back(dataVersion.Value()); } InteractionModelReports::ResetOptions(); InteractionModelReports::SetMinInterval(minInterval); InteractionModelReports::SetMaxInterval(maxInterval); InteractionModelReports::SetFabricFiltered(fabricFiltered); InteractionModelReports::SetDataVersions(dataVersions); InteractionModelReports::SetKeepSubscriptions(keepSubscriptions); InteractionModelReports::SetAutoResubscribe(autoResubscribe); return InteractionModelReports::SubscribeAttribute(device, endpointIds, clusterIds, attributeIds); } CHIP_ERROR InteractionModel::SubscribeEvent(const char * identity, EndpointId endpointId, ClusterId clusterId, EventId eventId, uint16_t minInterval, uint16_t maxInterval, bool fabricFiltered, const Optional & eventNumber, const Optional & keepSubscriptions, const Optional & autoResubscribe) { DeviceProxy * device = GetDevice(identity); VerifyOrReturnError(device != nullptr, CHIP_ERROR_INCORRECT_STATE); std::vector endpointIds = { endpointId }; std::vector clusterIds = { clusterId }; std::vector eventIds = { eventId }; InteractionModelReports::ResetOptions(); SetMinInterval(minInterval); SetMaxInterval(maxInterval); SetFabricFiltered(fabricFiltered); SetEventNumber(eventNumber); SetKeepSubscriptions(keepSubscriptions); SetAutoResubscribe(autoResubscribe); return InteractionModelReports::SubscribeEvent(device, endpointIds, clusterIds, eventIds); } void InteractionModel::Shutdown() { InteractionModelReports::Shutdown(); InteractionModelWriter::Shutdown(); InteractionModelCommands::Shutdown(); } /////////// ReadClient Callback Interface ///////// void InteractionModel::OnAttributeData(const ConcreteDataAttributePath & path, TLV::TLVReader * data, const StatusIB & status) { OnResponse(status, data); } void InteractionModel::OnEventData(const EventHeader & eventHeader, TLV::TLVReader * data, const StatusIB * status) { OnResponse(status == nullptr ? StatusIB() : *status, data); } void InteractionModel::OnError(CHIP_ERROR error) { StatusIB status(error); OnResponse(status, nullptr); } void InteractionModel::OnDone(ReadClient * aReadClient) { InteractionModelReports::CleanupReadClient(aReadClient); ContinueOnChipMainThread(CHIP_NO_ERROR); } void InteractionModel::OnDeallocatePaths(chip::app::ReadPrepareParams && aReadPrepareParams) { InteractionModelReports::OnDeallocatePaths(std::move(aReadPrepareParams)); } void InteractionModel::OnSubscriptionEstablished(SubscriptionId subscriptionId) { ContinueOnChipMainThread(CHIP_NO_ERROR); } /////////// WriteClient Callback Interface ///////// void InteractionModel::OnResponse(const WriteClient * client, const ConcreteDataAttributePath & path, StatusIB status) { OnResponse(status, nullptr); } void InteractionModel::OnError(const WriteClient * client, CHIP_ERROR error) { StatusIB status(error); OnResponse(status, nullptr); } void InteractionModel::OnDone(WriteClient * client) { mWriteClient.reset(); ContinueOnChipMainThread(CHIP_NO_ERROR); } /////////// CommandSender Callback Interface ///////// void InteractionModel::OnResponse(CommandSender * client, const ConcreteCommandPath & path, const StatusIB & status, TLV::TLVReader * data) { OnResponse(status, data); } void InteractionModel::OnError(const CommandSender * client, CHIP_ERROR error) { StatusIB status(error); OnResponse(status, nullptr); } void InteractionModel::OnDone(CommandSender * client) { if (mCommandSender.size()) { mCommandSender.front().reset(); mCommandSender.erase(mCommandSender.begin()); } // If the command is repeated N times, wait for all the responses to comes in // before exiting. if (!mCommandSender.size()) { ContinueOnChipMainThread(CHIP_NO_ERROR); } } CHIP_ERROR InteractionModelConfig::GetAttributePaths(std::vector endpointIds, std::vector clusterIds, std::vector attributeIds, const Optional> & dataVersions, AttributePathsConfig & pathsConfig) { const size_t endpointCount = endpointIds.size(); const size_t clusterCount = clusterIds.size(); const size_t attributeCount = attributeIds.size(); const size_t dataVersionsCount = dataVersions.HasValue() ? dataVersions.Value().size() : 0; VerifyOrReturnError(clusterCount > 0 && clusterCount <= kMaxAllowedPaths, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(attributeCount > 0 && attributeCount <= kMaxAllowedPaths, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(endpointCount > 0 && endpointCount <= kMaxAllowedPaths, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(dataVersionsCount <= kMaxAllowedPaths, CHIP_ERROR_INVALID_ARGUMENT); const bool hasSameIdsCount = (clusterCount == attributeCount) && (clusterCount == endpointCount) && (dataVersionsCount == 0 || clusterCount == dataVersionsCount); const bool multipleClusters = clusterCount > 1 && attributeCount == 1 && endpointCount == 1 && (dataVersionsCount == 0 || dataVersionsCount == 1); const bool multipleAttributes = attributeCount > 1 && clusterCount == 1 && endpointCount == 1 && (dataVersionsCount == 0 || dataVersionsCount == 1); const bool multipleEndpoints = endpointCount > 1 && clusterCount == 1 && attributeCount == 1 && (dataVersionsCount == 0 || dataVersionsCount == 1); const bool multipleDataVersions = dataVersionsCount > 1 && clusterCount == 1 && attributeCount == 1 && endpointCount == 1; size_t pathsCount = 0; if (hasSameIdsCount) { pathsCount = clusterCount; } else if (multipleClusters) { pathsCount = clusterCount; } else if (multipleAttributes) { pathsCount = attributeCount; } else if (multipleEndpoints) { pathsCount = endpointCount; } else if (multipleDataVersions) { pathsCount = dataVersionsCount; } else { ChipLogError(chipTool, "\nCommand targetting multiple paths needs to have: \n \t * One element with multiple ids (for " "example 1 cluster id, 1 attribute id, 2 endpoint ids)\n\t * Or the same " "number of ids (for examples 2 cluster ids, 2 attribute ids and 2 endpoint ids).\n The current command has %u " "cluster ids, %u attribute ids, %u endpoint ids.", static_cast(clusterCount), static_cast(attributeCount), static_cast(endpointCount)); return CHIP_ERROR_INVALID_ARGUMENT; } pathsConfig.count = pathsCount; pathsConfig.attributePathParams = std::make_unique(pathsCount); pathsConfig.dataVersionFilter = std::make_unique(pathsCount); for (size_t i = 0; i < pathsCount; i++) { ClusterId clusterId = clusterIds.at((hasSameIdsCount || multipleClusters) ? i : 0); AttributeId attributeId = attributeIds.at((hasSameIdsCount || multipleAttributes) ? i : 0); EndpointId endpointId = endpointIds.at((hasSameIdsCount || multipleEndpoints) ? i : 0); ChipLogProgress(chipTool, "\tcluster " ChipLogFormatMEI ", attribute: " ChipLogFormatMEI ", endpoint %u", ChipLogValueMEI(clusterId), ChipLogValueMEI(attributeId), endpointId); if (clusterId != kInvalidClusterId) { pathsConfig.attributePathParams[i].mClusterId = clusterId; } if (attributeId != kInvalidAttributeId) { pathsConfig.attributePathParams[i].mAttributeId = attributeId; } if (endpointId != kInvalidEndpointId) { pathsConfig.attributePathParams[i].mEndpointId = endpointId; } if (dataVersions.HasValue()) { DataVersion dataVersion = dataVersions.Value().at((hasSameIdsCount || multipleDataVersions) ? i : 0); pathsConfig.dataVersionFilter[i].mEndpointId = endpointId; pathsConfig.dataVersionFilter[i].mClusterId = clusterId; pathsConfig.dataVersionFilter[i].mDataVersion.SetValue(dataVersion); } } return CHIP_NO_ERROR; } CHIP_ERROR InteractionModelReports::ReportAttribute(DeviceProxy * device, std::vector endpointIds, std::vector clusterIds, std::vector attributeIds, ReadClient::InteractionType interactionType) { ChipLogProgress(chipTool, "Sending %sAttribute to:", interactionType == ReadClient::InteractionType::Subscribe ? "Subscribe" : "Read"); InteractionModelConfig::AttributePathsConfig pathsConfig; ReturnErrorOnFailure( InteractionModelConfig::GetAttributePaths(endpointIds, clusterIds, attributeIds, mDataVersions, pathsConfig)); ReadPrepareParams params(device->GetSecureSession().Value()); params.mpEventPathParamsList = nullptr; params.mEventPathParamsListSize = 0; params.mpAttributePathParamsList = pathsConfig.attributePathParams.get(); params.mAttributePathParamsListSize = pathsConfig.count; if (mFabricFiltered.HasValue()) { params.mIsFabricFiltered = mFabricFiltered.Value(); } if (mDataVersions.HasValue()) { params.mpDataVersionFilterList = pathsConfig.dataVersionFilter.get(); params.mDataVersionFilterListSize = pathsConfig.count; } if (interactionType == ReadClient::InteractionType::Subscribe) { params.mMinIntervalFloorSeconds = mMinInterval; params.mMaxIntervalCeilingSeconds = mMaxInterval; if (mKeepSubscriptions.HasValue()) { params.mKeepSubscriptions = mKeepSubscriptions.Value(); } params.mIsPeerLIT = mIsPeerLIT; } auto client = std::make_unique(InteractionModelEngine::GetInstance(), device->GetExchangeManager(), mBufferedReadAdapter, interactionType); if (interactionType == ReadClient::InteractionType::Read) { ReturnErrorOnFailure(client->SendRequest(params)); } else if (mAutoResubscribe.ValueOr(false)) { pathsConfig.attributePathParams.release(); if (mDataVersions.HasValue()) { pathsConfig.dataVersionFilter.release(); } ReturnErrorOnFailure(client->SendAutoResubscribeRequest(std::move(params))); } else { // We want to allow certain kinds of spec-invalid subscriptions so we // can test how the server reacts to them. ReturnErrorOnFailure(client->SendSubscribeRequestWithoutValidation(params)); } mReadClients.push_back(std::move(client)); return CHIP_NO_ERROR; } CHIP_ERROR InteractionModelReports::ReportEvent(DeviceProxy * device, std::vector endpointIds, std::vector clusterIds, std::vector eventIds, chip::app::ReadClient::InteractionType interactionType) { const size_t clusterCount = clusterIds.size(); const size_t eventCount = eventIds.size(); const size_t endpointCount = endpointIds.size(); const size_t isUrgentCount = mIsUrgents.HasValue() ? mIsUrgents.Value().size() : 0; VerifyOrReturnError(clusterCount > 0 && clusterCount <= kMaxAllowedPaths, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(eventCount > 0 && eventCount <= kMaxAllowedPaths, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(endpointCount > 0 && endpointCount <= kMaxAllowedPaths, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(isUrgentCount <= kMaxAllowedPaths, CHIP_ERROR_INVALID_ARGUMENT); const bool hasSameIdsCount = (clusterCount == eventCount) && (clusterCount == endpointCount); const bool multipleClusters = clusterCount > 1 && eventCount == 1 && endpointCount == 1; const bool multipleEvents = eventCount > 1 && clusterCount == 1 && endpointCount == 1; const bool multipleEndpoints = endpointCount > 1 && clusterCount == 1 && eventCount == 1; size_t pathsCount = 0; if (hasSameIdsCount) { pathsCount = clusterCount; } else if (multipleClusters) { pathsCount = clusterCount; } else if (multipleEvents) { pathsCount = eventCount; } else if (multipleEndpoints) { pathsCount = endpointCount; } else { ChipLogError(chipTool, "\n%sEvent command targetting multiple paths needs to have: \n \t * One element with multiple ids (for " "example 1 cluster id, 1 event id, 2 endpoint ids)\n\t * Or the same " "number of ids (for examples 2 cluster ids, 2 event ids and 2 endpoint ids).\n The current command has %u " "cluster ids, %u event ids, %u endpoint ids.", interactionType == ReadClient::InteractionType::Subscribe ? "Subscribe" : "Read", static_cast(clusterCount), static_cast(eventCount), static_cast(endpointCount)); return CHIP_ERROR_INVALID_ARGUMENT; } auto eventPathParams = std::make_unique(pathsCount); ChipLogProgress(chipTool, "Sending %sEvent to:", interactionType == ReadClient::InteractionType::Subscribe ? "Subscribe" : "Read"); for (size_t i = 0; i < pathsCount; i++) { ClusterId clusterId = clusterIds.at((hasSameIdsCount || multipleClusters) ? i : 0); EventId eventId = eventIds.at((hasSameIdsCount || multipleEvents) ? i : 0); EndpointId endpointId = endpointIds.at((hasSameIdsCount || multipleEndpoints) ? i : 0); ChipLogProgress(chipTool, "\tcluster " ChipLogFormatMEI ", event: " ChipLogFormatMEI ", endpoint %u", ChipLogValueMEI(clusterId), ChipLogValueMEI(eventId), endpointId); if (clusterId != kInvalidClusterId) { eventPathParams[i].mClusterId = clusterId; } if (eventId != kInvalidEventId) { eventPathParams[i].mEventId = eventId; } if (endpointId != kInvalidEndpointId) { eventPathParams[i].mEndpointId = endpointId; } if (mIsUrgents.HasValue() && mIsUrgents.Value().size() > i) { eventPathParams[i].mIsUrgentEvent = mIsUrgents.Value().at(i); } } ReadPrepareParams params(device->GetSecureSession().Value()); params.mpEventPathParamsList = eventPathParams.get(); params.mEventPathParamsListSize = pathsCount; params.mEventNumber = mEventNumber; params.mpAttributePathParamsList = nullptr; params.mAttributePathParamsListSize = 0; if (mFabricFiltered.HasValue()) { params.mIsFabricFiltered = mFabricFiltered.Value(); } if (interactionType == ReadClient::InteractionType::Subscribe) { params.mMinIntervalFloorSeconds = mMinInterval; params.mMaxIntervalCeilingSeconds = mMaxInterval; if (mKeepSubscriptions.HasValue()) { params.mKeepSubscriptions = mKeepSubscriptions.Value(); } params.mIsPeerLIT = mIsPeerLIT; } auto client = std::make_unique(InteractionModelEngine::GetInstance(), device->GetExchangeManager(), mBufferedReadAdapter, interactionType); if (mAutoResubscribe.ValueOr(false)) { eventPathParams.release(); ReturnErrorOnFailure(client->SendAutoResubscribeRequest(std::move(params))); } else { ReturnErrorOnFailure(client->SendRequest(params)); } mReadClients.push_back(std::move(client)); return CHIP_NO_ERROR; } void InteractionModelReports::CleanupReadClient(ReadClient * aReadClient) { mReadClients.erase( std::remove_if(mReadClients.begin(), mReadClients.end(), [aReadClient](auto & item) { return item.get() == aReadClient; }), mReadClients.end()); } CHIP_ERROR InteractionModelReports::ReportNone(chip::DeviceProxy * device, chip::app::ReadClient::InteractionType interactionType) { AttributePathParams attributePathParams[kMaxAllowedPaths]; EventPathParams eventPathParams[kMaxAllowedPaths]; ReadPrepareParams params(device->GetSecureSession().Value()); params.mpEventPathParamsList = eventPathParams; params.mEventPathParamsListSize = 0; params.mEventNumber = mEventNumber; params.mpAttributePathParamsList = attributePathParams; params.mAttributePathParamsListSize = 0; if (mFabricFiltered.HasValue()) { params.mIsFabricFiltered = mFabricFiltered.Value(); } if (interactionType == ReadClient::InteractionType::Subscribe) { params.mMinIntervalFloorSeconds = mMinInterval; params.mMaxIntervalCeilingSeconds = mMaxInterval; if (mKeepSubscriptions.HasValue()) { params.mKeepSubscriptions = mKeepSubscriptions.Value(); } } auto client = std::make_unique(InteractionModelEngine::GetInstance(), device->GetExchangeManager(), mBufferedReadAdapter, interactionType); ReturnErrorOnFailure(client->SendRequest(params)); mReadClients.push_back(std::move(client)); return CHIP_NO_ERROR; } CHIP_ERROR InteractionModelReports::ReportAll(chip::DeviceProxy * device, std::vector endpointIds, std::vector clusterIds, std::vector attributeIds, std::vector eventIds, chip::app::ReadClient::InteractionType interactionType) { const size_t endpointCount = endpointIds.size(); const size_t clusterCount = clusterIds.size(); const size_t attributeCount = attributeIds.size(); const size_t eventCount = eventIds.size(); // TODO Add data version supports // TODO Add isUrgents supports VerifyOrReturnError(endpointCount > 0 && endpointCount <= kMaxAllowedPaths, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(clusterCount > 0 && clusterCount <= kMaxAllowedPaths, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(attributeCount > 0 && attributeCount <= kMaxAllowedPaths, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(eventCount > 0 && eventCount <= kMaxAllowedPaths, CHIP_ERROR_INVALID_ARGUMENT); const bool hasSameIdsCount = (clusterCount == (attributeCount + eventCount)) && (clusterCount == endpointCount); if (!hasSameIdsCount) { ChipLogError(chipTool, "\nCommand targetting a combination of attribute and event paths needs to have has many clusters and " "endpoints than the number of attribute and events combined.\n" "For example if there are 2 attributes and 1 event, the command expects 3 clusters and 3 endpoints.\n" "Clusters and endpoints ids will be consumed first to populate the attribute paths of the request, and then " "to populate the event paths of the request.\n\n" "For example the following arguments:\n" "\tcluster-ids: 6,6,0X28\n" "\tendpoint-ids: 1,1,0\n" "\tattribute-ids: 0,0x4001\n" "\tevent-ids: 0\n" "\n" "will create the following paths:\n" "\t{cluster: 6, endpoint: 1, attribute: 0}\n" "\t{cluster: 6, endpoint: 1, attribute: 0x4001}\n" "\t{cluster: 0x28, endpoint: 0, event: 0}\n"); return CHIP_ERROR_INVALID_ARGUMENT; } AttributePathParams attributePathParams[kMaxAllowedPaths]; EventPathParams eventPathParams[kMaxAllowedPaths]; size_t attributeIndex = 0; size_t eventIndex = 0; size_t pathsCount = clusterCount; for (size_t i = 0; i < pathsCount; i++) { auto clusterId = clusterIds.at(i); auto endpointId = endpointIds.at(i); if (attributeIndex < attributeIds.size()) { auto attributeId = attributeIds.at(attributeIndex); if (endpointId != kInvalidEndpointId) { attributePathParams[attributeIndex].mEndpointId = endpointId; } if (clusterId != kInvalidClusterId) { attributePathParams[attributeIndex].mClusterId = clusterId; } if (attributeId != kInvalidAttributeId) { attributePathParams[attributeIndex].mAttributeId = attributeId; } attributeIndex++; } else if (eventIndex < eventIds.size()) { auto eventId = eventIds.at(eventIndex); if (endpointId != kInvalidEndpointId) { eventPathParams[eventIndex].mEndpointId = endpointId; } if (clusterId != kInvalidClusterId) { eventPathParams[eventIndex].mClusterId = clusterId; } if (eventId != kInvalidEventId) { eventPathParams[eventIndex].mEventId = eventId; } eventIndex++; } } ReadPrepareParams params(device->GetSecureSession().Value()); params.mpEventPathParamsList = eventPathParams; params.mEventPathParamsListSize = eventCount; params.mEventNumber = mEventNumber; params.mpAttributePathParamsList = attributePathParams; params.mAttributePathParamsListSize = attributeCount; if (mFabricFiltered.HasValue()) { params.mIsFabricFiltered = mFabricFiltered.Value(); } if (interactionType == ReadClient::InteractionType::Subscribe) { params.mMinIntervalFloorSeconds = mMinInterval; params.mMaxIntervalCeilingSeconds = mMaxInterval; if (mKeepSubscriptions.HasValue()) { params.mKeepSubscriptions = mKeepSubscriptions.Value(); } params.mIsPeerLIT = mIsPeerLIT; } auto client = std::make_unique(InteractionModelEngine::GetInstance(), device->GetExchangeManager(), mBufferedReadAdapter, interactionType); ReturnErrorOnFailure(client->SendRequest(params)); mReadClients.push_back(std::move(client)); return CHIP_NO_ERROR; } void InteractionModelReports::OnDeallocatePaths(chip::app::ReadPrepareParams && aReadPrepareParams) { if (aReadPrepareParams.mpAttributePathParamsList != nullptr) { delete[] aReadPrepareParams.mpAttributePathParamsList; } if (aReadPrepareParams.mpDataVersionFilterList != nullptr) { delete[] aReadPrepareParams.mpDataVersionFilterList; } if (aReadPrepareParams.mpEventPathParamsList != nullptr) { delete[] aReadPrepareParams.mpEventPathParamsList; } }