diff --git a/src/app/AttributeEncodeState.h b/src/app/AttributeEncodeState.h new file mode 100644 index 00000000000000..271e16235f0947 --- /dev/null +++ b/src/app/AttributeEncodeState.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2021-2024 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. + */ +#pragma once + +#include + +namespace chip { +namespace app { + +/// Maintains the internal state of list encoding +/// +/// List encoding is generally assumed incremental and chunkable (i.e. +/// partial encoding is ok.). For this purpose the class maintains two +/// pieces of data: +/// - AllowPartialData tracks if partial encoding is acceptable in the +/// current encoding state (to be used for atomic/non-atomic list item writes) +/// - CurrentEncodingListIndex representing the list index that is next +/// to be encoded in the output. kInvalidListIndex means that a new list +/// encoding has been started. +class AttributeEncodeState +{ +public: + AttributeEncodeState() = default; + + /// Allows the encode state to be initialized from an OPTIONAL + /// other encoding state + /// + /// if other is nullptr, this is the same as the default initializer. + AttributeEncodeState(const AttributeEncodeState * other) + { + if (other != nullptr) + { + *this = *other; + } + else + { + mCurrentEncodingListIndex = kInvalidListIndex; + mAllowPartialData = false; + } + } + + bool AllowPartialData() const { return mAllowPartialData; } + ListIndex CurrentEncodingListIndex() const { return mCurrentEncodingListIndex; } + + AttributeEncodeState & SetAllowPartialData(bool allow) + { + mAllowPartialData = allow; + return *this; + } + + AttributeEncodeState & SetCurrentEncodingListIndex(ListIndex idx) + { + mCurrentEncodingListIndex = idx; + return *this; + } + + void Reset() + { + mCurrentEncodingListIndex = kInvalidListIndex; + mAllowPartialData = false; + } + +private: + /** + * If set to kInvalidListIndex, indicates that we have not encoded any data for the list yet and + * need to start by encoding an empty list before we start encoding any list items. + * + * When set to a valid ListIndex value, indicates the index of the next list item that needs to be + * encoded (i.e. the count of items encoded so far). + */ + ListIndex mCurrentEncodingListIndex = kInvalidListIndex; + + /** + * When an attempt to encode an attribute returns an error, the buffer may contain tailing dirty data + * (since the put was aborted). The report engine normally rolls back the buffer to right before encoding + * of the attribute started on errors. + * + * When chunking a list, EncodeListItem will atomically encode list items, ensuring that the + * state of the buffer is valid to send (i.e. contains no trailing garbage), and return an error + * if the list doesn't entirely fit. In this situation, mAllowPartialData is set to communicate to the + * report engine that it should not roll back the list items. + * + * TODO: There might be a better name for this variable. + */ + bool mAllowPartialData = false; +}; + +} // namespace app +} // namespace chip diff --git a/src/app/AttributeValueEncoder.cpp b/src/app/AttributeValueEncoder.cpp index da4edd1fc483e7..4b6bb10e66abdb 100644 --- a/src/app/AttributeValueEncoder.cpp +++ b/src/app/AttributeValueEncoder.cpp @@ -32,13 +32,13 @@ CHIP_ERROR AttributeValueEncoder::EnsureListStarted() { VerifyOrDie(mCurrentEncodingListIndex == kInvalidListIndex); - mEncodingInitialList = (mEncodeState.mCurrentEncodingListIndex == kInvalidListIndex); + mEncodingInitialList = (mEncodeState.CurrentEncodingListIndex() == kInvalidListIndex); if (mEncodingInitialList) { // Clear mAllowPartialData flag here since this encode procedure is not atomic. // The most common error in this function is CHIP_ERROR_NO_MEMORY / CHIP_ERROR_BUFFER_TOO_SMALL, just revert and try // next time is ok. - mEncodeState.mAllowPartialData = false; + mEncodeState.SetAllowPartialData(false); AttributeReportBuilder builder; @@ -59,7 +59,7 @@ CHIP_ERROR AttributeValueEncoder::EnsureListStarted() ReturnErrorOnFailure( mAttributeReportIBsBuilder.GetWriter()->ReserveBuffer(kEndOfAttributeReportIBByteCount + kEndOfListByteCount)); - mEncodeState.mCurrentEncodingListIndex = 0; + mEncodeState.SetCurrentEncodingListIndex(0); } else { @@ -72,7 +72,7 @@ CHIP_ERROR AttributeValueEncoder::EnsureListStarted() // After encoding the initial list start, the remaining items are atomically encoded into the buffer. Tell report engine to not // revert partial data. - mEncodeState.mAllowPartialData = true; + mEncodeState.SetAllowPartialData(true); return CHIP_NO_ERROR; } @@ -105,7 +105,7 @@ void AttributeValueEncoder::EnsureListEnded() // If we succeeded at encoding the whole list (i.e. the list is in fact // empty and we fit in the packet), mAllowPartialData will be ignored, // so it's safe to set it to false even if encoding succeeded. - mEncodeState.mAllowPartialData = false; + mEncodeState.SetAllowPartialData(false); } } diff --git a/src/app/AttributeValueEncoder.h b/src/app/AttributeValueEncoder.h index d5efe1768c9d06..5c196f91f2e426 100644 --- a/src/app/AttributeValueEncoder.h +++ b/src/app/AttributeValueEncoder.h @@ -16,6 +16,8 @@ */ #pragma once +#include +#include #include #include #include @@ -51,9 +53,9 @@ class AttributeValueEncoder // If we are encoding for a fabric filtered attribute read and the fabric index does not match that present in the // request, skip encoding this list item. VerifyOrReturnError(!mAttributeValueEncoder.mIsFabricFiltered || - aArg.GetFabricIndex() == mAttributeValueEncoder.mAccessingFabricIndex, + aArg.GetFabricIndex() == mAttributeValueEncoder.AccessingFabricIndex(), CHIP_NO_ERROR); - return mAttributeValueEncoder.EncodeListItem(mAttributeValueEncoder.mAccessingFabricIndex, std::forward(aArg)); + return mAttributeValueEncoder.EncodeListItem(mAttributeValueEncoder.AccessingFabricIndex(), std::forward(aArg)); } template ::value, bool> = true> @@ -66,42 +68,11 @@ class AttributeValueEncoder AttributeValueEncoder & mAttributeValueEncoder; }; - class AttributeEncodeState - { - public: - AttributeEncodeState() : mAllowPartialData(false), mCurrentEncodingListIndex(kInvalidListIndex) {} - bool AllowPartialData() const { return mAllowPartialData; } - - private: - friend class AttributeValueEncoder; - /** - * When an attempt to encode an attribute returns an error, the buffer may contain tailing dirty data - * (since the put was aborted). The report engine normally rolls back the buffer to right before encoding - * of the attribute started on errors. - * - * When chunking a list, EncodeListItem will atomically encode list items, ensuring that the - * state of the buffer is valid to send (i.e. contains no trailing garbage), and return an error - * if the list doesn't entirely fit. In this situation, mAllowPartialData is set to communicate to the - * report engine that it should not roll back the list items. - * - * TODO: There might be a better name for this variable. - */ - bool mAllowPartialData = false; - /** - * If set to kInvalidListIndex, indicates that we have not encoded any data for the list yet and - * need to start by encoding an empty list before we start encoding any list items. - * - * When set to a valid ListIndex value, indicates the index of the next list item that needs to be - * encoded (i.e. the count of items encoded so far). - */ - ListIndex mCurrentEncodingListIndex = kInvalidListIndex; - }; - - AttributeValueEncoder(AttributeReportIBs::Builder & aAttributeReportIBsBuilder, FabricIndex aAccessingFabricIndex, + AttributeValueEncoder(AttributeReportIBs::Builder & aAttributeReportIBsBuilder, Access::SubjectDescriptor subjectDescriptor, const ConcreteAttributePath & aPath, DataVersion aDataVersion, bool aIsFabricFiltered = false, const AttributeEncodeState & aState = AttributeEncodeState()) : mAttributeReportIBsBuilder(aAttributeReportIBsBuilder), - mAccessingFabricIndex(aAccessingFabricIndex), mPath(aPath.mEndpointId, aPath.mClusterId, aPath.mAttributeId), + mSubjectDescriptor(subjectDescriptor), mPath(aPath.mEndpointId, aPath.mClusterId, aPath.mAttributeId), mDataVersion(aDataVersion), mIsFabricFiltered(aIsFabricFiltered), mEncodeState(aState) {} @@ -169,17 +140,19 @@ class AttributeValueEncoder if (err == CHIP_NO_ERROR) { // The Encode procedure finished without any error, clear the state. - mEncodeState = AttributeEncodeState(); + mEncodeState.Reset(); } return err; } bool TriedEncode() const { return mTriedEncode; } + const Access::SubjectDescriptor & GetSubjectDescriptor() const { return mSubjectDescriptor; } + /** * The accessing fabric index for this read or subscribe interaction. */ - FabricIndex AccessingFabricIndex() const { return mAccessingFabricIndex; } + FabricIndex AccessingFabricIndex() const { return GetSubjectDescriptor().fabricIndex; } /** * AttributeValueEncoder is a short lived object, and the state is persisted by mEncodeState and restored by constructor. @@ -195,7 +168,7 @@ class AttributeValueEncoder { // EncodeListItem must be called after EnsureListStarted(), thus mCurrentEncodingListIndex and // mEncodeState.mCurrentEncodingListIndex are not invalid values. - if (mCurrentEncodingListIndex < mEncodeState.mCurrentEncodingListIndex) + if (mCurrentEncodingListIndex < mEncodeState.CurrentEncodingListIndex()) { // We have encoded this element in previous chunks, skip it. mCurrentEncodingListIndex++; @@ -226,7 +199,7 @@ class AttributeValueEncoder } mCurrentEncodingListIndex++; - mEncodeState.mCurrentEncodingListIndex++; + mEncodeState.SetCurrentEncodingListIndex(mCurrentEncodingListIndex); mEncodedAtLeastOneListItem = true; return CHIP_NO_ERROR; } @@ -266,20 +239,20 @@ class AttributeValueEncoder */ void EnsureListEnded(); - bool mTriedEncode = false; AttributeReportIBs::Builder & mAttributeReportIBsBuilder; - const FabricIndex mAccessingFabricIndex; + const Access::SubjectDescriptor mSubjectDescriptor; ConcreteDataAttributePath mPath; DataVersion mDataVersion; + bool mTriedEncode = false; bool mIsFabricFiltered = false; // mEncodingInitialList is true if we're encoding a list and we have not // started chunking it yet, so we're encoding a single attribute report IB // for the whole list, not one per item. bool mEncodingInitialList = false; // mEncodedAtLeastOneListItem becomes true once we successfully encode a list item. - bool mEncodedAtLeastOneListItem = false; - AttributeEncodeState mEncodeState; + bool mEncodedAtLeastOneListItem = false; ListIndex mCurrentEncodingListIndex = kInvalidListIndex; + AttributeEncodeState mEncodeState; }; } // namespace app diff --git a/src/app/BUILD.gn b/src/app/BUILD.gn index 042d3ba92281d4..e40847403cd01f 100644 --- a/src/app/BUILD.gn +++ b/src/app/BUILD.gn @@ -281,6 +281,7 @@ static_library("attribute-access") { "AttributeAccessInterfaceCache.h", "AttributeAccessInterfaceRegistry.cpp", "AttributeAccessInterfaceRegistry.h", + "AttributeEncodeState.h", "AttributeReportBuilder.cpp", "AttributeReportBuilder.h", "AttributeValueDecoder.h", diff --git a/src/app/ConcreteAttributePath.h b/src/app/ConcreteAttributePath.h index 03e801bd9b3395..99afb4614e574f 100644 --- a/src/app/ConcreteAttributePath.h +++ b/src/app/ConcreteAttributePath.h @@ -101,7 +101,7 @@ struct ConcreteReadAttributePath : public ConcreteAttributePath */ struct ConcreteDataAttributePath : public ConcreteAttributePath { - enum class ListOperation + enum class ListOperation : uint8_t { NotList, // Path points to an attribute that isn't a list. ReplaceAll, // Path points to an attribute that is a list, indicating that the contents of the list should be replaced in diff --git a/src/app/ReadHandler.cpp b/src/app/ReadHandler.cpp index 142df8015601ca..461add0f2dc150 100644 --- a/src/app/ReadHandler.cpp +++ b/src/app/ReadHandler.cpp @@ -841,7 +841,7 @@ void ReadHandler::PersistSubscription() void ReadHandler::ResetPathIterator() { mAttributePathExpandIterator = AttributePathExpandIterator(mpAttributePathList); - mAttributeEncoderState = AttributeValueEncoder::AttributeEncodeState(); + mAttributeEncoderState.Reset(); } void ReadHandler::AttributePathIsDirty(const AttributePathParams & aAttributeChanged) @@ -870,7 +870,7 @@ void ReadHandler::AttributePathIsDirty(const AttributePathParams & aAttributeCha // our iterator to point back to the beginning of that cluster. This ensures that the receiver will get a coherent view of // the state of the cluster as present on the server mAttributePathExpandIterator.ResetCurrentCluster(); - mAttributeEncoderState = AttributeValueEncoder::AttributeEncodeState(); + mAttributeEncoderState.Reset(); } // ReportScheduler will take care of verifying the reportability of the handler and schedule the run diff --git a/src/app/ReadHandler.h b/src/app/ReadHandler.h index f5355372ac5301..a30f1f8974ad25 100644 --- a/src/app/ReadHandler.h +++ b/src/app/ReadHandler.h @@ -412,8 +412,8 @@ class ReadHandler : public Messaging::ExchangeDelegate /// or after the min interval is reached if it has not yet been reached. void ForceDirtyState(); - const AttributeValueEncoder::AttributeEncodeState & GetAttributeEncodeState() const { return mAttributeEncoderState; } - void SetAttributeEncodeState(const AttributeValueEncoder::AttributeEncodeState & aState) { mAttributeEncoderState = aState; } + const AttributeEncodeState & GetAttributeEncodeState() const { return mAttributeEncoderState; } + void SetAttributeEncodeState(const AttributeEncodeState & aState) { mAttributeEncoderState = aState; } uint32_t GetLastWrittenEventsBytes() const { return mLastWrittenEventsBytes; } // Returns the number of interested paths, including wildcard and concrete paths. @@ -562,7 +562,7 @@ class ReadHandler : public Messaging::ExchangeDelegate // The detailed encoding state for a single attribute, used by list chunking feature. // The size of AttributeEncoderState is 2 bytes for now. - AttributeValueEncoder::AttributeEncodeState mAttributeEncoderState; + AttributeEncodeState mAttributeEncoderState; // Current Handler state HandlerState mState = HandlerState::Idle; diff --git a/src/app/dynamic_server/DynamicDispatcher.cpp b/src/app/dynamic_server/DynamicDispatcher.cpp index 5f9476aa6fd40a..e1f458f0bc3df1 100644 --- a/src/app/dynamic_server/DynamicDispatcher.cpp +++ b/src/app/dynamic_server/DynamicDispatcher.cpp @@ -123,7 +123,7 @@ Status DetermineAttributeStatus(const ConcreteAttributePath & aPath, bool aIsWri CHIP_ERROR ReadSingleClusterData(const SubjectDescriptor & aSubjectDescriptor, bool aIsFabricFiltered, const ConcreteReadAttributePath & aPath, AttributeReportIBs::Builder & aAttributeReports, - AttributeValueEncoder::AttributeEncodeState * aEncoderState) + AttributeEncodeState * aEncoderState) { Status status = DetermineAttributeStatus(aPath, /* aIsWrite = */ false); return aAttributeReports.EncodeAttributeStatus(aPath, StatusIB(status)); diff --git a/src/app/reporting/Engine.cpp b/src/app/reporting/Engine.cpp index 37c7e31dd645fb..39c582f3e45f0b 100644 --- a/src/app/reporting/Engine.cpp +++ b/src/app/reporting/Engine.cpp @@ -82,7 +82,7 @@ bool Engine::IsClusterDataVersionMatch(const SingleLinkedListNode Cluster %" PRIx32 ", Attribute %" PRIx32 " is dirty", aPath.mClusterId, aPath.mAttributeId); @@ -199,7 +199,7 @@ CHIP_ERROR Engine::BuildSingleReportDataAttributeReportIBs(ReportDataMessage::Bu attributeReportIBs.Checkpoint(attributeBackup); ConcreteReadAttributePath pathForRetrieval(readPath); // Load the saved state from previous encoding session for chunking of one single attribute (list chunking). - AttributeValueEncoder::AttributeEncodeState encodeState = apReadHandler->GetAttributeEncodeState(); + AttributeEncodeState encodeState = apReadHandler->GetAttributeEncodeState(); err = RetrieveClusterData(apReadHandler->GetSubjectDescriptor(), apReadHandler->IsFabricFiltered(), attributeReportIBs, pathForRetrieval, &encodeState); if (err != CHIP_NO_ERROR) @@ -226,7 +226,7 @@ CHIP_ERROR Engine::BuildSingleReportDataAttributeReportIBs(ReportDataMessage::Bu // We met a error during writing reports, one common case is we are running out of buffer, rollback the // attributeReportIB to avoid any partial data. attributeReportIBs.Rollback(attributeBackup); - apReadHandler->SetAttributeEncodeState(AttributeValueEncoder::AttributeEncodeState()); + apReadHandler->SetAttributeEncodeState(AttributeEncodeState()); if (!IsOutOfWriterSpaceError(err)) { @@ -256,7 +256,7 @@ CHIP_ERROR Engine::BuildSingleReportDataAttributeReportIBs(ReportDataMessage::Bu } SuccessOrExit(err); // Successfully encoded the attribute, clear the internal state. - apReadHandler->SetAttributeEncodeState(AttributeValueEncoder::AttributeEncodeState()); + apReadHandler->SetAttributeEncodeState(AttributeEncodeState()); } // We just visited all paths interested by this read handler and did not abort in the middle of iteration, there are no more // chunks for this report. diff --git a/src/app/reporting/Engine.h b/src/app/reporting/Engine.h index fccf9e08ab020f..0bf7a9f83de1ae 100644 --- a/src/app/reporting/Engine.h +++ b/src/app/reporting/Engine.h @@ -170,8 +170,7 @@ class Engine bool aBufferIsUsed, bool * apHasMoreChunks, bool * apHasEncodedData); CHIP_ERROR RetrieveClusterData(const Access::SubjectDescriptor & aSubjectDescriptor, bool aIsFabricFiltered, AttributeReportIBs::Builder & aAttributeReportIBs, - const ConcreteReadAttributePath & aClusterInfo, - AttributeValueEncoder::AttributeEncodeState * apEncoderState); + const ConcreteReadAttributePath & aClusterInfo, AttributeEncodeState * apEncoderState); CHIP_ERROR CheckAccessDeniedEventPaths(TLV::TLVWriter & aWriter, bool & aHasEncodedData, ReadHandler * apReadHandler); // If version match, it means don't send, if version mismatch, it means send. diff --git a/src/app/tests/TestAttributeValueEncoder.cpp b/src/app/tests/TestAttributeValueEncoder.cpp index 25ea87736c84e8..085c4f870cdf64 100644 --- a/src/app/tests/TestAttributeValueEncoder.cpp +++ b/src/app/tests/TestAttributeValueEncoder.cpp @@ -49,15 +49,37 @@ constexpr ClusterId kRandomClusterId = 0xaa; constexpr AttributeId kRandomAttributeId = 0xcc; constexpr DataVersion kRandomDataVersion = 0x99; constexpr FabricIndex kTestFabricIndex = 1; +constexpr NodeId kFakeNodeId = 1; constexpr TLV::Tag kFabricIndexTag = TLV::ContextTag(254); +Access::SubjectDescriptor DescriptorWithFabric(FabricIndex fabricIndex) +{ + Access::SubjectDescriptor result; + + result.fabricIndex = fabricIndex; + result.subject = kFakeNodeId; + + if (fabricIndex == kUndefinedFabricIndex) + { + // Make it seem somewhat valid: a fabric index is not available in PASE + // before AddNOC + result.authMode = Access::AuthMode::kPase; + } + else + { + result.authMode = Access::AuthMode::kCase; + } + return result; +} + template struct LimitedTestSetup { LimitedTestSetup(nlTestSuite * aSuite, const FabricIndex aFabricIndex = kUndefinedFabricIndex, - const AttributeValueEncoder::AttributeEncodeState & aState = AttributeValueEncoder::AttributeEncodeState()) : - encoder(builder, aFabricIndex, ConcreteAttributePath(kRandomEndpointId, kRandomClusterId, kRandomAttributeId), - kRandomDataVersion, aFabricIndex != kUndefinedFabricIndex, aState) + const AttributeEncodeState & aState = AttributeEncodeState()) : + encoder(builder, DescriptorWithFabric(aFabricIndex), + ConcreteAttributePath(kRandomEndpointId, kRandomClusterId, kRandomAttributeId), kRandomDataVersion, + aFabricIndex != kUndefinedFabricIndex, aState) { writer.Init(buf); { @@ -275,7 +297,7 @@ void TestEncodeFabricScoped(nlTestSuite * aSuite, void * aContext) void TestEncodeListChunking(nlTestSuite * aSuite, void * aContext) { - AttributeValueEncoder::AttributeEncodeState state; + AttributeEncodeState state; bool list[] = { true, false, false, true, true, false }; auto listEncoder = [&list](const auto & encoder) -> CHIP_ERROR { @@ -400,7 +422,7 @@ void TestEncodeListChunking(nlTestSuite * aSuite, void * aContext) void TestEncodeListChunking2(nlTestSuite * aSuite, void * aContext) { - AttributeValueEncoder::AttributeEncodeState state; + AttributeEncodeState state; bool list[] = { true, false, false, true, true, false }; auto listEncoder = [&list](const auto & encoder) -> CHIP_ERROR { diff --git a/src/app/tests/TestPowerSourceCluster.cpp b/src/app/tests/TestPowerSourceCluster.cpp index 46371b332a1804..374b9619d38684 100644 --- a/src/app/tests/TestPowerSourceCluster.cpp +++ b/src/app/tests/TestPowerSourceCluster.cpp @@ -80,7 +80,8 @@ std::vector ReadEndpointsThroughAttributeReader(nlTestSuite * apSuit ConcreteAttributePath path(endpoint, Clusters::PowerSource::Id, Clusters::PowerSource::Attributes::EndpointList::Id); ConcreteReadAttributePath readPath(path); chip::DataVersion dataVersion(0); - AttributeValueEncoder aEncoder(builder, 0, path, dataVersion); + Access::SubjectDescriptor subjectDescriptor; + AttributeValueEncoder aEncoder(builder, subjectDescriptor, path, dataVersion); err = attrAccess.Read(readPath, aEncoder); diff --git a/src/app/tests/TestReadInteraction.cpp b/src/app/tests/TestReadInteraction.cpp index eacd9a1d6f7762..8173761b6ca83a 100644 --- a/src/app/tests/TestReadInteraction.cpp +++ b/src/app/tests/TestReadInteraction.cpp @@ -303,7 +303,7 @@ namespace app { CHIP_ERROR ReadSingleClusterData(const Access::SubjectDescriptor & aSubjectDescriptor, bool aIsFabricFiltered, const ConcreteReadAttributePath & aPath, AttributeReportIBs::Builder & aAttributeReports, - AttributeValueEncoder::AttributeEncodeState * apEncoderState) + AttributeEncodeState * apEncoderState) { if (aPath.mClusterId >= Test::kMockEndpointMin) { @@ -331,7 +331,7 @@ CHIP_ERROR ReadSingleClusterData(const Access::SubjectDescriptor & aSubjectDescr return attributeReport.EndOfAttributeReportIB(); } - return AttributeValueEncoder(aAttributeReports, 0, aPath, 0).Encode(kTestFieldValue1); + return AttributeValueEncoder(aAttributeReports, aSubjectDescriptor, aPath, 0 /* dataVersion */).Encode(kTestFieldValue1); } bool IsClusterDataVersionEqual(const ConcreteClusterPath & aConcreteClusterPath, DataVersion aRequiredVersion) diff --git a/src/app/tests/integration/chip_im_initiator.cpp b/src/app/tests/integration/chip_im_initiator.cpp index 56cd7f37005ce5..48a66cb7fc2bba 100644 --- a/src/app/tests/integration/chip_im_initiator.cpp +++ b/src/app/tests/integration/chip_im_initiator.cpp @@ -631,7 +631,7 @@ void DispatchSingleClusterCommand(const ConcreteCommandPath & aCommandPath, chip CHIP_ERROR ReadSingleClusterData(const Access::SubjectDescriptor & aSubjectDescriptor, bool aIsFabricFiltered, const ConcreteReadAttributePath & aPath, AttributeReportIBs::Builder & aAttributeReports, - AttributeValueEncoder::AttributeEncodeState * apEncoderState) + AttributeEncodeState * apEncoderState) { AttributeReportIB::Builder & attributeReport = aAttributeReports.CreateAttributeReport(); ReturnErrorOnFailure(aAttributeReports.GetError()); diff --git a/src/app/tests/integration/chip_im_responder.cpp b/src/app/tests/integration/chip_im_responder.cpp index 0250cd0fbc7e69..72f056ca6ab8b7 100644 --- a/src/app/tests/integration/chip_im_responder.cpp +++ b/src/app/tests/integration/chip_im_responder.cpp @@ -121,10 +121,9 @@ void DispatchSingleClusterCommand(const ConcreteCommandPath & aRequestCommandPat CHIP_ERROR ReadSingleClusterData(const Access::SubjectDescriptor & aSubjectDescriptor, bool aIsFabricFiltered, const ConcreteReadAttributePath & aPath, AttributeReportIBs::Builder & aAttributeReports, - AttributeValueEncoder::AttributeEncodeState * apEncoderState) + AttributeEncodeState * apEncoderState) { - ReturnErrorOnFailure(AttributeValueEncoder(aAttributeReports, 0, aPath, 0).Encode(kTestFieldValue1)); - return CHIP_NO_ERROR; + return AttributeValueEncoder(aAttributeReports, aSubjectDescriptor, aPath, 0).Encode(kTestFieldValue1); } bool ConcreteAttributePathExists(const ConcreteAttributePath & aPath) diff --git a/src/app/util/ember-compatibility-functions.cpp b/src/app/util/ember-compatibility-functions.cpp index 3c98863aa50fb1..e87e0f564a5ff7 100644 --- a/src/app/util/ember-compatibility-functions.cpp +++ b/src/app/util/ember-compatibility-functions.cpp @@ -425,16 +425,15 @@ CHIP_ERROR GlobalAttributeReader::EncodeCommandList(const ConcreteClusterPath & // Helper function for trying to read an attribute value via an // AttributeAccessInterface. On failure, the read has failed. On success, the // aTriedEncode outparam is set to whether the AttributeAccessInterface tried to encode a value. -CHIP_ERROR ReadViaAccessInterface(FabricIndex aAccessingFabricIndex, bool aIsFabricFiltered, +CHIP_ERROR ReadViaAccessInterface(const SubjectDescriptor & subjectDescriptor, bool aIsFabricFiltered, const ConcreteReadAttributePath & aPath, AttributeReportIBs::Builder & aAttributeReports, - AttributeValueEncoder::AttributeEncodeState * aEncoderState, - AttributeAccessInterface * aAccessInterface, bool * aTriedEncode) + AttributeEncodeState * aEncoderState, AttributeAccessInterface * aAccessInterface, + bool * aTriedEncode) { - AttributeValueEncoder::AttributeEncodeState state = - (aEncoderState == nullptr ? AttributeValueEncoder::AttributeEncodeState() : *aEncoderState); + AttributeEncodeState state(aEncoderState); DataVersion version = 0; ReturnErrorOnFailure(ReadClusterDataVersion(aPath, version)); - AttributeValueEncoder valueEncoder(aAttributeReports, aAccessingFabricIndex, aPath, version, aIsFabricFiltered, state); + AttributeValueEncoder valueEncoder(aAttributeReports, subjectDescriptor, aPath, version, aIsFabricFiltered, state); CHIP_ERROR err = aAccessInterface->Read(aPath, valueEncoder); if (err == CHIP_IM_GLOBAL_STATUS(UnsupportedRead) && aPath.mExpanded) @@ -523,7 +522,7 @@ bool ConcreteAttributePathExists(const ConcreteAttributePath & aPath) CHIP_ERROR ReadSingleClusterData(const SubjectDescriptor & aSubjectDescriptor, bool aIsFabricFiltered, const ConcreteReadAttributePath & aPath, AttributeReportIBs::Builder & aAttributeReports, - AttributeValueEncoder::AttributeEncodeState * apEncoderState) + AttributeEncodeState * apEncoderState) { ChipLogDetail(DataManagement, "Reading attribute: Cluster=" ChipLogFormatMEI " Endpoint=%x AttributeId=" ChipLogFormatMEI " (expanded=%d)", @@ -569,7 +568,7 @@ CHIP_ERROR ReadSingleClusterData(const SubjectDescriptor & aSubjectDescriptor, b if (attributeOverride) { bool triedEncode = false; - ReturnErrorOnFailure(ReadViaAccessInterface(aSubjectDescriptor.fabricIndex, aIsFabricFiltered, aPath, aAttributeReports, + ReturnErrorOnFailure(ReadViaAccessInterface(aSubjectDescriptor, aIsFabricFiltered, aPath, aAttributeReports, apEncoderState, attributeOverride, &triedEncode)); ReturnErrorCodeIf(triedEncode, CHIP_NO_ERROR); } diff --git a/src/app/util/ember-compatibility-functions.h b/src/app/util/ember-compatibility-functions.h index 72d4379bce69f0..ca7f16950bad39 100644 --- a/src/app/util/ember-compatibility-functions.h +++ b/src/app/util/ember-compatibility-functions.h @@ -60,7 +60,7 @@ bool ConcreteAttributePathExists(const ConcreteAttributePath & aPath); */ CHIP_ERROR ReadSingleClusterData(const Access::SubjectDescriptor & aSubjectDescriptor, bool aIsFabricFiltered, const ConcreteReadAttributePath & aPath, AttributeReportIBs::Builder & aAttributeReports, - AttributeValueEncoder::AttributeEncodeState * apEncoderState); + AttributeEncodeState * apEncoderState); /** * Returns the metadata of the attribute for the given path. diff --git a/src/app/util/mock/Functions.h b/src/app/util/mock/Functions.h index 2304d54c589787..6d58e6370cf39d 100644 --- a/src/app/util/mock/Functions.h +++ b/src/app/util/mock/Functions.h @@ -35,7 +35,7 @@ namespace Test { CHIP_ERROR ReadSingleMockClusterData(FabricIndex aAccessingFabricIndex, const app::ConcreteAttributePath & aPath, app::AttributeReportIBs::Builder & aAttributeReports, - app::AttributeValueEncoder::AttributeEncodeState * apEncoderState); + app::AttributeEncodeState * apEncoderState); /// Increase the current value for `GetVersion` void BumpVersion(); diff --git a/src/app/util/mock/attribute-storage.cpp b/src/app/util/mock/attribute-storage.cpp index 7326886851ff37..2293a48a8403e4 100644 --- a/src/app/util/mock/attribute-storage.cpp +++ b/src/app/util/mock/attribute-storage.cpp @@ -321,8 +321,7 @@ DataVersion GetVersion() } CHIP_ERROR ReadSingleMockClusterData(FabricIndex aAccessingFabricIndex, const ConcreteAttributePath & aPath, - AttributeReportIBs::Builder & aAttributeReports, - AttributeValueEncoder::AttributeEncodeState * apEncoderState) + AttributeReportIBs::Builder & aAttributeReports, AttributeEncodeState * apEncoderState) { bool dataExists = (emberAfGetServerAttributeIndexByAttributeId(aPath.mEndpointId, aPath.mClusterId, aPath.mAttributeId) != UINT16_MAX); @@ -351,9 +350,11 @@ CHIP_ERROR ReadSingleMockClusterData(FabricIndex aAccessingFabricIndex, const Co // Attribute 4 acts as a large attribute to trigger chunking. if (aPath.mAttributeId == MockAttributeId(4)) { - AttributeValueEncoder::AttributeEncodeState state = - (apEncoderState == nullptr ? AttributeValueEncoder::AttributeEncodeState() : *apEncoderState); - AttributeValueEncoder valueEncoder(aAttributeReports, aAccessingFabricIndex, aPath, dataVersion, false, state); + AttributeEncodeState state(apEncoderState); + Access::SubjectDescriptor subject; + subject.fabricIndex = aAccessingFabricIndex; + + AttributeValueEncoder valueEncoder(aAttributeReports, subject, aPath, dataVersion, /* aIsFabricFiltered = */ false, state); CHIP_ERROR err = valueEncoder.EncodeList([](const auto & encoder) -> CHIP_ERROR { for (int i = 0; i < 6; i++) diff --git a/src/controller/tests/data_model/TestRead.cpp b/src/controller/tests/data_model/TestRead.cpp index fc2cfaf516524b..bcf0233662d2bc 100644 --- a/src/controller/tests/data_model/TestRead.cpp +++ b/src/controller/tests/data_model/TestRead.cpp @@ -89,7 +89,7 @@ namespace chip { namespace app { CHIP_ERROR ReadSingleClusterData(const Access::SubjectDescriptor & aSubjectDescriptor, bool aIsFabricFiltered, const ConcreteReadAttributePath & aPath, AttributeReportIBs::Builder & aAttributeReports, - AttributeValueEncoder::AttributeEncodeState * apEncoderState) + AttributeEncodeState * apEncoderState) { if (aPath.mEndpointId >= chip::Test::kMockEndpointMin) { @@ -109,10 +109,8 @@ CHIP_ERROR ReadSingleClusterData(const Access::SubjectDescriptor & aSubjectDescr // Use an incorrect attribute id for some of the responses. path.mAttributeId = static_cast(path.mAttributeId + (i / 2) + (responseDirective == kSendManyDataResponsesWrongPath)); - AttributeValueEncoder::AttributeEncodeState state = - (apEncoderState == nullptr ? AttributeValueEncoder::AttributeEncodeState() : *apEncoderState); - AttributeValueEncoder valueEncoder(aAttributeReports, aSubjectDescriptor.fabricIndex, path, - kDataVersion /* data version */, aIsFabricFiltered, state); + AttributeEncodeState state(apEncoderState); + AttributeValueEncoder valueEncoder(aAttributeReports, aSubjectDescriptor, path, kDataVersion, aIsFabricFiltered, state); ReturnErrorOnFailure(valueEncoder.Encode(true)); } @@ -124,10 +122,9 @@ CHIP_ERROR ReadSingleClusterData(const Access::SubjectDescriptor & aSubjectDescr if (aPath.mClusterId == app::Clusters::UnitTesting::Id && aPath.mAttributeId == app::Clusters::UnitTesting::Attributes::ListFabricScoped::Id) { - AttributeValueEncoder::AttributeEncodeState state = - (apEncoderState == nullptr ? AttributeValueEncoder::AttributeEncodeState() : *apEncoderState); - AttributeValueEncoder valueEncoder(aAttributeReports, aSubjectDescriptor.fabricIndex, aPath, - kDataVersion /* data version */, aIsFabricFiltered, state); + AttributeEncodeState state(apEncoderState); + AttributeValueEncoder valueEncoder(aAttributeReports, aSubjectDescriptor, aPath, kDataVersion, aIsFabricFiltered, + state); return valueEncoder.EncodeList([aSubjectDescriptor](const auto & encoder) -> CHIP_ERROR { app::Clusters::UnitTesting::Structs::TestFabricScoped::Type val; @@ -141,19 +138,18 @@ CHIP_ERROR ReadSingleClusterData(const Access::SubjectDescriptor & aSubjectDescr if (aPath.mClusterId == app::Clusters::UnitTesting::Id && aPath.mAttributeId == app::Clusters::UnitTesting::Attributes::Int16u::Id) { - AttributeValueEncoder::AttributeEncodeState state = - (apEncoderState == nullptr ? AttributeValueEncoder::AttributeEncodeState() : *apEncoderState); - AttributeValueEncoder valueEncoder(aAttributeReports, aSubjectDescriptor.fabricIndex, aPath, - kDataVersion /* data version */, aIsFabricFiltered, state); + AttributeEncodeState state(apEncoderState); + AttributeValueEncoder valueEncoder(aAttributeReports, aSubjectDescriptor, aPath, kDataVersion, aIsFabricFiltered, + state); return valueEncoder.Encode(++totalReadCount); } if (aPath.mClusterId == kPerpetualClusterId || (aPath.mClusterId == app::Clusters::UnitTesting::Id && aPath.mAttributeId == kPerpetualAttributeid)) { - AttributeValueEncoder::AttributeEncodeState state = AttributeValueEncoder::AttributeEncodeState(); - AttributeValueEncoder valueEncoder(aAttributeReports, aSubjectDescriptor.fabricIndex, aPath, - kDataVersion /* data version */, aIsFabricFiltered, state); + AttributeEncodeState state; + AttributeValueEncoder valueEncoder(aAttributeReports, aSubjectDescriptor, aPath, kDataVersion, aIsFabricFiltered, + state); CHIP_ERROR err = valueEncoder.EncodeList([](const auto & encoder) -> CHIP_ERROR { encoder.Encode(static_cast(1)); @@ -174,10 +170,9 @@ CHIP_ERROR ReadSingleClusterData(const Access::SubjectDescriptor & aSubjectDescr if (aPath.mClusterId == app::Clusters::IcdManagement::Id && aPath.mAttributeId == app::Clusters::IcdManagement::Attributes::OperatingMode::Id) { - AttributeValueEncoder::AttributeEncodeState state = - (apEncoderState == nullptr ? AttributeValueEncoder::AttributeEncodeState() : *apEncoderState); - AttributeValueEncoder valueEncoder(aAttributeReports, aSubjectDescriptor.fabricIndex, aPath, - kDataVersion /* data version */, aIsFabricFiltered, state); + AttributeEncodeState state(apEncoderState); + AttributeValueEncoder valueEncoder(aAttributeReports, aSubjectDescriptor, aPath, kDataVersion, aIsFabricFiltered, + state); return valueEncoder.Encode(isLitIcd ? Clusters::IcdManagement::OperatingModeEnum::kLit : Clusters::IcdManagement::OperatingModeEnum::kSit);