Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[IM] Add Fabric Filtered Write Support #13756

Merged
merged 16 commits into from
Feb 1, 2022
Merged
118 changes: 118 additions & 0 deletions examples/light-switch-app/light-switch-common/light-switch-app.matter
Original file line number Diff line number Diff line change
Expand Up @@ -595,6 +595,35 @@ client cluster Groups = 4 {
readonly attribute bitmap8 nameSupport = 0;
readonly global attribute attrib_id attributeList[] = 65531;
readonly global attribute int16u clusterRevision = 65533;

request struct AddGroupRequest {
INT16U groupId = 0;
CHAR_STRING groupName = 1;
}

request struct AddGroupIfIdentifyingRequest {
INT16U groupId = 0;
CHAR_STRING groupName = 1;
}

request struct GetGroupMembershipRequest {
INT16U groupList[] = 0;
}

request struct RemoveGroupRequest {
INT16U groupId = 0;
}

request struct ViewGroupRequest {
INT16U groupId = 0;
}

command AddGroup(AddGroupRequest): AddGroupResponse = 0;
command AddGroupIfIdentifying(AddGroupIfIdentifyingRequest): DefaultSuccess = 5;
command GetGroupMembership(GetGroupMembershipRequest): GetGroupMembershipResponse = 2;
command RemoveAllGroups(): DefaultSuccess = 4;
command RemoveGroup(RemoveGroupRequest): RemoveGroupResponse = 3;
command ViewGroup(ViewGroupRequest): ViewGroupResponse = 1;
}

client cluster Identify = 3 {
Expand Down Expand Up @@ -964,6 +993,10 @@ client cluster OnOff = 6 {
readonly global attribute attrib_id attributeList[] = 65531;
readonly global attribute bitmap32 featureMap = 65532;
readonly global attribute int16u clusterRevision = 65533;

command Off(): DefaultSuccess = 0;
command On(): DefaultSuccess = 1;
command Toggle(): DefaultSuccess = 2;
}

server cluster OperationalCredentials = 62 {
Expand Down Expand Up @@ -1093,6 +1126,91 @@ client cluster Scenes = 5 {
readonly attribute bitmap8 nameSupport = 4;
readonly global attribute attrib_id attributeList[] = 65531;
readonly global attribute int16u clusterRevision = 65533;

request struct AddSceneRequest {
INT16U groupId = 0;
INT8U sceneId = 1;
INT16U transitionTime = 2;
CHAR_STRING sceneName = 3;
SceneExtensionFieldSet extensionFieldSets[] = 4;
}

request struct GetSceneMembershipRequest {
INT16U groupId = 0;
}

request struct RecallSceneRequest {
INT16U groupId = 0;
INT8U sceneId = 1;
INT16U transitionTime = 2;
}

request struct RemoveAllScenesRequest {
INT16U groupId = 0;
}

request struct RemoveSceneRequest {
INT16U groupId = 0;
INT8U sceneId = 1;
}

request struct StoreSceneRequest {
INT16U groupId = 0;
INT8U sceneId = 1;
}

request struct ViewSceneRequest {
INT16U groupId = 0;
INT8U sceneId = 1;
}

response struct AddSceneResponse {
ENUM8 status = 0;
INT16U groupId = 1;
INT8U sceneId = 2;
}

response struct GetSceneMembershipResponse {
ENUM8 status = 0;
INT8U capacity = 1;
INT16U groupId = 2;
INT8U sceneCount = 3;
INT8U sceneList[] = 4;
}

response struct RemoveAllScenesResponse {
ENUM8 status = 0;
INT16U groupId = 1;
}

response struct RemoveSceneResponse {
ENUM8 status = 0;
INT16U groupId = 1;
INT8U sceneId = 2;
}

response struct StoreSceneResponse {
ENUM8 status = 0;
INT16U groupId = 1;
INT8U sceneId = 2;
}

response struct ViewSceneResponse {
ENUM8 status = 0;
INT16U groupId = 1;
INT8U sceneId = 2;
INT16U transitionTime = 3;
CHAR_STRING sceneName = 4;
SceneExtensionFieldSet extensionFieldSets[] = 5;
}

command AddScene(AddSceneRequest): AddSceneResponse = 0;
command GetSceneMembership(GetSceneMembershipRequest): GetSceneMembershipResponse = 6;
command RecallScene(RecallSceneRequest): DefaultSuccess = 5;
command RemoveAllScenes(RemoveAllScenesRequest): RemoveAllScenesResponse = 3;
command RemoveScene(RemoveSceneRequest): RemoveSceneResponse = 2;
command StoreScene(StoreSceneRequest): StoreSceneResponse = 4;
command ViewScene(ViewSceneRequest): ViewSceneResponse = 1;
}

server cluster SoftwareDiagnostics = 52 {
Expand Down
13 changes: 12 additions & 1 deletion src/app/AttributeAccessInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -316,13 +316,24 @@ class AttributeValueDecoder
mReader(aReader), mSubjectDescriptor(aSubjectDescriptor)
{}

template <typename T>
template <typename T, typename std::enable_if_t<!DataModel::IsFabricScoped<T>::value, bool> = true>
CHIP_ERROR Decode(T & aArg)
{
mTriedDecode = true;
return DataModel::Decode(mReader, aArg);
}

template <typename T, typename std::enable_if_t<DataModel::IsFabricScoped<T>::value, bool> = true>
CHIP_ERROR Decode(T & aArg)
{
mTriedDecode = true;
// TODO: We may want to reject kUndefinedFabricIndex for writing fabric scoped data. mAccessingFabricIndex will be
// kUndefinedFabricIndex on PASE sessions.
erjiaqing marked this conversation as resolved.
Show resolved Hide resolved
ReturnErrorOnFailure(DataModel::Decode(mReader, aArg));
aArg.SetFabricIndex(AccessingFabricIndex());
return CHIP_NO_ERROR;
}

bool TriedDecode() const { return mTriedDecode; }

/**
Expand Down
27 changes: 21 additions & 6 deletions src/app/clusters/access-control-server/access-control-server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,8 @@ struct AccessControlEntryCodec
return fabricIndex;
}

void SetFabricIndex(FabricIndex fabricIndex) { entry.SetFabricIndex(fabricIndex); }

AccessControl::Entry entry;
};

Expand Down Expand Up @@ -435,30 +437,43 @@ CHIP_ERROR AccessControlAttribute::Write(const ConcreteDataAttributePath & aPath

CHIP_ERROR AccessControlAttribute::WriteAcl(AttributeValueDecoder & aDecoder)
{
FabricIndex accessingFabricIndex = aDecoder.AccessingFabricIndex();

DataModel::DecodableList<AccessControlEntryCodec> list;
ReturnErrorOnFailure(aDecoder.Decode(list));

size_t oldCount;
size_t oldCount = 0;
size_t allCount;
size_t newCount;
size_t maxCount;
ReturnErrorOnFailure(GetAccessControl().GetEntryCount(oldCount));

AccessControl::EntryIterator it;
AccessControl::Entry entry;
ReturnErrorOnFailure(GetAccessControl().Entries(it, &accessingFabricIndex));
while (it.Next(entry) == CHIP_NO_ERROR)
{
oldCount++;
erjiaqing marked this conversation as resolved.
Show resolved Hide resolved
}

ReturnErrorOnFailure(GetAccessControl().GetEntryCount(allCount));
ReturnErrorOnFailure(list.ComputeSize(&newCount));
ReturnErrorOnFailure(GetAccessControl().GetMaxEntryCount(maxCount));
ReturnErrorCodeIf(newCount > maxCount, CHIP_ERROR_INVALID_LIST_LENGTH);
VerifyOrReturnError(allCount >= oldCount, CHIP_ERROR_INTERNAL);
VerifyOrReturnError(static_cast<size_t>(allCount - oldCount + newCount) > maxCount, CHIP_ERROR_INVALID_LIST_LENGTH);
bzbarsky-apple marked this conversation as resolved.
Show resolved Hide resolved

auto iterator = list.begin();
size_t i = 0;
while (iterator.Next())
{
if (i < oldCount)
{
ReturnErrorOnFailure(GetAccessControl().UpdateEntry(i, iterator.GetValue().entry));
ReturnErrorOnFailure(GetAccessControl().UpdateEntry(i, iterator.GetValue().entry, &accessingFabricIndex));
ReturnErrorOnFailure(LogAccessControlEvent(iterator.GetValue().entry, aDecoder.GetSubjectDescriptor(),
AccessControlCluster::ChangeTypeEnum::kChanged));
}
else
{
ReturnErrorOnFailure(GetAccessControl().CreateEntry(nullptr, iterator.GetValue().entry));
ReturnErrorOnFailure(GetAccessControl().CreateEntry(nullptr, iterator.GetValue().entry, &accessingFabricIndex));
ReturnErrorOnFailure(LogAccessControlEvent(iterator.GetValue().entry, aDecoder.GetSubjectDescriptor(),
AccessControlCluster::ChangeTypeEnum::kAdded));
}
Expand All @@ -469,7 +484,7 @@ CHIP_ERROR AccessControlAttribute::WriteAcl(AttributeValueDecoder & aDecoder)
while (i < oldCount)
{
--oldCount;
ReturnErrorOnFailure(GetAccessControl().DeleteEntry(oldCount));
ReturnErrorOnFailure(GetAccessControl().DeleteEntry(oldCount, &accessingFabricIndex));
ReturnErrorOnFailure(LogAccessControlEvent(iterator.GetValue().entry, aDecoder.GetSubjectDescriptor(),
AccessControlCluster::ChangeTypeEnum::kRemoved));
}
Expand Down
78 changes: 54 additions & 24 deletions src/app/data-model/DecodableList.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

#include <app/data-model/Decode.h>
#include <app/data-model/Encode.h>
#include <app/data-model/FabricScoped.h>
#include <lib/core/CHIPTLV.h>

namespace chip {
Expand All @@ -39,6 +40,8 @@ class DecodableList
public:
DecodableList() { ClearReader(); }

static constexpr bool kIsFabricScoped = DataModel::IsFabricScoped<T>::value;

/*
* @brief
*
Expand All @@ -56,6 +59,12 @@ class DecodableList
*/
void ClearReader() { mReader.Init(nullptr, 0); }

template <typename T0 = T, std::enable_if_t<DataModel::IsFabricScoped<T0>::value, bool> = true>
void SetFabricIndex(FabricIndex fabricIndex)
{
mFabricIndex.SetValue(fabricIndex);
}

class Iterator
{
public:
Expand All @@ -67,7 +76,7 @@ class DecodableList
* have a `kTLVType_NotSpecified` container type if there is
* no list.
*/
Iterator(const TLV::TLVReader & reader)
Iterator(const TLV::TLVReader & reader, Optional<FabricIndex> fabricIndex) : mFabricIndex(fabricIndex)
{
mStatus = CHIP_NO_ERROR;
mReader.Init(reader);
Expand All @@ -88,24 +97,23 @@ class DecodableList
* this shall return false as well. The caller is expected to invoke GetStatus()
* to retrieve the status of the operation.
*/
template <typename T0 = T, std::enable_if_t<!DataModel::IsFabricScoped<T0>::value, bool> = true>
bool Next()
{
if (mReader.GetContainerType() == TLV::kTLVType_NotSpecified)
{
return false;
}
return DoNext();
}

if (mStatus == CHIP_NO_ERROR)
{
mStatus = mReader.Next();
}
template <typename T0 = T, std::enable_if_t<DataModel::IsFabricScoped<T0>::value, bool> = true>
bool Next()
{
bool hasNext = DoNext();

if (mStatus == CHIP_NO_ERROR)
if (hasNext && mFabricIndex.HasValue())
{
mStatus = Decode(mReader, mValue);
mValue.SetFabricIndex(mFabricIndex.Value());
}

return (mStatus == CHIP_NO_ERROR);
return hasNext;
}

/*
Expand Down Expand Up @@ -133,12 +141,34 @@ class DecodableList
}

private:
bool DoNext()
{
if (mReader.GetContainerType() == TLV::kTLVType_NotSpecified)
{
return false;
}

if (mStatus == CHIP_NO_ERROR)
{
mStatus = mReader.Next();
}

if (mStatus == CHIP_NO_ERROR)
{
mStatus = DataModel::Decode(mReader, mValue);
}

return (mStatus == CHIP_NO_ERROR);
}

T mValue;
CHIP_ERROR mStatus;
TLV::TLVReader mReader;
// TODO: Consider some method to disable this field when T is not a fabric scoped struct.
erjiaqing marked this conversation as resolved.
Show resolved Hide resolved
const Optional<FabricIndex> mFabricIndex;
};

Iterator begin() const { return Iterator(mReader); }
Iterator begin() const { return Iterator(mReader, mFabricIndex); }

/*
* Compute the size of the list. This can fail if the TLV is malformed. If
Expand All @@ -160,21 +190,21 @@ class DecodableList
}
}

CHIP_ERROR Decode(TLV::TLVReader & reader)
{
VerifyOrReturnError(reader.GetType() == TLV::kTLVType_Array, CHIP_ERROR_SCHEMA_MISMATCH);
TLV::TLVType type;
ReturnErrorOnFailure(reader.EnterContainer(type));
SetReader(reader);
ReturnErrorOnFailure(reader.ExitContainer(type));
return CHIP_NO_ERROR;
}

private:
TLV::TLVReader mReader;
chip::Optional<FabricIndex> mFabricIndex;
erjiaqing marked this conversation as resolved.
Show resolved Hide resolved
};

template <typename X>
CHIP_ERROR Decode(TLV::TLVReader & reader, DecodableList<X> & x)
{
VerifyOrReturnError(reader.GetType() == TLV::kTLVType_Array, CHIP_ERROR_SCHEMA_MISMATCH);
TLV::TLVType type;
ReturnErrorOnFailure(reader.EnterContainer(type));
x.SetReader(reader);
ReturnErrorOnFailure(reader.ExitContainer(type));
return CHIP_NO_ERROR;
}

} // namespace DataModel
} // namespace app
} // namespace chip
10 changes: 8 additions & 2 deletions src/app/data-model/Decode.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,10 @@ CHIP_ERROR Decode(TLV::TLVReader & reader, const ConcreteAttributePath & path, X
*
* Decodes an optional value (struct field, command field, event field).
*/
template <typename X>
template <
typename X,
typename std::enable_if_t<
std::is_same<decltype(Decode(std::declval<TLV::TLVReader &>(), std::declval<X &>())), CHIP_ERROR>::value, X> * = nullptr>
bzbarsky-apple marked this conversation as resolved.
Show resolved Hide resolved
CHIP_ERROR Decode(TLV::TLVReader & reader, Optional<X> & x)
{
// If we are calling this, it means we found the right tag, so just decode
Expand All @@ -148,7 +151,10 @@ CHIP_ERROR Decode(TLV::TLVReader & reader, Optional<X> & x)
*
* Decodes a nullable value.
*/
template <typename X>
template <
typename X,
typename std::enable_if_t<
std::is_same<decltype(Decode(std::declval<TLV::TLVReader &>(), std::declval<X &>())), CHIP_ERROR>::value, X> * = nullptr>
bzbarsky-apple marked this conversation as resolved.
Show resolved Hide resolved
CHIP_ERROR Decode(TLV::TLVReader & reader, Nullable<X> & x)
{
if (reader.GetType() == TLV::kTLVType_Null)
Expand Down
Loading