Skip to content

Commit

Permalink
[IM] Fix Fabric filtered reading (#13404)
Browse files Browse the repository at this point in the history
* [IM] Fix fabric scoped read support

* Add controller interface support & tests

* Update Python Interface

* Add GDB when running programs

* Address comments

* Disable wildcard read temporarily

* Default fabricFiltered to true

* Run Codegen
  • Loading branch information
erjiaqing authored and pull[bot] committed Feb 17, 2022
1 parent f60b7e8 commit 2688417
Show file tree
Hide file tree
Showing 37 changed files with 544 additions and 124 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
45 changes: 45 additions & 0 deletions examples/all-clusters-app/all-clusters-common/all-clusters-app.zap
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
12 changes: 8 additions & 4 deletions src/app/AttributeAccessInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,11 @@ class AttributeValueEncoder
template <typename T, std::enable_if_t<DataModel::IsFabricScoped<T>::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<T>(aArg));
}

Expand Down Expand Up @@ -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)
{}

/**
Expand Down Expand Up @@ -300,6 +303,7 @@ class AttributeValueEncoder
const FabricIndex mAccessingFabricIndex;
ConcreteDataAttributePath mPath;
DataVersion mDataVersion;
bool mIsFabricFiltered = false;
AttributeEncodeState mEncodeState;
ListIndex mCurrentEncodingListIndex = kInvalidListIndex;
};
Expand Down
4 changes: 2 additions & 2 deletions src/app/InteractionModelEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

/**
Expand Down
4 changes: 2 additions & 2 deletions src/app/ReadClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}

Expand Down Expand Up @@ -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));
Expand Down
2 changes: 2 additions & 0 deletions src/app/ReadHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
1 change: 1 addition & 0 deletions src/app/ReadHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
Expand Down
3 changes: 3 additions & 0 deletions src/app/ReadPrepareParams.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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;
Expand All @@ -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;
Expand Down
23 changes: 23 additions & 0 deletions src/app/clusters/test-cluster-server/test-cluster-server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include <app/CommandHandler.h>
#include <app/ConcreteCommandPath.h>
#include <app/EventLogging.h>
#include <app/server/Server.h>
#include <app/util/attribute-storage.h>
#include <lib/core/CHIPSafeCasts.h>
#include <lib/core/CHIPTLV.h>
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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,
Expand Down
10 changes: 6 additions & 4 deletions src/app/reporting/Engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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, "<RE:Run> 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;
}
Expand Down Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion src/app/reporting/Engine.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 2 additions & 2 deletions src/app/tests/TestAttributeValueEncoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,10 @@ constexpr FabricIndex kTestFabricIndex = 1;
template <size_t N>
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);
{
Expand Down
4 changes: 2 additions & 2 deletions src/app/tests/TestReadInteraction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions src/app/tests/integration/chip_im_initiator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
4 changes: 2 additions & 2 deletions src/app/tests/integration/chip_im_responder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
18 changes: 8 additions & 10 deletions src/app/util/ember-compatibility-functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -331,16 +331,17 @@ 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)
{
// TODO: We should probably clone the writer and convert failures here
// 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)
Expand All @@ -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,
Expand Down Expand Up @@ -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).
Expand All @@ -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);
}
}
Expand Down
Loading

0 comments on commit 2688417

Please sign in to comment.