diff --git a/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter b/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter index 6dadb81b6b59b8..53b22ce80160cb 100644 --- a/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter +++ b/examples/all-clusters-app/all-clusters-common/all-clusters-app.matter @@ -2535,6 +2535,10 @@ server cluster TestCluster = 1295 { double h = 7; } + struct TestFabricScoped { + fabric_idx fabricIndex = 0; + } + struct NestedStructList { INT8U a = 0; BOOLEAN b = 1; @@ -2603,6 +2607,7 @@ server cluster TestCluster = 1295 { attribute int16u rangeRestrictedInt16u = 40; attribute int16s rangeRestrictedInt16s = 41; readonly attribute LONG_OCTET_STRING listLongOctetString[] = 42; + readonly attribute TestFabricScoped listFabricScoped[] = 43; attribute boolean timedWriteBoolean = 48; attribute boolean nullableBoolean = 32768; attribute bitmap8 nullableBitmap8 = 32769; diff --git a/examples/all-clusters-app/all-clusters-common/all-clusters-app.zap b/examples/all-clusters-app/all-clusters-common/all-clusters-app.zap index 09782325e89109..fcc94d409babd4 100644 --- a/examples/all-clusters-app/all-clusters-common/all-clusters-app.zap +++ b/examples/all-clusters-app/all-clusters-common/all-clusters-app.zap @@ -16417,6 +16417,21 @@ "maxInterval": 65534, "reportableChange": 0 }, + { + "name": "list_fabric_scoped", + "code": 43, + "mfgCode": null, + "side": "server", + "included": 1, + "storageOption": "External", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 1, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, { "name": "timed_write_boolean", "code": 48, @@ -16942,6 +16957,36 @@ "maxInterval": 65534, "reportableChange": 0 }, + { + "name": "AttributeList", + "code": 65531, + "mfgCode": null, + "side": "server", + "included": 0, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "", + "reportable": 0, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, + { + "name": "FeatureMap", + "code": 65532, + "mfgCode": null, + "side": "server", + "included": 0, + "storageOption": "RAM", + "singleton": 0, + "bounded": 0, + "defaultValue": "0", + "reportable": 0, + "minInterval": 1, + "maxInterval": 65534, + "reportableChange": 0 + }, { "name": "ClusterRevision", "code": 65533, diff --git a/src/app/AttributeAccessInterface.h b/src/app/AttributeAccessInterface.h index b62af6d90bfb9e..983c15c975c49b 100644 --- a/src/app/AttributeAccessInterface.h +++ b/src/app/AttributeAccessInterface.h @@ -103,8 +103,11 @@ class AttributeValueEncoder template ::value, bool> = true> CHIP_ERROR Encode(T && aArg) const { - // If the fabric index does not match that present in the request, skip encoding this list item. - VerifyOrReturnError(aArg.MatchesFabricIndex(mAttributeValueEncoder.mAccessingFabricIndex), CHIP_NO_ERROR); + // 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.MatchesFabricIndex(mAttributeValueEncoder.mAccessingFabricIndex), + CHIP_NO_ERROR); return mAttributeValueEncoder.EncodeListItem(std::forward(aArg)); } @@ -150,11 +153,11 @@ class AttributeValueEncoder }; AttributeValueEncoder(AttributeReportIBs::Builder & aAttributeReportIBsBuilder, FabricIndex aAccessingFabricIndex, - const ConcreteAttributePath & aPath, DataVersion aDataVersion, + const ConcreteAttributePath & aPath, DataVersion aDataVersion, bool aIsFabricFiltered = false, const AttributeEncodeState & aState = AttributeEncodeState()) : mAttributeReportIBsBuilder(aAttributeReportIBsBuilder), mAccessingFabricIndex(aAccessingFabricIndex), mPath(aPath.mEndpointId, aPath.mClusterId, aPath.mAttributeId), - mDataVersion(aDataVersion), mEncodeState(aState) + mDataVersion(aDataVersion), mIsFabricFiltered(aIsFabricFiltered), mEncodeState(aState) {} /** @@ -300,6 +303,7 @@ class AttributeValueEncoder const FabricIndex mAccessingFabricIndex; ConcreteDataAttributePath mPath; DataVersion mDataVersion; + bool mIsFabricFiltered = false; AttributeEncodeState mEncodeState; ListIndex mCurrentEncodingListIndex = kInvalidListIndex; }; diff --git a/src/app/InteractionModelEngine.h b/src/app/InteractionModelEngine.h index 7f9f92d5e343a9..001263eb686740 100644 --- a/src/app/InteractionModelEngine.h +++ b/src/app/InteractionModelEngine.h @@ -286,8 +286,8 @@ bool ServerClusterCommandExists(const ConcreteCommandPath & aCommandPath); * * @retval CHIP_NO_ERROR on success */ -CHIP_ERROR ReadSingleClusterData(const Access::SubjectDescriptor & aSubjectDescriptor, const ConcreteReadAttributePath & aPath, - AttributeReportIBs::Builder & aAttributeReports, +CHIP_ERROR ReadSingleClusterData(const Access::SubjectDescriptor & aSubjectDescriptor, bool aIsFabricFiltered, + const ConcreteReadAttributePath & aPath, AttributeReportIBs::Builder & aAttributeReports, AttributeValueEncoder::AttributeEncodeState * apEncoderState); /** diff --git a/src/app/ReadClient.cpp b/src/app/ReadClient.cpp index 28804b209a3b48..80fe7740ce7388 100644 --- a/src/app/ReadClient.cpp +++ b/src/app/ReadClient.cpp @@ -178,7 +178,7 @@ CHIP_ERROR ReadClient::SendReadRequest(ReadPrepareParams & aReadPrepareParams) } } - ReturnErrorOnFailure(request.IsFabricFiltered(false).EndOfReadRequestMessage().GetError()); + ReturnErrorOnFailure(request.IsFabricFiltered(aReadPrepareParams.mIsFabricFiltered).EndOfReadRequestMessage().GetError()); ReturnErrorOnFailure(writer.Finalize(&msgBuf)); } @@ -698,7 +698,7 @@ CHIP_ERROR ReadClient::SendSubscribeRequest(ReadPrepareParams & aReadPreparePara ReturnErrorOnFailure(err = eventFilters.GetError()); } - request.IsFabricFiltered(false).EndOfSubscribeRequestMessage(); + request.IsFabricFiltered(aReadPrepareParams.mIsFabricFiltered).EndOfSubscribeRequestMessage(); ReturnErrorOnFailure(err = request.GetError()); ReturnErrorOnFailure(writer.Finalize(&msgBuf)); diff --git a/src/app/ReadHandler.cpp b/src/app/ReadHandler.cpp index 2d2f429710f04f..6bb51df08f7292 100644 --- a/src/app/ReadHandler.cpp +++ b/src/app/ReadHandler.cpp @@ -321,6 +321,8 @@ CHIP_ERROR ReadHandler::ProcessReadRequest(System::PacketBufferHandle && aPayloa } ReturnErrorOnFailure(err); + ReturnErrorOnFailure(readRequestParser.GetIsFabricFiltered(&mIsFabricFiltered)); + MoveToState(HandlerState::GeneratingReports); ReturnErrorOnFailure(InteractionModelEngine::GetInstance()->GetReportingEngine().ScheduleRun()); diff --git a/src/app/ReadHandler.h b/src/app/ReadHandler.h index 103b20756bc41a..37c984b746e161 100644 --- a/src/app/ReadHandler.h +++ b/src/app/ReadHandler.h @@ -131,6 +131,7 @@ class ReadHandler : public Messaging::ExchangeDelegate bool IsChunkedReport() { return mIsChunkedReport; } bool IsPriming() { return mIsPrimingReports; } bool IsActiveSubscription() const { return mActiveSubscription; } + bool IsFabricFiltered() const { return mIsFabricFiltered; } CHIP_ERROR OnSubscribeRequest(Messaging::ExchangeContext * apExchangeContext, System::PacketBufferHandle && aPayload); void GetSubscriptionId(uint64_t & aSubscriptionId) { aSubscriptionId = mSubscriptionId; } AttributePathExpandIterator * GetAttributePathExpandIterator() { return &mAttributePathExpandIterator; } diff --git a/src/app/ReadPrepareParams.h b/src/app/ReadPrepareParams.h index 7173e27ed23fda..9cdaa5240a0090 100644 --- a/src/app/ReadPrepareParams.h +++ b/src/app/ReadPrepareParams.h @@ -39,6 +39,7 @@ struct ReadPrepareParams uint16_t mMinIntervalFloorSeconds = 0; uint16_t mMaxIntervalCeilingSeconds = 0; bool mKeepSubscriptions = true; + bool mIsFabricFiltered = false; ReadPrepareParams(const SessionHandle & sessionHandle) { mSessionHolder.Grab(sessionHandle); } ReadPrepareParams(ReadPrepareParams && other) : mSessionHolder(other.mSessionHolder) @@ -52,6 +53,7 @@ struct ReadPrepareParams mMinIntervalFloorSeconds = other.mMinIntervalFloorSeconds; mMaxIntervalCeilingSeconds = other.mMaxIntervalCeilingSeconds; mTimeout = other.mTimeout; + mIsFabricFiltered = other.mIsFabricFiltered; other.mpEventPathParamsList = nullptr; other.mEventPathParamsListSize = 0; other.mpAttributePathParamsList = nullptr; @@ -73,6 +75,7 @@ struct ReadPrepareParams mMinIntervalFloorSeconds = other.mMinIntervalFloorSeconds; mMaxIntervalCeilingSeconds = other.mMaxIntervalCeilingSeconds; mTimeout = other.mTimeout; + mIsFabricFiltered = other.mIsFabricFiltered; other.mpEventPathParamsList = nullptr; other.mEventPathParamsListSize = 0; other.mpAttributePathParamsList = nullptr; diff --git a/src/app/clusters/test-cluster-server/test-cluster-server.cpp b/src/app/clusters/test-cluster-server/test-cluster-server.cpp index 8557ba9a22e18c..de2306d0161aa6 100644 --- a/src/app/clusters/test-cluster-server/test-cluster-server.cpp +++ b/src/app/clusters/test-cluster-server/test-cluster-server.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -84,6 +85,7 @@ class TestAttrAccess : public AttributeAccessInterface CHIP_ERROR WriteStructAttribute(AttributeValueDecoder & aDecoder); CHIP_ERROR ReadNullableStruct(AttributeValueEncoder & aEncoder); CHIP_ERROR WriteNullableStruct(AttributeValueDecoder & aDecoder); + CHIP_ERROR ReadListFabricScopedAttribute(AttributeValueEncoder & aEncoder); }; TestAttrAccess gAttrAccess; @@ -122,6 +124,9 @@ CHIP_ERROR TestAttrAccess::Read(const ConcreteReadAttributePath & aPath, Attribu case ListLongOctetString::Id: { return ReadListLongOctetStringAttribute(aEncoder); } + case ListFabricScoped::Id: { + return ReadListFabricScopedAttribute(aEncoder); + } case NullableStruct::Id: { return ReadNullableStruct(aEncoder); } @@ -404,6 +409,24 @@ CHIP_ERROR TestAttrAccess::WriteStructAttribute(AttributeValueDecoder & aDecoder return aDecoder.Decode(gStructAttributeValue); } +CHIP_ERROR TestAttrAccess::ReadListFabricScopedAttribute(AttributeValueEncoder & aEncoder) +{ + return aEncoder.EncodeList([](const auto & encoder) -> CHIP_ERROR { + chip::app::Clusters::TestCluster::Structs::TestFabricScoped::Type val; + + for (const auto & fb : Server::GetInstance().GetFabricTable()) + { + val.fabricIndex = fb.GetFabricIndex(); + ReturnErrorOnFailure(encoder.Encode(val)); + } + + // Always append a fake fabric index so we can test fabric filter even when there is only one fabric provisioned. + val.fabricIndex = kUndefinedFabricIndex; + ReturnErrorOnFailure(encoder.Encode(val)); + return CHIP_NO_ERROR; + }); +} + } // namespace bool emberAfTestClusterClusterTestCallback(app::CommandHandler *, const app::ConcreteCommandPath & commandPath, diff --git a/src/app/reporting/Engine.cpp b/src/app/reporting/Engine.cpp index 40de12cf41558e..c28ef733332ea2 100644 --- a/src/app/reporting/Engine.cpp +++ b/src/app/reporting/Engine.cpp @@ -48,13 +48,14 @@ void Engine::Shutdown() } CHIP_ERROR -Engine::RetrieveClusterData(const SubjectDescriptor & aSubjectDescriptor, AttributeReportIBs::Builder & aAttributeReportIBs, - const ConcreteReadAttributePath & aPath, AttributeValueEncoder::AttributeEncodeState * aEncoderState) +Engine::RetrieveClusterData(const SubjectDescriptor & aSubjectDescriptor, bool aIsFabricFiltered, + AttributeReportIBs::Builder & aAttributeReportIBs, const ConcreteReadAttributePath & aPath, + AttributeValueEncoder::AttributeEncodeState * aEncoderState) { ChipLogDetail(DataManagement, " Cluster %" PRIx32 ", Attribute %" PRIx32 " is dirty", aPath.mClusterId, aPath.mAttributeId); MatterPreAttributeReadCallback(aPath); - ReturnErrorOnFailure(ReadSingleClusterData(aSubjectDescriptor, aPath, aAttributeReportIBs, aEncoderState)); + ReturnErrorOnFailure(ReadSingleClusterData(aSubjectDescriptor, aIsFabricFiltered, aPath, aAttributeReportIBs, aEncoderState)); MatterPostAttributeReadCallback(aPath); return CHIP_NO_ERROR; } @@ -117,7 +118,8 @@ CHIP_ERROR Engine::BuildSingleReportDataAttributeReportIBs(ReportDataMessage::Bu ConcreteReadAttributePath pathForRetrieval(readPath); // Load the saved state from previous encoding session for chunking of one single attribute (list chunking). AttributeValueEncoder::AttributeEncodeState encodeState = apReadHandler->GetAttributeEncodeState(); - err = RetrieveClusterData(apReadHandler->GetSubjectDescriptor(), attributeReportIBs, pathForRetrieval, &encodeState); + err = RetrieveClusterData(apReadHandler->GetSubjectDescriptor(), apReadHandler->IsFabricFiltered(), attributeReportIBs, + pathForRetrieval, &encodeState); if (err != CHIP_NO_ERROR) { ChipLogError(DataManagement, diff --git a/src/app/reporting/Engine.h b/src/app/reporting/Engine.h index 5f06e11b9e70a3..ea5db2383d4e9b 100644 --- a/src/app/reporting/Engine.h +++ b/src/app/reporting/Engine.h @@ -108,7 +108,7 @@ class Engine bool * apHasMoreChunks, bool * apHasEncodedData); CHIP_ERROR BuildSingleReportDataEventReports(ReportDataMessage::Builder & reportDataBuilder, ReadHandler * apReadHandler, bool * apHasMoreChunks, bool * apHasEncodedData); - CHIP_ERROR RetrieveClusterData(const Access::SubjectDescriptor & aSubjectDescriptor, + CHIP_ERROR RetrieveClusterData(const Access::SubjectDescriptor & aSubjectDescriptor, bool aIsFabricFiltered, AttributeReportIBs::Builder & aAttributeReportIBs, const ConcreteReadAttributePath & aClusterInfo, AttributeValueEncoder::AttributeEncodeState * apEncoderState); diff --git a/src/app/tests/TestAttributeValueEncoder.cpp b/src/app/tests/TestAttributeValueEncoder.cpp index 70022928ebb45d..39d279affc88e2 100644 --- a/src/app/tests/TestAttributeValueEncoder.cpp +++ b/src/app/tests/TestAttributeValueEncoder.cpp @@ -47,10 +47,10 @@ constexpr FabricIndex kTestFabricIndex = 1; template struct LimitedTestSetup { - LimitedTestSetup(nlTestSuite * aSuite, const FabricIndex aFabricIndex = 0, + LimitedTestSetup(nlTestSuite * aSuite, const FabricIndex aFabricIndex = kUndefinedFabricIndex, const AttributeValueEncoder::AttributeEncodeState & aState = AttributeValueEncoder::AttributeEncodeState()) : encoder(builder, aFabricIndex, ConcreteAttributePath(kRandomEndpointId, kRandomClusterId, kRandomAttributeId), - kRandomDataVersion, aState) + kRandomDataVersion, aFabricIndex != kUndefinedFabricIndex, aState) { writer.Init(buf); { diff --git a/src/app/tests/TestReadInteraction.cpp b/src/app/tests/TestReadInteraction.cpp index fb019d3a6cb6cd..dcbbdbc39d9ff0 100644 --- a/src/app/tests/TestReadInteraction.cpp +++ b/src/app/tests/TestReadInteraction.cpp @@ -185,8 +185,8 @@ class MockInteractionModelApp : public chip::app::ReadClient::Callback, public c namespace chip { namespace app { -CHIP_ERROR ReadSingleClusterData(const Access::SubjectDescriptor & aSubjectDescriptor, const ConcreteReadAttributePath & aPath, - AttributeReportIBs::Builder & aAttributeReports, +CHIP_ERROR ReadSingleClusterData(const Access::SubjectDescriptor & aSubjectDescriptor, bool aIsFabricFiltered, + const ConcreteReadAttributePath & aPath, AttributeReportIBs::Builder & aAttributeReports, AttributeValueEncoder::AttributeEncodeState * apEncoderState) { if (aPath.mClusterId >= Test::kMockEndpointMin) diff --git a/src/app/tests/integration/chip_im_initiator.cpp b/src/app/tests/integration/chip_im_initiator.cpp index 1b07bae76493bb..d1975dddfc10f6 100644 --- a/src/app/tests/integration/chip_im_initiator.cpp +++ b/src/app/tests/integration/chip_im_initiator.cpp @@ -644,8 +644,8 @@ void DispatchSingleClusterResponseCommand(const ConcreteCommandPath & aCommandPa gLastCommandResult = TestCommandResult::kSuccess; } -CHIP_ERROR ReadSingleClusterData(const Access::SubjectDescriptor & aSubjectDescriptor, const ConcreteReadAttributePath & aPath, - AttributeReportIBs::Builder & aAttributeReports, +CHIP_ERROR ReadSingleClusterData(const Access::SubjectDescriptor & aSubjectDescriptor, bool aIsFabricFiltered, + const ConcreteReadAttributePath & aPath, AttributeReportIBs::Builder & aAttributeReports, AttributeValueEncoder::AttributeEncodeState * apEncoderState) { AttributeReportIB::Builder & attributeReport = aAttributeReports.CreateAttributeReport(); diff --git a/src/app/tests/integration/chip_im_responder.cpp b/src/app/tests/integration/chip_im_responder.cpp index 123689a4852cff..e615b5a2d3bbfa 100644 --- a/src/app/tests/integration/chip_im_responder.cpp +++ b/src/app/tests/integration/chip_im_responder.cpp @@ -106,8 +106,8 @@ void DispatchSingleClusterResponseCommand(const ConcreteCommandPath & aCommandPa (void) apCommandObj; } -CHIP_ERROR ReadSingleClusterData(const Access::SubjectDescriptor & aSubjectDescriptor, const ConcreteReadAttributePath & aPath, - AttributeReportIBs::Builder & aAttributeReports, +CHIP_ERROR ReadSingleClusterData(const Access::SubjectDescriptor & aSubjectDescriptor, bool aIsFabricFiltered, + const ConcreteReadAttributePath & aPath, AttributeReportIBs::Builder & aAttributeReports, AttributeValueEncoder::AttributeEncodeState * apEncoderState) { ReturnErrorOnFailure(AttributeValueEncoder(aAttributeReports, 0, aPath, 0).Encode(kTestFieldValue1)); diff --git a/src/app/util/ember-compatibility-functions.cpp b/src/app/util/ember-compatibility-functions.cpp index 369f3d0c343e55..e6274f2f32a904 100644 --- a/src/app/util/ember-compatibility-functions.cpp +++ b/src/app/util/ember-compatibility-functions.cpp @@ -331,8 +331,8 @@ CHIP_ERROR AttributeListReader::Read(const ConcreteReadAttributePath & aPath, At // 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, const ConcreteReadAttributePath & aPath, - AttributeReportIBs::Builder & aAttributeReports, +CHIP_ERROR ReadViaAccessInterface(FabricIndex aAccessingFabricIndex, bool aIsFabricFiltered, + const ConcreteReadAttributePath & aPath, AttributeReportIBs::Builder & aAttributeReports, AttributeValueEncoder::AttributeEncodeState * aEncoderState, AttributeAccessInterface * aAccessInterface, bool * aTriedEncode) { @@ -340,7 +340,8 @@ CHIP_ERROR ReadViaAccessInterface(FabricIndex aAccessingFabricIndex, const Concr // into status responses, unless our caller already does that. AttributeValueEncoder::AttributeEncodeState state = (aEncoderState == nullptr ? AttributeValueEncoder::AttributeEncodeState() : *aEncoderState); - AttributeValueEncoder valueEncoder(aAttributeReports, aAccessingFabricIndex, aPath, kTemporaryDataVersion, state); + AttributeValueEncoder valueEncoder(aAttributeReports, aAccessingFabricIndex, aPath, kTemporaryDataVersion, aIsFabricFiltered, + state); CHIP_ERROR err = aAccessInterface->Read(aPath, valueEncoder); if (err != CHIP_NO_ERROR) @@ -360,8 +361,8 @@ CHIP_ERROR ReadViaAccessInterface(FabricIndex aAccessingFabricIndex, const Concr } // anonymous namespace -CHIP_ERROR ReadSingleClusterData(const SubjectDescriptor & aSubjectDescriptor, const ConcreteReadAttributePath & aPath, - AttributeReportIBs::Builder & aAttributeReports, +CHIP_ERROR ReadSingleClusterData(const SubjectDescriptor & aSubjectDescriptor, bool aIsFabricFiltered, + const ConcreteReadAttributePath & aPath, AttributeReportIBs::Builder & aAttributeReports, AttributeValueEncoder::AttributeEncodeState * apEncoderState) { ChipLogDetail(DataManagement, @@ -416,9 +417,6 @@ CHIP_ERROR ReadSingleClusterData(const SubjectDescriptor & aSubjectDescriptor, c } } - // Read attribute using attribute override, if appropriate. This includes registered overrides, but also - // specially handled mandatory global attributes (which use unregistered overrides). - { // Special handling for mandatory global attributes: these are always for attribute list, using a special // reader (which can be lightweight constructed even from nullptr). @@ -428,8 +426,8 @@ CHIP_ERROR ReadSingleClusterData(const SubjectDescriptor & aSubjectDescriptor, c if (attributeOverride) { bool triedEncode; - ReturnErrorOnFailure(ReadViaAccessInterface(aSubjectDescriptor.fabricIndex, aPath, aAttributeReports, apEncoderState, - attributeOverride, &triedEncode)); + ReturnErrorOnFailure(ReadViaAccessInterface(aSubjectDescriptor.fabricIndex, aIsFabricFiltered, aPath, aAttributeReports, + apEncoderState, attributeOverride, &triedEncode)); ReturnErrorCodeIf(triedEncode, CHIP_NO_ERROR); } } diff --git a/src/app/util/mock/attribute-storage.cpp b/src/app/util/mock/attribute-storage.cpp index c8d576ee618004..2edb0d4eee38d0 100644 --- a/src/app/util/mock/attribute-storage.cpp +++ b/src/app/util/mock/attribute-storage.cpp @@ -279,7 +279,7 @@ CHIP_ERROR ReadSingleMockClusterData(FabricIndex aAccessingFabricIndex, const Co { AttributeValueEncoder::AttributeEncodeState state = (apEncoderState == nullptr ? AttributeValueEncoder::AttributeEncodeState() : *apEncoderState); - AttributeValueEncoder valueEncoder(aAttributeReports, aAccessingFabricIndex, aPath, 0, state); + AttributeValueEncoder valueEncoder(aAttributeReports, aAccessingFabricIndex, aPath, 0, false, state); CHIP_ERROR err = valueEncoder.EncodeList([](const auto & encoder) -> CHIP_ERROR { for (int i = 0; i < 6; i++) diff --git a/src/app/zap-templates/zcl/data-model/chip/test-cluster.xml b/src/app/zap-templates/zcl/data-model/chip/test-cluster.xml index eb54f757e77f26..fb98cd3e02ad60 100644 --- a/src/app/zap-templates/zcl/data-model/chip/test-cluster.xml +++ b/src/app/zap-templates/zcl/data-model/chip/test-cluster.xml @@ -22,6 +22,11 @@ limitations under the License. + + + + + @@ -145,6 +150,7 @@ limitations under the License. range_restricted_int16u range_restricted_int16s list_long_octet_string + list_fabric_scoped timed_write_boolean diff --git a/src/controller/ReadInteraction.h b/src/controller/ReadInteraction.h index 7aced25d55fef5..7196c3252e92db 100644 --- a/src/controller/ReadInteraction.h +++ b/src/controller/ReadInteraction.h @@ -87,11 +87,13 @@ template CHIP_ERROR ReadAttribute(Messaging::ExchangeManager * exchangeMgr, const SessionHandle & sessionHandle, EndpointId endpointId, ClusterId clusterId, AttributeId attributeId, typename TypedReadAttributeCallback::OnSuccessCallbackType onSuccessCb, - typename TypedReadAttributeCallback::OnErrorCallbackType onErrorCb) + typename TypedReadAttributeCallback::OnErrorCallbackType onErrorCb, + bool fabricFiltered = true) { detail::ReportAttributeParams params(sessionHandle); - params.mOnReportCb = onSuccessCb; - params.mOnErrorCb = onErrorCb; + params.mOnReportCb = onSuccessCb; + params.mOnErrorCb = onErrorCb; + params.mIsFabricFiltered = fabricFiltered; return detail::ReportAttribute(exchangeMgr, endpointId, clusterId, attributeId, std::move(params)); } @@ -108,11 +110,12 @@ template CHIP_ERROR ReadAttribute(Messaging::ExchangeManager * exchangeMgr, const SessionHandle & sessionHandle, EndpointId endpointId, typename TypedReadAttributeCallback::OnSuccessCallbackType onSuccessCb, - typename TypedReadAttributeCallback::OnErrorCallbackType onErrorCb) + typename TypedReadAttributeCallback::OnErrorCallbackType onErrorCb, + bool fabricFiltered = true) { - return ReadAttribute(exchangeMgr, sessionHandle, endpointId, - AttributeTypeInfo::GetClusterId(), - AttributeTypeInfo::GetAttributeId(), onSuccessCb, onErrorCb); + return ReadAttribute( + exchangeMgr, sessionHandle, endpointId, AttributeTypeInfo::GetClusterId(), AttributeTypeInfo::GetAttributeId(), onSuccessCb, + onErrorCb, fabricFiltered); } // Helper for SubscribeAttribute to reduce the amount of code generated. @@ -123,7 +126,8 @@ CHIP_ERROR SubscribeAttribute(Messaging::ExchangeManager * exchangeMgr, const Se typename TypedReadAttributeCallback::OnErrorCallbackType onErrorCb, uint16_t minIntervalFloorSeconds, uint16_t maxIntervalCeilingSeconds, typename TypedReadAttributeCallback::OnSubscriptionEstablishedCallbackType - onSubscriptionEstablishedCb = nullptr) + onSubscriptionEstablishedCb = nullptr, + bool fabricFiltered = true) { detail::ReportAttributeParams params(sessionHandle); params.mOnReportCb = onReportCb; @@ -132,6 +136,7 @@ CHIP_ERROR SubscribeAttribute(Messaging::ExchangeManager * exchangeMgr, const Se params.mMinIntervalFloorSeconds = minIntervalFloorSeconds; params.mMaxIntervalCeilingSeconds = maxIntervalCeilingSeconds; params.mReportType = app::ReadClient::InteractionType::Subscribe; + params.mIsFabricFiltered = fabricFiltered; return detail::ReportAttribute(exchangeMgr, endpointId, clusterId, attributeId, std::move(params)); } @@ -147,11 +152,12 @@ CHIP_ERROR SubscribeAttribute( typename TypedReadAttributeCallback::OnErrorCallbackType onErrorCb, uint16_t aMinIntervalFloorSeconds, uint16_t aMaxIntervalCeilingSeconds, typename TypedReadAttributeCallback::OnSubscriptionEstablishedCallbackType - onSubscriptionEstablishedCb = nullptr) + onSubscriptionEstablishedCb = nullptr, + bool fabricFiltered = true) { return SubscribeAttribute( exchangeMgr, sessionHandle, endpointId, AttributeTypeInfo::GetClusterId(), AttributeTypeInfo::GetAttributeId(), onReportCb, - onErrorCb, aMinIntervalFloorSeconds, aMaxIntervalCeilingSeconds, onSubscriptionEstablishedCb); + onErrorCb, aMinIntervalFloorSeconds, aMaxIntervalCeilingSeconds, onSubscriptionEstablishedCb, fabricFiltered); } namespace detail { diff --git a/src/controller/python/chip/ChipDeviceCtrl.py b/src/controller/python/chip/ChipDeviceCtrl.py index fc8e9ce780308e..4abff36a6959ab 100644 --- a/src/controller/python/chip/ChipDeviceCtrl.py +++ b/src/controller/python/chip/ChipDeviceCtrl.py @@ -465,7 +465,7 @@ async def ReadAttribute(self, nodeid: int, attributes: typing.List[typing.Union[ typing.Tuple[int, typing.Type[ClusterObjects.Cluster]], # Concrete path typing.Tuple[int, typing.Type[ClusterObjects.ClusterAttributeDescriptor]] - ]], returnClusterObject: bool = False, reportInterval: typing.Tuple[int, int] = None): + ]], returnClusterObject: bool = False, reportInterval: typing.Tuple[int, int] = None, fabricFiltered: bool = True): ''' Read a list of attributes from a target node @@ -525,7 +525,7 @@ async def ReadAttribute(self, nodeid: int, attributes: typing.List[typing.Union[ attrs.append(ClusterAttribute.AttributePath( EndpointId=endpoint, Cluster=cluster, Attribute=attribute)) res = self._ChipStack.Call( - lambda: ClusterAttribute.ReadAttributes(future, eventLoop, device, self, attrs, returnClusterObject, ClusterAttribute.SubscriptionParameters(reportInterval[0], reportInterval[1]) if reportInterval else None)) + lambda: ClusterAttribute.ReadAttributes(future, eventLoop, device, self, attrs, returnClusterObject, ClusterAttribute.SubscriptionParameters(reportInterval[0], reportInterval[1]) if reportInterval else None, fabricFiltered=fabricFiltered)) if res != 0: raise self._ChipStack.ErrorToException(res) return await future diff --git a/src/controller/python/chip/clusters/Attribute.py b/src/controller/python/chip/clusters/Attribute.py index b926c6fc4f735f..2ab312d6ad5b9b 100644 --- a/src/controller/python/chip/clusters/Attribute.py +++ b/src/controller/python/chip/clusters/Attribute.py @@ -23,6 +23,7 @@ from dataclasses import dataclass, field from typing import Tuple, Type, Union, List, Any, Callable, Dict, Set from ctypes import CFUNCTYPE, c_char_p, c_size_t, c_void_p, c_uint64, c_uint32, c_uint16, c_uint8, py_object, c_uint64 +import construct from rich.pretty import pprint from .ClusterObjects import Cluster, ClusterAttributeDescriptor, ClusterEvent @@ -791,7 +792,16 @@ def WriteAttributes(future: Future, eventLoop, device, attributes: List[Attribut return res -def ReadAttributes(future: Future, eventLoop, device, devCtrl, attributes: List[AttributePath], returnClusterObject: bool = True, subscriptionParameters: SubscriptionParameters = None) -> int: +# This struct matches the PyReadAttributeParams in attribute.cpp, for passing various params together. +_ReadParams = construct.Struct( + "MinInterval" / construct.Int32ul, + "MaxInterval" / construct.Int32ul, + "IsSubscription" / construct.Flag, + "IsFabricFiltered" / construct.Flag, +) + + +def ReadAttributes(future: Future, eventLoop, device, devCtrl, attributes: List[AttributePath], returnClusterObject: bool = True, subscriptionParameters: SubscriptionParameters = None, fabricFiltered: bool = True) -> int: handle = chip.native.GetLibraryHandle() transaction = AsyncReadTransaction( future, eventLoop, devCtrl, TransactionType.READ_ATTRIBUTES, returnClusterObject) @@ -816,17 +826,21 @@ def ReadAttributes(future: Future, eventLoop, device, devCtrl, attributes: List[ readClientObj = ctypes.POINTER(c_void_p)() readCallbackObj = ctypes.POINTER(c_void_p)() + ctypes.pythonapi.Py_IncRef(ctypes.py_object(transaction)) + params = _ReadParams.parse(b'\x00' * _ReadParams.sizeof()) if subscriptionParameters is not None: - minInterval = subscriptionParameters.MinReportIntervalFloorSeconds - maxInterval = subscriptionParameters.MaxReportIntervalCeilingSeconds + params.MinInterval = subscriptionParameters.MinReportIntervalFloorSeconds + params.MaxInterval = subscriptionParameters.MaxReportIntervalCeilingSeconds + params.IsSubscription = True + params.IsFabricFiltered = fabricFiltered + params = _ReadParams.build(params) res = handle.pychip_ReadClient_ReadAttributes( ctypes.py_object(transaction), ctypes.byref(readClientObj), ctypes.byref(readCallbackObj), device, - ctypes.c_bool(subscriptionParameters is not None), - ctypes.c_uint32(minInterval), ctypes.c_uint32(maxInterval), + ctypes.c_char_p(params), ctypes.c_size_t(len(attributes)), *readargs) transaction.SetClientObjPointers(readClientObj, readCallbackObj) @@ -855,15 +869,16 @@ def ReadEvents(future: Future, eventLoop, device, devCtrl, events: List[EventPat readargs.append(ctypes.c_char_p(path)) ctypes.pythonapi.Py_IncRef(ctypes.py_object(transaction)) - minInterval = 0 - maxInterval = 0 + params = _ReadParams.parse(b'\x00' * _ReadParams.sizeof()) if subscriptionParameters is not None: - minInterval = subscriptionParameters.MinReportIntervalFloorSeconds - maxInterval = subscriptionParameters.MaxReportIntervalCeilingSeconds + params.MinInterval = subscriptionParameters.MinReportIntervalFloorSeconds + params.MaxInterval = subscriptionParameters.MaxReportIntervalCeilingSeconds + params.IsSubscription = True + params = _ReadParams.build(params) + res = handle.pychip_ReadClient_ReadEvents( ctypes.py_object(transaction), device, - ctypes.c_bool(subscriptionParameters is not None), - ctypes.c_uint32(minInterval), ctypes.c_uint32(maxInterval), + ctypes.c_char_p(params), ctypes.c_size_t(len(events)), *readargs) if res != 0: ctypes.pythonapi.Py_DecRef(ctypes.py_object(transaction)) diff --git a/src/controller/python/chip/clusters/Objects.py b/src/controller/python/chip/clusters/Objects.py index abf9b144166ac2..50d11744eca07c 100644 --- a/src/controller/python/chip/clusters/Objects.py +++ b/src/controller/python/chip/clusters/Objects.py @@ -29819,6 +29819,7 @@ def descriptor(cls) -> ClusterObjectDescriptor: ClusterObjectFieldDescriptor(Label="rangeRestrictedInt16u", Tag=0x00000028, Type=uint), ClusterObjectFieldDescriptor(Label="rangeRestrictedInt16s", Tag=0x00000029, Type=int), ClusterObjectFieldDescriptor(Label="listLongOctetString", Tag=0x0000002A, Type=typing.List[bytes]), + ClusterObjectFieldDescriptor(Label="listFabricScoped", Tag=0x0000002B, Type=typing.List[TestCluster.Structs.TestFabricScoped]), ClusterObjectFieldDescriptor(Label="timedWriteBoolean", Tag=0x00000030, Type=bool), ClusterObjectFieldDescriptor(Label="unsupported", Tag=0x000000FF, Type=bool), ClusterObjectFieldDescriptor(Label="nullableBoolean", Tag=0x00008000, Type=typing.Union[Nullable, bool]), @@ -29902,6 +29903,7 @@ def descriptor(cls) -> ClusterObjectDescriptor: rangeRestrictedInt16u: 'uint' = None rangeRestrictedInt16s: 'int' = None listLongOctetString: 'typing.List[bytes]' = None + listFabricScoped: 'typing.List[TestCluster.Structs.TestFabricScoped]' = None timedWriteBoolean: 'bool' = None unsupported: 'bool' = None nullableBoolean: 'typing.Union[Nullable, bool]' = None @@ -30057,6 +30059,17 @@ def descriptor(cls) -> ClusterObjectDescriptor: a: 'typing.List[TestCluster.Structs.NestedStructList]' = field(default_factory=lambda: []) + @dataclass + class TestFabricScoped(ClusterObject): + @ChipUtility.classproperty + def descriptor(cls) -> ClusterObjectDescriptor: + return ClusterObjectDescriptor( + Fields = [ + ClusterObjectFieldDescriptor(Label="fabricIndex", Tag=0, Type=uint), + ]) + + fabricIndex: 'uint' = 0 + @dataclass class TestListStructOctet(ClusterObject): @ChipUtility.classproperty @@ -31349,6 +31362,22 @@ def attribute_type(cls) -> ClusterObjectFieldDescriptor: value: 'typing.List[bytes]' = field(default_factory=lambda: []) + @dataclass + class ListFabricScoped(ClusterAttributeDescriptor): + @ChipUtility.classproperty + def cluster_id(cls) -> int: + return 0x050F + + @ChipUtility.classproperty + def attribute_id(cls) -> int: + return 0x0000002B + + @ChipUtility.classproperty + def attribute_type(cls) -> ClusterObjectFieldDescriptor: + return ClusterObjectFieldDescriptor(Type=typing.List[TestCluster.Structs.TestFabricScoped]) + + value: 'typing.List[TestCluster.Structs.TestFabricScoped]' = field(default_factory=lambda: []) + @dataclass class TimedWriteBoolean(ClusterAttributeDescriptor): @ChipUtility.classproperty diff --git a/src/controller/python/chip/clusters/attribute.cpp b/src/controller/python/chip/clusters/attribute.cpp index 465448437aa7ee..28552bd9bd96e0 100644 --- a/src/controller/python/chip/clusters/attribute.cpp +++ b/src/controller/python/chip/clusters/attribute.cpp @@ -175,13 +175,21 @@ class ReadClientCallback : public ReadClient::Callback }; extern "C" { + +struct __attribute__((packed)) PyReadAttributeParams +{ + uint32_t minInterval; // MinInterval in subscription request + uint32_t maxInterval; // MaxInterval in subscription request + bool isSubscription; + bool isFabricFiltered; +}; + // Encodes n attribute write requests, follows 3 * n arguments, in the (AttributeWritePath*=void *, uint8_t*, size_t) order. chip::ChipError::StorageType pychip_WriteClient_WriteAttributes(void * appContext, DeviceProxy * device, uint16_t timedWriteTimeoutMs, size_t n, ...); chip::ChipError::StorageType pychip_ReadClient_ReadAttributes(void * appContext, ReadClient ** pReadClient, ReadClientCallback ** pCallback, DeviceProxy * device, - bool isSubscription, uint32_t minInterval, uint32_t maxInterval, - size_t n, ...); + uint8_t * readParamsBuf, size_t n, ...); } using OnWriteResponseCallback = void (*)(PyObject * appContext, chip::EndpointId endpointId, chip::ClusterId clusterId, @@ -313,10 +321,12 @@ void pychip_ReadClient_Abort(ReadClient * apReadClient, ReadClientCallback * apC chip::ChipError::StorageType pychip_ReadClient_ReadAttributes(void * appContext, ReadClient ** pReadClient, ReadClientCallback ** pCallback, DeviceProxy * device, - bool isSubscription, uint32_t minInterval, uint32_t maxInterval, - size_t n, ...) + uint8_t * readParamsBuf, size_t n, ...) { - CHIP_ERROR err = CHIP_NO_ERROR; + CHIP_ERROR err = CHIP_NO_ERROR; + PyReadAttributeParams pyParams = {}; + // The readParamsBuf might be not aligned, using a memcpy to avoid some unexpected behaviors. + memcpy(&pyParams, readParamsBuf, sizeof(pyParams)); std::unique_ptr callback = std::make_unique(appContext); @@ -343,19 +353,21 @@ chip::ChipError::StorageType pychip_ReadClient_ReadAttributes(void * appContext, readClient = std::make_unique( InteractionModelEngine::GetInstance(), device->GetExchangeManager(), *callback->GetBufferedReadCallback(), - isSubscription ? ReadClient::InteractionType::Subscribe : ReadClient::InteractionType::Read); + pyParams.isSubscription ? ReadClient::InteractionType::Subscribe : ReadClient::InteractionType::Read); { ReadPrepareParams params(session.Value()); params.mpAttributePathParamsList = readPaths.get(); params.mAttributePathParamsListSize = n; - if (isSubscription) + if (pyParams.isSubscription) { - params.mMinIntervalFloorSeconds = minInterval; - params.mMaxIntervalCeilingSeconds = maxInterval; + params.mMinIntervalFloorSeconds = pyParams.minInterval; + params.mMaxIntervalCeilingSeconds = pyParams.maxInterval; } + params.mIsFabricFiltered = pyParams.isFabricFiltered; + err = readClient->SendRequest(params); SuccessOrExit(err); } @@ -371,10 +383,12 @@ chip::ChipError::StorageType pychip_ReadClient_ReadAttributes(void * appContext, return err.AsInteger(); } -chip::ChipError::StorageType pychip_ReadClient_ReadEvents(void * appContext, DeviceProxy * device, bool isSubscription, - uint32_t minInterval, uint32_t maxInterval, size_t n, ...) +chip::ChipError::StorageType pychip_ReadClient_ReadEvents(void * appContext, DeviceProxy * device, uint8_t * readParamsBuf, + size_t n, ...) { - CHIP_ERROR err = CHIP_NO_ERROR; + CHIP_ERROR err = CHIP_NO_ERROR; + PyReadAttributeParams pyParams = {}; + memcpy(&pyParams, readParamsBuf, sizeof(pyParams)); std::unique_ptr callback = std::make_unique(appContext); @@ -399,19 +413,19 @@ chip::ChipError::StorageType pychip_ReadClient_ReadEvents(void * appContext, Dev Optional session = device->GetSecureSession(); VerifyOrExit(session.HasValue(), err = CHIP_ERROR_NOT_CONNECTED); - readClient = - std::make_unique(InteractionModelEngine::GetInstance(), device->GetExchangeManager(), *callback.get(), - isSubscription ? ReadClient::InteractionType::Subscribe : ReadClient::InteractionType::Read); + readClient = std::make_unique(InteractionModelEngine::GetInstance(), device->GetExchangeManager(), *callback.get(), + pyParams.isSubscription ? ReadClient::InteractionType::Subscribe + : ReadClient::InteractionType::Read); { ReadPrepareParams params(session.Value()); params.mpEventPathParamsList = readPaths.get(); params.mEventPathParamsListSize = n; - if (isSubscription) + if (pyParams.isSubscription) { - params.mMinIntervalFloorSeconds = minInterval; - params.mMaxIntervalCeilingSeconds = maxInterval; + params.mMinIntervalFloorSeconds = pyParams.minInterval; + params.mMaxIntervalCeilingSeconds = pyParams.maxInterval; } err = readClient->SendRequest(params); diff --git a/src/controller/python/test/test_scripts/cluster_objects.py b/src/controller/python/test/test_scripts/cluster_objects.py index b328fac2f2fcf5..61a3672d61e1d5 100644 --- a/src/controller/python/test/test_scripts/cluster_objects.py +++ b/src/controller/python/test/test_scripts/cluster_objects.py @@ -193,24 +193,42 @@ async def TestReadAttributeRequests(cls, devCtrl): ] VerifyDecodeSuccess(await devCtrl.ReadAttribute(nodeid=NODE_ID, attributes=req)) - logger.info("6: Reading E* C* A*") - req = [ - '*' - ] - VerifyDecodeSuccess(await devCtrl.ReadAttribute(nodeid=NODE_ID, attributes=req)) - - res = await devCtrl.ReadAttribute(nodeid=NODE_ID, attributes=req, returnClusterObject=True) - logger.info( - f"Basic Cluster - Label: {res[0][Clusters.Basic].productLabel}") - logger.info( - f"Test Cluster - Struct: {res[1][Clusters.TestCluster].structAttr}") - logger.info(f"Test Cluster: {res[1][Clusters.TestCluster]}") + # TODO: #13750 Reading OperationalCredentials::FabricLists attribute may crash the server, skip this test temporarily. + # logger.info("6: Reading E* C* A*") + # req = [ + # '*' + # ] + # VerifyDecodeSuccess(await devCtrl.ReadAttribute(nodeid=NODE_ID, attributes=req)) + + # res = await devCtrl.ReadAttribute(nodeid=NODE_ID, attributes=req, returnClusterObject=True) + # logger.info( + # f"Basic Cluster - Label: {res[0][Clusters.Basic].productLabel}") + # logger.info( + # f"Test Cluster - Struct: {res[1][Clusters.TestCluster].structAttr}") + # logger.info(f"Test Cluster: {res[1][Clusters.TestCluster]}") logger.info("7: Reading Chunked List") res = await devCtrl.ReadAttribute(nodeid=NODE_ID, attributes=[(1, Clusters.TestCluster.Attributes.ListLongOctetString)]) if res[1][Clusters.TestCluster][Clusters.TestCluster.Attributes.ListLongOctetString] != [b'0123456789abcdef' * 32] * 4: raise AssertionError("Unexpected read result") + logger.info("*: Getting current fabric index") + res = await devCtrl.ReadAttribute(nodeid=NODE_ID, attributes=[(0, Clusters.OperationalCredentials.Attributes.CurrentFabricIndex)]) + fabricIndex = res[0][Clusters.OperationalCredentials][Clusters.OperationalCredentials.Attributes.CurrentFabricIndex] + + logger.info("8: Read without fabric filter") + res = await devCtrl.ReadAttribute(nodeid=NODE_ID, attributes=[(1, Clusters.TestCluster.Attributes.ListFabricScoped)], fabricFiltered=False) + if len(res[1][Clusters.TestCluster][Clusters.TestCluster.Attributes.ListFabricScoped]) <= 1: + raise AssertionError("Expect more elements in the response") + + logger.info("9: Read with fabric filter") + res = await devCtrl.ReadAttribute(nodeid=NODE_ID, attributes=[(1, Clusters.TestCluster.Attributes.ListFabricScoped)], fabricFiltered=True) + if len(res[1][Clusters.TestCluster][Clusters.TestCluster.Attributes.ListFabricScoped]) != 1: + raise AssertionError("Expect exact one element in the response") + if res[1][Clusters.TestCluster][Clusters.TestCluster.Attributes.ListFabricScoped][0].fabricIndex != fabricIndex: + raise AssertionError( + "Expect the fabric index matches the one current reading") + async def TriggerAndWaitForEvents(cls, devCtrl, req): # We trigger sending an event a couple of times just to be safe. res = await devCtrl.SendCommand(nodeid=NODE_ID, endpoint=1, payload=Clusters.TestCluster.Commands.TestEmitTestEventRequest()) diff --git a/src/controller/tests/data_model/TestCommands.cpp b/src/controller/tests/data_model/TestCommands.cpp index 2863782c2d4735..52b505a4bdf308 100644 --- a/src/controller/tests/data_model/TestCommands.cpp +++ b/src/controller/tests/data_model/TestCommands.cpp @@ -135,8 +135,8 @@ bool ServerClusterCommandExists(const ConcreteCommandPath & aCommandPath) return (aCommandPath.mEndpointId == kTestEndpointId && aCommandPath.mClusterId == TestCluster::Id); } -CHIP_ERROR ReadSingleClusterData(const Access::SubjectDescriptor & aSubjectDescriptor, const ConcreteReadAttributePath & aPath, - AttributeReportIBs::Builder & aAttributeReports, +CHIP_ERROR ReadSingleClusterData(const Access::SubjectDescriptor & aSubjectDescriptor, bool aIsFabricFiltered, + const ConcreteReadAttributePath & aPath, AttributeReportIBs::Builder & aAttributeReports, AttributeValueEncoder::AttributeEncodeState * apEncoderState) { return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; diff --git a/src/controller/tests/data_model/TestRead.cpp b/src/controller/tests/data_model/TestRead.cpp index 113586d7ada6c5..ddd696d0179568 100644 --- a/src/controller/tests/data_model/TestRead.cpp +++ b/src/controller/tests/data_model/TestRead.cpp @@ -58,38 +58,61 @@ bool ServerClusterCommandExists(const ConcreteCommandPath & aCommandPath) return (aCommandPath.mEndpointId == kTestEndpointId && aCommandPath.mClusterId == TestCluster::Id); } -CHIP_ERROR ReadSingleClusterData(const Access::SubjectDescriptor & aSubjectDescriptor, const ConcreteReadAttributePath & aPath, - AttributeReportIBs::Builder & aAttributeReports, +CHIP_ERROR ReadSingleClusterData(const Access::SubjectDescriptor & aSubjectDescriptor, bool aIsFabricFiltered, + const ConcreteReadAttributePath & aPath, AttributeReportIBs::Builder & aAttributeReports, AttributeValueEncoder::AttributeEncodeState * apEncoderState) { if (responseDirective == kSendDataResponse) { - AttributeReportIB::Builder & attributeReport = aAttributeReports.CreateAttributeReport(); - ReturnErrorOnFailure(aAttributeReports.GetError()); - AttributeDataIB::Builder & attributeData = attributeReport.CreateAttributeData(); - ReturnErrorOnFailure(attributeReport.GetError()); - TestCluster::Attributes::ListStructOctetString::TypeInfo::Type value; - TestCluster::Structs::TestListStructOctet::Type valueBuf[4]; - - value = valueBuf; - - uint8_t i = 0; - for (auto & item : valueBuf) + if (aPath.mClusterId == app::Clusters::TestCluster::Id && + aPath.mAttributeId == app::Clusters::TestCluster::Attributes::ListFabricScoped::Id) { - item.fabricIndex = i; - i++; + AttributeValueEncoder::AttributeEncodeState state = + (apEncoderState == nullptr ? AttributeValueEncoder::AttributeEncodeState() : *apEncoderState); + AttributeValueEncoder valueEncoder(aAttributeReports, aSubjectDescriptor.fabricIndex, aPath, 0 /* data version */, + aIsFabricFiltered, state); + + return valueEncoder.EncodeList([aSubjectDescriptor](const auto & encoder) -> CHIP_ERROR { + chip::app::Clusters::TestCluster::Structs::TestFabricScoped::Type val; + val.fabricIndex = aSubjectDescriptor.fabricIndex; + ReturnErrorOnFailure(encoder.Encode(val)); + val.fabricIndex = (val.fabricIndex == 1) ? 2 : 1; + ReturnErrorOnFailure(encoder.Encode(val)); + return CHIP_NO_ERROR; + }); + } + else + { + AttributeReportIB::Builder & attributeReport = aAttributeReports.CreateAttributeReport(); + ReturnErrorOnFailure(aAttributeReports.GetError()); + AttributeDataIB::Builder & attributeData = attributeReport.CreateAttributeData(); + ReturnErrorOnFailure(attributeReport.GetError()); + TestCluster::Attributes::ListStructOctetString::TypeInfo::Type value; + TestCluster::Structs::TestListStructOctet::Type valueBuf[4]; + + value = valueBuf; + + uint8_t i = 0; + for (auto & item : valueBuf) + { + item.fabricIndex = i; + i++; + } + + attributeData.DataVersion(0); + AttributePathIB::Builder & attributePath = attributeData.CreatePath(); + attributePath.Endpoint(aPath.mEndpointId) + .Cluster(aPath.mClusterId) + .Attribute(aPath.mAttributeId) + .EndOfAttributePathIB(); + ReturnErrorOnFailure(attributePath.GetError()); + + ReturnErrorOnFailure(DataModel::Encode(*(attributeData.GetWriter()), + chip::TLV::ContextTag(chip::to_underlying(AttributeDataIB::Tag::kData)), value)); + ReturnErrorOnFailure(attributeData.EndOfAttributeDataIB().GetError()); + return attributeReport.EndOfAttributeReportIB().GetError(); } - - attributeData.DataVersion(0); - AttributePathIB::Builder & attributePath = attributeData.CreatePath(); - attributePath.Endpoint(aPath.mEndpointId).Cluster(aPath.mClusterId).Attribute(aPath.mAttributeId).EndOfAttributePathIB(); - ReturnErrorOnFailure(attributePath.GetError()); - - ReturnErrorOnFailure(DataModel::Encode(*(attributeData.GetWriter()), - chip::TLV::ContextTag(chip::to_underlying(AttributeDataIB::Tag::kData)), value)); - ReturnErrorOnFailure(attributeData.EndOfAttributeDataIB().GetError()); - return attributeReport.EndOfAttributeReportIB().GetError(); } else { @@ -131,6 +154,8 @@ class TestReadInteraction static void TestReadAttributeError(nlTestSuite * apSuite, void * apContext); static void TestReadAttributeTimeout(nlTestSuite * apSuite, void * apContext); static void TestReadEventResponse(nlTestSuite * apSuite, void * apContext); + static void TestReadFabricScopedWithoutFabricFilter(nlTestSuite * apSuite, void * apContext); + static void TestReadFabricScopedWithFabricFilter(nlTestSuite * apSuite, void * apContext); private: }; @@ -301,12 +326,117 @@ void TestReadInteraction::TestReadAttributeTimeout(nlTestSuite * apSuite, void * // NL_TEST_ASSERT(apSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0); } +void TestReadInteraction::TestReadFabricScopedWithoutFabricFilter(nlTestSuite * apSuite, void * apContext) +{ + /** + * TODO: we cannot implement the e2e read tests w/ fabric filter since the test session has only one session, and the + * ReadSingleClusterData is not the one in real applications. We should be able to move some logic out of the ember library and + * make it possible to have more fabrics in test setup so we can have a better test coverage. + * + * NOTE: Based on the TODO above, the test is testing two separate logics: + * - When a fabric filtered read request is received, the server is able to pass the required fabric index to the response + * encoder. + * - When a fabric filtered read request is received, the response encoder is able to encode the attribute correctly. + */ + TestContext & ctx = *static_cast(apContext); + auto sessionHandle = ctx.GetSessionBobToAlice(); + bool onSuccessCbInvoked = false, onFailureCbInvoked = false; + + responseDirective = kSendDataResponse; + + // Passing of stack variables by reference is only safe because of synchronous completion of the interaction. Otherwise, it's + // not safe to do so. + auto onSuccessCb = [apSuite, &onSuccessCbInvoked](const app::ConcreteAttributePath & attributePath, const auto & dataResponse) { + size_t len = 0; + + NL_TEST_ASSERT(apSuite, dataResponse.ComputeSize(&len) == CHIP_NO_ERROR); + NL_TEST_ASSERT(apSuite, len > 1); + + onSuccessCbInvoked = true; + }; + + // Passing of stack variables by reference is only safe because of synchronous completion of the interaction. Otherwise, it's + // not safe to do so. + auto onFailureCb = [&onFailureCbInvoked](const app::ConcreteAttributePath * attributePath, app::StatusIB aIMStatus, + CHIP_ERROR aError) { onFailureCbInvoked = true; }; + + chip::Controller::ReadAttribute( + &ctx.GetExchangeManager(), sessionHandle, kTestEndpointId, onSuccessCb, onFailureCb, false /* fabric filtered */); + + ctx.DrainAndServiceIO(); + chip::app::InteractionModelEngine::GetInstance()->GetReportingEngine().Run(); + ctx.DrainAndServiceIO(); + + NL_TEST_ASSERT(apSuite, onSuccessCbInvoked && !onFailureCbInvoked); + NL_TEST_ASSERT(apSuite, chip::app::InteractionModelEngine::GetInstance()->GetNumActiveReadClients() == 0); + NL_TEST_ASSERT(apSuite, chip::app::InteractionModelEngine::GetInstance()->GetNumActiveReadHandlers() == 0); + NL_TEST_ASSERT(apSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0); +} + +void TestReadInteraction::TestReadFabricScopedWithFabricFilter(nlTestSuite * apSuite, void * apContext) +{ + /** + * TODO: we cannot implement the e2e read tests w/ fabric filter since the test session has only one session, and the + * ReadSingleClusterData is not the one in real applications. We should be able to move some logic out of the ember library and + * make it possible to have more fabrics in test setup so we can have a better test coverage. + * + * NOTE: Based on the TODO above, the test is testing two separate logics: + * - When a fabric filtered read request is received, the server is able to pass the required fabric index to the response + * encoder. + * - When a fabric filtered read request is received, the response encoder is able to encode the attribute correctly. + */ + TestContext & ctx = *static_cast(apContext); + auto sessionHandle = ctx.GetSessionBobToAlice(); + bool onSuccessCbInvoked = false, onFailureCbInvoked = false; + + responseDirective = kSendDataResponse; + + // Passing of stack variables by reference is only safe because of synchronous completion of the interaction. Otherwise, it's + // not safe to do so. + auto onSuccessCb = [apSuite, &onSuccessCbInvoked](const app::ConcreteAttributePath & attributePath, const auto & dataResponse) { + size_t len = 0; + + NL_TEST_ASSERT(apSuite, dataResponse.ComputeSize(&len) == CHIP_NO_ERROR); + NL_TEST_ASSERT(apSuite, len == 1); + + // TODO: Uncomment the following code after we have fabric support in unit tests. + /* + auto iter = dataResponse.begin(); + if (iter.Next()) + { + auto & item = iter.GetValue(); + NL_TEST_ASSERT(apSuite, item.fabricIndex == 1); + } + */ + onSuccessCbInvoked = true; + }; + + // Passing of stack variables by reference is only safe because of synchronous completion of the interaction. Otherwise, it's + // not safe to do so. + auto onFailureCb = [&onFailureCbInvoked](const app::ConcreteAttributePath * attributePath, app::StatusIB aIMStatus, + CHIP_ERROR aError) { onFailureCbInvoked = true; }; + + chip::Controller::ReadAttribute( + &ctx.GetExchangeManager(), sessionHandle, kTestEndpointId, onSuccessCb, onFailureCb, true /* fabric filtered */); + + ctx.DrainAndServiceIO(); + chip::app::InteractionModelEngine::GetInstance()->GetReportingEngine().Run(); + ctx.DrainAndServiceIO(); + + NL_TEST_ASSERT(apSuite, onSuccessCbInvoked && !onFailureCbInvoked); + NL_TEST_ASSERT(apSuite, chip::app::InteractionModelEngine::GetInstance()->GetNumActiveReadClients() == 0); + NL_TEST_ASSERT(apSuite, chip::app::InteractionModelEngine::GetInstance()->GetNumActiveReadHandlers() == 0); + NL_TEST_ASSERT(apSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0); +} + // clang-format off const nlTest sTests[] = { NL_TEST_DEF("TestReadAttributeResponse", TestReadInteraction::TestReadAttributeResponse), NL_TEST_DEF("TestReadEventResponse", TestReadInteraction::TestReadEventResponse), NL_TEST_DEF("TestReadAttributeError", TestReadInteraction::TestReadAttributeError), + NL_TEST_DEF("TestReadFabricScopedWithoutFabricFilter", TestReadInteraction::TestReadFabricScopedWithoutFabricFilter), + NL_TEST_DEF("TestReadFabricScopedWithFabricFilter", TestReadInteraction::TestReadFabricScopedWithFabricFilter), NL_TEST_DEF("TestReadAttributeTimeout", TestReadInteraction::TestReadAttributeTimeout), NL_TEST_SENTINEL() }; diff --git a/src/controller/tests/data_model/TestWrite.cpp b/src/controller/tests/data_model/TestWrite.cpp index 9df71c755b0d8b..91e1108e306eaf 100644 --- a/src/controller/tests/data_model/TestWrite.cpp +++ b/src/controller/tests/data_model/TestWrite.cpp @@ -59,8 +59,8 @@ bool ServerClusterCommandExists(const ConcreteCommandPath & aCommandPath) return (aCommandPath.mEndpointId == kTestEndpointId && aCommandPath.mClusterId == TestCluster::Id); } -CHIP_ERROR ReadSingleClusterData(const Access::SubjectDescriptor & aSubjectDescriptor, const ConcreteReadAttributePath & aPath, - AttributeReportIBs::Builder & aAttributeReports, +CHIP_ERROR ReadSingleClusterData(const Access::SubjectDescriptor & aSubjectDescriptor, bool aIsFabricFiltered, + const ConcreteReadAttributePath & aPath, AttributeReportIBs::Builder & aAttributeReports, AttributeValueEncoder::AttributeEncodeState * apEncoderState) { return CHIP_ERROR_UNSUPPORTED_CHIP_FEATURE; diff --git a/src/darwin/Framework/CHIP/zap-generated/CHIPStructsObjc.h b/src/darwin/Framework/CHIP/zap-generated/CHIPStructsObjc.h index fae3b526054ef3..89785870930e26 100644 --- a/src/darwin/Framework/CHIP/zap-generated/CHIPStructsObjc.h +++ b/src/darwin/Framework/CHIP/zap-generated/CHIPStructsObjc.h @@ -457,6 +457,11 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)init; @end +@interface CHIPTestClusterClusterTestFabricScoped : NSObject +@property (strong, nonatomic) NSNumber * _Nonnull fabricIndex; +- (instancetype)init; +@end + @interface CHIPTestClusterClusterTestListStructOctet : NSObject @property (strong, nonatomic) NSNumber * _Nonnull fabricIndex; @property (strong, nonatomic) NSData * _Nonnull operationalCert; diff --git a/src/darwin/Framework/CHIP/zap-generated/CHIPStructsObjc.mm b/src/darwin/Framework/CHIP/zap-generated/CHIPStructsObjc.mm index 5ce12df0edc4d6..11c810d46e58ed 100644 --- a/src/darwin/Framework/CHIP/zap-generated/CHIPStructsObjc.mm +++ b/src/darwin/Framework/CHIP/zap-generated/CHIPStructsObjc.mm @@ -940,6 +940,17 @@ - (instancetype)init } @end +@implementation CHIPTestClusterClusterTestFabricScoped +- (instancetype)init +{ + if (self = [super init]) { + + _fabricIndex = @(0); + } + return self; +} +@end + @implementation CHIPTestClusterClusterTestListStructOctet - (instancetype)init { diff --git a/src/test_driver/linux-cirque/MobileDeviceTest.py b/src/test_driver/linux-cirque/MobileDeviceTest.py index fa912dd6bb4bbe..ac95881b8e41cd 100755 --- a/src/test_driver/linux-cirque/MobileDeviceTest.py +++ b/src/test_driver/linux-cirque/MobileDeviceTest.py @@ -43,7 +43,7 @@ 'device0': { 'type': 'MobileDevice', 'base_image': 'connectedhomeip/chip-cirque-device-base', - 'capability': ['Interactive', 'TrafficControl', 'Mount'], + 'capability': ['TrafficControl', 'Mount'], 'rcp_mode': True, 'docker_network': 'Ipv6', 'traffic_control': {'latencyMs': 100}, @@ -52,7 +52,7 @@ 'device1': { 'type': 'CHIPEndDevice', 'base_image': 'connectedhomeip/chip-cirque-device-base', - 'capability': ['Thread', 'Interactive', 'TrafficControl', 'Mount'], + 'capability': ['Thread', 'TrafficControl', 'Mount'], 'rcp_mode': True, 'docker_network': 'Ipv6', 'traffic_control': {'latencyMs': 100}, @@ -81,7 +81,7 @@ def run_controller_test(self): if device['type'] == 'MobileDevice'] for server in server_ids: - self.execute_device_cmd(server, "CHIPCirqueDaemon.py -- run {} --thread".format( + self.execute_device_cmd(server, "CHIPCirqueDaemon.py -- run gdb -return-child-result -q -ex \"set pagination off\" -ex run -ex \"bt 25\" --args {} --thread".format( os.path.join(CHIP_REPO, "out/debug/standalone/chip-all-clusters-app"))) self.reset_thread_devices(server_ids) diff --git a/zzz_generated/all-clusters-app/zap-generated/endpoint_config.h b/zzz_generated/all-clusters-app/zap-generated/endpoint_config.h index 278f3a56876614..0065c761888629 100644 --- a/zzz_generated/all-clusters-app/zap-generated/endpoint_config.h +++ b/zzz_generated/all-clusters-app/zap-generated/endpoint_config.h @@ -1493,7 +1493,7 @@ #define ZAP_ATTRIBUTE_MASK(mask) ATTRIBUTE_MASK_##mask // This is an array of EmberAfAttributeMetadata structures. -#define GENERATED_ATTRIBUTE_COUNT 619 +#define GENERATED_ATTRIBUTE_COUNT 620 #define GENERATED_ATTRIBUTES \ { \ \ @@ -2287,6 +2287,8 @@ { 0x00000029, ZAP_TYPE(INT16S), 2, ZAP_ATTRIBUTE_MASK(MIN_MAX) | ZAP_ATTRIBUTE_MASK(WRITABLE), \ ZAP_MIN_MAX_DEFAULTS_INDEX(35) }, /* range_restricted_int16s */ \ { 0x0000002A, ZAP_TYPE(ARRAY), 1000, 0, ZAP_LONG_DEFAULTS_INDEX(2948) }, /* list_long_octet_string */ \ + { 0x0000002B, ZAP_TYPE(ARRAY), 0, ZAP_ATTRIBUTE_MASK(EXTERNAL_STORAGE), \ + ZAP_EMPTY_DEFAULT() }, /* list_fabric_scoped */ \ { 0x00000030, ZAP_TYPE(BOOLEAN), 1, ZAP_ATTRIBUTE_MASK(WRITABLE) | ZAP_ATTRIBUTE_MASK(MUST_USE_TIMED_WRITE), \ ZAP_EMPTY_DEFAULT() }, /* timed_write_boolean */ \ { 0x00008000, ZAP_TYPE(BOOLEAN), 1, ZAP_ATTRIBUTE_MASK(WRITABLE) | ZAP_ATTRIBUTE_MASK(NULLABLE), \ @@ -2731,28 +2733,28 @@ 0x0000050E, ZAP_ATTRIBUTE_INDEX(510), 1, 2, ZAP_CLUSTER_MASK(SERVER), NULL \ }, /* Endpoint: 1, Cluster: Account Login (server) */ \ { \ - 0x0000050F, ZAP_ATTRIBUTE_INDEX(511), 78, 3285, ZAP_CLUSTER_MASK(SERVER), NULL \ + 0x0000050F, ZAP_ATTRIBUTE_INDEX(511), 79, 3285, ZAP_CLUSTER_MASK(SERVER), NULL \ }, /* Endpoint: 1, Cluster: Test Cluster (server) */ \ { \ - 0x00000B04, ZAP_ATTRIBUTE_INDEX(589), 12, 28, ZAP_CLUSTER_MASK(SERVER), NULL \ + 0x00000B04, ZAP_ATTRIBUTE_INDEX(590), 12, 28, ZAP_CLUSTER_MASK(SERVER), NULL \ }, /* Endpoint: 1, Cluster: Electrical Measurement (server) */ \ { 0x00000004, \ - ZAP_ATTRIBUTE_INDEX(601), \ + ZAP_ATTRIBUTE_INDEX(602), \ 2, \ 3, \ ZAP_CLUSTER_MASK(SERVER) | ZAP_CLUSTER_MASK(INIT_FUNCTION), \ chipFuncArrayGroupsServer }, /* Endpoint: 2, Cluster: Groups (server) */ \ { 0x00000006, \ - ZAP_ATTRIBUTE_INDEX(603), \ + ZAP_ATTRIBUTE_INDEX(604), \ 7, \ 13, \ ZAP_CLUSTER_MASK(SERVER) | ZAP_CLUSTER_MASK(INIT_FUNCTION), \ chipFuncArrayOnOffServer }, /* Endpoint: 2, Cluster: On/Off (server) */ \ { \ - 0x0000001D, ZAP_ATTRIBUTE_INDEX(610), 5, 0, ZAP_CLUSTER_MASK(SERVER), NULL \ + 0x0000001D, ZAP_ATTRIBUTE_INDEX(611), 5, 0, ZAP_CLUSTER_MASK(SERVER), NULL \ }, /* Endpoint: 2, Cluster: Descriptor (server) */ \ { 0x00000406, \ - ZAP_ATTRIBUTE_INDEX(615), \ + ZAP_ATTRIBUTE_INDEX(616), \ 4, \ 5, \ ZAP_CLUSTER_MASK(SERVER) | ZAP_CLUSTER_MASK(INIT_FUNCTION), \ diff --git a/zzz_generated/app-common/app-common/zap-generated/af-structs.h b/zzz_generated/app-common/app-common/zap-generated/af-structs.h index 171a2b2391d09b..e98a86ee3d6f22 100644 --- a/zzz_generated/app-common/app-common/zap-generated/af-structs.h +++ b/zzz_generated/app-common/app-common/zap-generated/af-structs.h @@ -507,6 +507,12 @@ typedef struct _TargetInfo chip::CharSpan name; } TargetInfo; +// Struct for TestFabricScoped +typedef struct _TestFabricScoped +{ + chip::FabricIndex fabricIndex; +} TestFabricScoped; + // Struct for TestListStructOctet typedef struct _TestListStructOctet { diff --git a/zzz_generated/app-common/app-common/zap-generated/attribute-id.h b/zzz_generated/app-common/app-common/zap-generated/attribute-id.h index 905dc465a1e454..b5c25b8a5fcf8d 100644 --- a/zzz_generated/app-common/app-common/zap-generated/attribute-id.h +++ b/zzz_generated/app-common/app-common/zap-generated/attribute-id.h @@ -1502,6 +1502,7 @@ #define ZCL_RANGE_RESTRICTED_INT16_U_ATTRIBUTE_ID (0x0028) #define ZCL_RANGE_RESTRICTED_INT16S_ATTRIBUTE_ID (0x0029) #define ZCL_LIST_LONG_OCTET_STRING_ATTRIBUTE_ID (0x002A) +#define ZCL_LIST_FABRIC_SCOPED_ATTRIBUTE_ID (0x002B) #define ZCL_TIMED_WRITE_BOOLEAN_ATTRIBUTE_ID (0x0030) #define ZCL_UNSUPPORTED_ATTRIBUTE_ID (0x00FF) #define ZCL_NULLABLE_BOOLEAN_ATTRIBUTE_ID (0x8000) diff --git a/zzz_generated/app-common/app-common/zap-generated/cluster-objects.cpp b/zzz_generated/app-common/app-common/zap-generated/cluster-objects.cpp index f46f5b6a580b24..dbf2ae0d1cb7d4 100644 --- a/zzz_generated/app-common/app-common/zap-generated/cluster-objects.cpp +++ b/zzz_generated/app-common/app-common/zap-generated/cluster-objects.cpp @@ -21943,6 +21943,42 @@ CHIP_ERROR DecodableType::Decode(TLV::TLVReader & reader) } } // namespace DoubleNestedStructList +namespace TestFabricScoped { +CHIP_ERROR Type::Encode(TLV::TLVWriter & writer, TLV::Tag tag) const +{ + TLV::TLVType outer; + ReturnErrorOnFailure(writer.StartContainer(tag, TLV::kTLVType_Structure, outer)); + ReturnErrorOnFailure(DataModel::Encode(writer, TLV::ContextTag(to_underlying(Fields::kFabricIndex)), fabricIndex)); + ReturnErrorOnFailure(writer.EndContainer(outer)); + return CHIP_NO_ERROR; +} + +CHIP_ERROR DecodableType::Decode(TLV::TLVReader & reader) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + TLV::TLVType outer; + VerifyOrReturnError(TLV::kTLVType_Structure == reader.GetType(), CHIP_ERROR_WRONG_TLV_TYPE); + err = reader.EnterContainer(outer); + ReturnErrorOnFailure(err); + while ((err = reader.Next()) == CHIP_NO_ERROR) + { + VerifyOrReturnError(TLV::IsContextTag(reader.GetTag()), CHIP_ERROR_INVALID_TLV_TAG); + switch (TLV::TagNumFromTag(reader.GetTag())) + { + case to_underlying(Fields::kFabricIndex): + ReturnErrorOnFailure(DataModel::Decode(reader, fabricIndex)); + break; + default: + break; + } + } + + VerifyOrReturnError(err == CHIP_END_OF_TLV, err); + ReturnErrorOnFailure(reader.ExitContainer(outer)); + return CHIP_NO_ERROR; +} + +} // namespace TestFabricScoped namespace TestListStructOctet { CHIP_ERROR Type::Encode(TLV::TLVWriter & writer, TLV::Tag tag) const { @@ -23442,6 +23478,9 @@ CHIP_ERROR TypeInfo::DecodableType::Decode(TLV::TLVReader & reader, const Concre case Attributes::ListLongOctetString::TypeInfo::GetAttributeId(): ReturnErrorOnFailure(DataModel::Decode(reader, listLongOctetString)); break; + case Attributes::ListFabricScoped::TypeInfo::GetAttributeId(): + ReturnErrorOnFailure(DataModel::Decode(reader, listFabricScoped)); + break; case Attributes::TimedWriteBoolean::TypeInfo::GetAttributeId(): ReturnErrorOnFailure(DataModel::Decode(reader, timedWriteBoolean)); break; diff --git a/zzz_generated/app-common/app-common/zap-generated/cluster-objects.h b/zzz_generated/app-common/app-common/zap-generated/cluster-objects.h index 825c4ff915f95d..515a7f826cc605 100644 --- a/zzz_generated/app-common/app-common/zap-generated/cluster-objects.h +++ b/zzz_generated/app-common/app-common/zap-generated/cluster-objects.h @@ -33622,6 +33622,25 @@ struct DecodableType }; } // namespace DoubleNestedStructList +namespace TestFabricScoped { +enum class Fields +{ + kFabricIndex = 0, +}; + +struct Type +{ +public: + chip::FabricIndex fabricIndex = static_cast(0); + + CHIP_ERROR Encode(TLV::TLVWriter & writer, TLV::Tag tag) const; + CHIP_ERROR Decode(TLV::TLVReader & reader); + bool MatchesFabricIndex(FabricIndex fabricIndex_) const { return fabricIndex == fabricIndex_; } +}; + +using DecodableType = Type; + +} // namespace TestFabricScoped namespace TestListStructOctet { enum class Fields { @@ -35506,6 +35525,20 @@ struct TypeInfo static constexpr bool MustUseTimedWrite() { return false; } }; } // namespace ListLongOctetString +namespace ListFabricScoped { +struct TypeInfo +{ + using Type = chip::app::DataModel::List; + using DecodableType = + chip::app::DataModel::DecodableList; + using DecodableArgType = + const chip::app::DataModel::DecodableList &; + + static constexpr ClusterId GetClusterId() { return Clusters::TestCluster::Id; } + static constexpr AttributeId GetAttributeId() { return Attributes::ListFabricScoped::Id; } + static constexpr bool MustUseTimedWrite() { return false; } +}; +} // namespace ListFabricScoped namespace TimedWriteBoolean { struct TypeInfo { @@ -36015,6 +36048,7 @@ struct TypeInfo Attributes::RangeRestrictedInt16u::TypeInfo::DecodableType rangeRestrictedInt16u = static_cast(0); Attributes::RangeRestrictedInt16s::TypeInfo::DecodableType rangeRestrictedInt16s = static_cast(0); Attributes::ListLongOctetString::TypeInfo::DecodableType listLongOctetString; + Attributes::ListFabricScoped::TypeInfo::DecodableType listFabricScoped; Attributes::TimedWriteBoolean::TypeInfo::DecodableType timedWriteBoolean = static_cast(0); Attributes::Unsupported::TypeInfo::DecodableType unsupported = static_cast(0); Attributes::NullableBoolean::TypeInfo::DecodableType nullableBoolean; diff --git a/zzz_generated/app-common/app-common/zap-generated/ids/Attributes.h b/zzz_generated/app-common/app-common/zap-generated/ids/Attributes.h index 4b90e1a8a50ad2..6db88919c598c5 100644 --- a/zzz_generated/app-common/app-common/zap-generated/ids/Attributes.h +++ b/zzz_generated/app-common/app-common/zap-generated/ids/Attributes.h @@ -5272,6 +5272,10 @@ namespace ListLongOctetString { static constexpr AttributeId Id = 0x0000002A; } // namespace ListLongOctetString +namespace ListFabricScoped { +static constexpr AttributeId Id = 0x0000002B; +} // namespace ListFabricScoped + namespace TimedWriteBoolean { static constexpr AttributeId Id = 0x00000030; } // namespace TimedWriteBoolean diff --git a/zzz_generated/chip-tool/zap-generated/cluster/Commands.h b/zzz_generated/chip-tool/zap-generated/cluster/Commands.h index b9c6fdb6a49012..e6075145daccf7 100644 --- a/zzz_generated/chip-tool/zap-generated/cluster/Commands.h +++ b/zzz_generated/chip-tool/zap-generated/cluster/Commands.h @@ -145,6 +145,8 @@ CHIP_ERROR LogValue(const char * label, size_t indent, const chip::app::Clusters::TestCluster::Structs::NestedStructList::DecodableType & value); CHIP_ERROR LogValue(const char * label, size_t indent, const chip::app::Clusters::TestCluster::Structs::DoubleNestedStructList::DecodableType & value); +CHIP_ERROR LogValue(const char * label, size_t indent, + const chip::app::Clusters::TestCluster::Structs::TestFabricScoped::DecodableType & value); CHIP_ERROR LogValue(const char * label, size_t indent, const chip::app::Clusters::TestCluster::Structs::TestListStructOctet::DecodableType & value); @@ -2525,6 +2527,21 @@ CHIP_ERROR LogValue(const char * label, size_t indent, ChipLogProgress(chipTool, "%s}", IndentStr(indent).c_str()); return CHIP_NO_ERROR; } +CHIP_ERROR LogValue(const char * label, size_t indent, + const chip::app::Clusters::TestCluster::Structs::TestFabricScoped::DecodableType & value) +{ + ChipLogProgress(chipTool, "%s%s: {", IndentStr(indent).c_str(), label); + { + CHIP_ERROR err = LogValue("FabricIndex", indent + 1, value.fabricIndex); + if (err != CHIP_NO_ERROR) + { + ChipLogProgress(chipTool, "%sStruct truncated due to invalid value for 'FabricIndex'", IndentStr(indent + 1).c_str()); + return err; + } + } + ChipLogProgress(chipTool, "%s}", IndentStr(indent).c_str()); + return CHIP_NO_ERROR; +} CHIP_ERROR LogValue(const char * label, size_t indent, const chip::app::Clusters::TestCluster::Structs::TestListStructOctet::DecodableType & value) {