diff --git a/src/app/BUILD.gn b/src/app/BUILD.gn index 5fa15d41afd031..9ab803d807578d 100644 --- a/src/app/BUILD.gn +++ b/src/app/BUILD.gn @@ -65,6 +65,8 @@ static_library("app") { "MessageDef/CommandPathIB.h", "MessageDef/EventDataElement.cpp", "MessageDef/EventDataElement.h", + "MessageDef/EventFilterIB.cpp", + "MessageDef/EventFilters.cpp", "MessageDef/EventList.cpp", "MessageDef/EventList.h", "MessageDef/EventPath.cpp", diff --git a/src/app/MessageDef/EventFilterIB.cpp b/src/app/MessageDef/EventFilterIB.cpp new file mode 100644 index 00000000000000..9fbb3e063d52d6 --- /dev/null +++ b/src/app/MessageDef/EventFilterIB.cpp @@ -0,0 +1,151 @@ +/** + * + * Copyright (c) 2021 Project CHIP Authors + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file + * This file defines EventFilterIB parser and builder in CHIP interaction model + * + */ + +#include "EventFilterIB.h" + +#include "MessageDefHelper.h" + +#include +#include +#include + +#include + +namespace chip { +namespace app { +CHIP_ERROR EventFilterIB::Parser::Init(const chip::TLV::TLVReader & aReader) +{ + mReader.Init(aReader); + VerifyOrReturnError(chip::TLV::kTLVType_Structure == mReader.GetType(), CHIP_ERROR_WRONG_TLV_TYPE); + ReturnErrorOnFailure(mReader.EnterContainer(mOuterContainerType)); + return CHIP_NO_ERROR; +} + +#if CHIP_CONFIG_IM_ENABLE_SCHEMA_CHECK +CHIP_ERROR EventFilterIB::Parser::CheckSchemaValidity() const +{ + CHIP_ERROR err = CHIP_NO_ERROR; + int TagPresenceMask = 0; + TLV::TLVReader reader; + + PRETTY_PRINT("EventFilterIB ="); + PRETTY_PRINT("{"); + + // make a copy of the reader + reader.Init(mReader); + + while (CHIP_NO_ERROR == (err = reader.Next())) + { + VerifyOrReturnError(TLV::IsContextTag(reader.GetTag()), CHIP_ERROR_INVALID_TLV_TAG); + uint32_t tagNum = TLV::TagNumFromTag(reader.GetTag()); + switch (tagNum) + { + case to_underlying(Tag::kNode): + // check if this tag has appeared before + VerifyOrReturnError(!(TagPresenceMask & (1 << to_underlying(Tag::kNode))), CHIP_ERROR_INVALID_TLV_TAG); + TagPresenceMask |= (1 << to_underlying(Tag::kNode)); +#if CHIP_DETAIL_LOGGING + { + NodeId node; + ReturnLogErrorOnFailure(reader.Get(node)); + PRETTY_PRINT("\tNode = 0x%" PRIx64 ",", node); + } +#endif // CHIP_DETAIL_LOGGING + break; + case to_underlying(Tag::kEventMin): + // check if this tag has appeared before + VerifyOrReturnError(!(TagPresenceMask & (1 << to_underlying(Tag::kEventMin))), CHIP_ERROR_INVALID_TLV_TAG); + TagPresenceMask |= (1 << to_underlying(Tag::kEventMin)); +#if CHIP_DETAIL_LOGGING + { + uint64_t eventMin; + ReturnLogErrorOnFailure(reader.Get(eventMin)); + PRETTY_PRINT("\tEventMin = 0x%" PRIx64 ",", eventMin); + } +#endif // CHIP_DETAIL_LOGGING + break; + default: + PRETTY_PRINT("Unknown tag num %" PRIu32, tagNum); + break; + } + } + + PRETTY_PRINT("},"); + PRETTY_PRINT(""); + + if (CHIP_END_OF_TLV == err) + { + const int RequiredFields = (1 << to_underlying(Tag::kEventMin)); + + if ((TagPresenceMask & RequiredFields) == RequiredFields) + { + err = CHIP_NO_ERROR; + } + } + + ReturnErrorOnFailure(err); + ReturnErrorOnFailure(reader.ExitContainer(mOuterContainerType)); + return CHIP_NO_ERROR; +} +#endif // CHIP_CONFIG_IM_ENABLE_SCHEMA_CHECK + +CHIP_ERROR EventFilterIB::Parser::GetNode(NodeId * const apNode) const +{ + return GetUnsignedInteger(to_underlying(Tag::kNode), apNode); +} + +CHIP_ERROR EventFilterIB::Parser::GetEventMin(uint64_t * const apEventMin) const +{ + return GetUnsignedInteger(to_underlying(Tag::kEventMin), apEventMin); +} + +CHIP_ERROR EventFilterIB::Builder::Init(TLV::TLVWriter * const apWriter) +{ + return InitAnonymousStructure(apWriter); +} + +EventFilterIB::Builder & EventFilterIB::Builder::Node(const NodeId aNode) +{ + // skip if error has already been set + if (mError == CHIP_NO_ERROR) + { + mError = mpWriter->Put(TLV::ContextTag(to_underlying(Tag::kNode)), aNode); + } + return *this; +} + +EventFilterIB::Builder & EventFilterIB::Builder::EventMin(const uint64_t aEventMin) +{ + // skip if error has already been set + if (mError == CHIP_NO_ERROR) + { + mError = mpWriter->Put(TLV::ContextTag(to_underlying(Tag::kEventMin)), aEventMin); + } + return *this; +} + +EventFilterIB::Builder & EventFilterIB::Builder::EndOfEventFilterIB() +{ + EndOfContainer(); + return *this; +} +}; // namespace app +}; // namespace chip diff --git a/src/app/MessageDef/EventFilterIB.h b/src/app/MessageDef/EventFilterIB.h new file mode 100644 index 00000000000000..e97e3a6197eb7d --- /dev/null +++ b/src/app/MessageDef/EventFilterIB.h @@ -0,0 +1,135 @@ +/** + * + * Copyright (c) 2021 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file + * This file defines EventFilterIB parser and builder in CHIP interaction model + * + */ + +#pragma once + +#include "Builder.h" +#include "Parser.h" + +#include +#include +#include +#include +#include +#include + +namespace chip { +namespace app { +namespace EventFilterIB { +enum class Tag : uint8_t +{ + kNode = 0, + kEventMin = 1, +}; + +class Parser : public chip::app::Parser +{ +public: + /** + * @brief Initialize the parser object with TLVReader + * + * @param [in] aReader A pointer to a TLVReader, which should point to the beginning of this EventFilterIB + * + * @return #CHIP_NO_ERROR on success + */ + CHIP_ERROR Init(const chip::TLV::TLVReader & aReader); + +#if CHIP_CONFIG_IM_ENABLE_SCHEMA_CHECK + /** + * @brief Roughly verify the message is correctly formed + * 1) all mandatory tags are present + * 2) all elements have expected data type + * 3) any tag can only appear once + * 4) At the top level of the structure, unknown tags are ignored for forward compatibility + * @note The main use of this function is to print out what we're + * receiving during protocol development and debugging. + * The encoding rule has changed in IM encoding spec so this + * check is only "roughly" conformant now. + * + * @return #CHIP_NO_ERROR on success + */ + CHIP_ERROR CheckSchemaValidity() const; +#endif + + /** + * @brief Get a TLVReader for the NodeId. Next() must be called before accessing them. + * + * @param [in] apNodeId A pointer to apNodeId + * + * @return #CHIP_NO_ERROR on success + * #CHIP_ERROR_WRONG_TLV_TYPE if there is such element but it's not any of the defined unsigned integer types + * #CHIP_END_OF_TLV if there is no such element + */ + CHIP_ERROR GetNode(NodeId * const apNodeId) const; + + /** + * @brief Get a TLVReader for the ClusterId. Next() must be called before accessing them. + * + * @param [in] apEventMin A pointer to EventMin + * + * @return #CHIP_NO_ERROR on success + * #CHIP_ERROR_WRONG_TLV_TYPE if there is such element but it's not any of the defined unsigned integer types + * #CHIP_END_OF_TLV if there is no such element + */ + CHIP_ERROR GetEventMin(uint64_t * const apEventMin) const; +}; + +class Builder : public chip::app::Builder +{ +public: + /** + * @brief Initialize a EventFilterIB::Builder for writing into a TLV stream + * + * @param [in] apWriter A pointer to TLVWriter + * + * @return #CHIP_NO_ERROR on success + */ + CHIP_ERROR Init(chip::TLV::TLVWriter * const apWriter); + + /** + * @brief Inject Node into the TLV stream to indicate the nodeId referenced by the event filter. + * + * @param [in] aNodeId refer to the NodeId + * + * @return A reference to *this + */ + EventFilterIB::Builder & Node(const NodeId aNode); + + /** + * @brief Inject EventId into the TLV stream. + * + * @param [in] aEventMin EventMin for this event filter + * + * @return A reference to *this + */ + EventFilterIB::Builder & EventMin(const uint64_t aEventMin); + + /** + * @brief Mark the end of this EventFilterIB + * + * @return A reference to *this + */ + EventFilterIB::Builder & EndOfEventFilterIB(); +}; +}; // namespace EventFilterIB +}; // namespace app +}; // namespace chip diff --git a/src/app/MessageDef/EventFilters.cpp b/src/app/MessageDef/EventFilters.cpp new file mode 100644 index 00000000000000..ce72f0bdd45368 --- /dev/null +++ b/src/app/MessageDef/EventFilters.cpp @@ -0,0 +1,91 @@ +/** + * + * Copyright (c) 2021 Project CHIP Authors + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file + * This file defines EventFilters parser and builder in CHIP interaction model + * + */ + +#include "EventFilters.h" + +#include "MessageDefHelper.h" + +#include +#include +#include + +#include + +namespace chip { +namespace app { +#if CHIP_CONFIG_IM_ENABLE_SCHEMA_CHECK +CHIP_ERROR EventFilters::Parser::CheckSchemaValidity() const +{ + CHIP_ERROR err = CHIP_NO_ERROR; + size_t numEventFilters = 0; + TLV::TLVReader reader; + + PRETTY_PRINT("EventFilters ="); + PRETTY_PRINT("["); + + // make a copy of the reader + reader.Init(mReader); + + while (CHIP_NO_ERROR == (err = reader.Next())) + { + VerifyOrReturnError(TLV::AnonymousTag == reader.GetTag(), CHIP_ERROR_INVALID_TLV_TAG); + { + EventFilterIB::Parser eventFilter; + ReturnErrorOnFailure(eventFilter.Init(reader)); + PRETTY_PRINT_INCDEPTH(); + ReturnErrorOnFailure(eventFilter.CheckSchemaValidity()); + PRETTY_PRINT_DECDEPTH(); + } + + ++numEventFilters; + } + + PRETTY_PRINT("],"); + PRETTY_PRINT(""); + + // if we have exhausted this container + if (CHIP_END_OF_TLV == err) + { + // if we have at least one event filter + if (numEventFilters > 0) + { + err = CHIP_NO_ERROR; + } + } + ReturnErrorOnFailure(err); + ReturnErrorOnFailure(reader.ExitContainer(mOuterContainerType)); + return CHIP_NO_ERROR; +} +#endif // CHIP_CONFIG_IM_ENABLE_SCHEMA_CHECK + +EventFilterIB::Builder & EventFilters::Builder::CreateEventFilter() +{ + mError = mEventFilter.Init(mpWriter); + return mEventFilter; +} + +EventFilters::Builder & EventFilters::Builder::EndOfEventFilters() +{ + EndOfContainer(); + return *this; +} +}; // namespace app +}; // namespace chip diff --git a/src/app/MessageDef/EventFilters.h b/src/app/MessageDef/EventFilters.h new file mode 100644 index 00000000000000..baa340dc76d355 --- /dev/null +++ b/src/app/MessageDef/EventFilters.h @@ -0,0 +1,87 @@ +/** + * + * Copyright (c) 2021 Project CHIP Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/** + * @file + * This file defines EventFilters parser and builder in CHIP interaction model + * + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "EventFilterIB.h" +#include "ListBuilder.h" +#include "ListParser.h" + +namespace chip { +namespace app { +namespace EventFilters { +class Parser : public ListParser +{ +public: +#if CHIP_CONFIG_IM_ENABLE_SCHEMA_CHECK + /** + * @brief Roughly verify the message is correctly formed + * 1) all mandatory tags are present + * 2) all elements have expected data type + * 3) any tag can only appear once + * 4) At the top level of the structure, unknown tags are ignored for forward compatibility + * @note The main use of this function is to print out what we're + * receiving during protocol development and debugging. + * The encoding rule has changed in IM encoding spec so this + * check is only "roughly" conformant now. + * + * @return #CHIP_NO_ERROR on success + */ + CHIP_ERROR CheckSchemaValidity() const; +#endif +}; + +class Builder : public ListBuilder +{ +public: + /** + * @brief Initialize a EventFilterIB::Builder for writing into the TLV stream + * + * @return A reference to EventFilterIB::Builder + */ + EventFilterIB::Builder & CreateEventFilter(); + + /** + * @return A reference to EventFilterIB::Builder + */ + EventFilterIB::Builder & GetEventFilter() { return mEventFilter; }; + + /** + * @brief Mark the end of this EventFilters + * + * @return A reference to *this + */ + EventFilters::Builder & EndOfEventFilters(); + +private: + EventFilterIB::Builder mEventFilter; +}; +}; // namespace EventFilters +}; // namespace app +}; // namespace chip diff --git a/src/app/tests/TestMessageDef.cpp b/src/app/tests/TestMessageDef.cpp index 891b6e29578d24..75371b16119ab2 100644 --- a/src/app/tests/TestMessageDef.cpp +++ b/src/app/tests/TestMessageDef.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -72,6 +73,56 @@ CHIP_ERROR DebugPrettyPrint(const chip::System::PacketBufferHandle & aMsgBuf) return err; } +void BuildEventFilterIB(nlTestSuite * apSuite, EventFilterIB::Builder & aEventFilterIBBuilder) +{ + aEventFilterIBBuilder.Node(1).EventMin(2).EndOfEventFilterIB(); + NL_TEST_ASSERT(apSuite, aEventFilterIBBuilder.GetError() == CHIP_NO_ERROR); +} + +void ParseEventFilterIB(nlTestSuite * apSuite, chip::TLV::TLVReader & aReader) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + EventFilterIB::Parser eventFilterIBParser; + chip::NodeId node = 1; + uint64_t eventMin = 2; + + err = eventFilterIBParser.Init(aReader); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); +#if CHIP_CONFIG_IM_ENABLE_SCHEMA_CHECK + err = eventFilterIBParser.CheckSchemaValidity(); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); +#endif + err = eventFilterIBParser.GetNode(&node); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR && node == 1); + + err = eventFilterIBParser.GetEventMin(&eventMin); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR && eventMin == 2); +} + +void BuildEventFilters(nlTestSuite * apSuite, EventFilters::Builder & aEventFiltersBuilder) +{ + EventFilterIB::Builder eventFilterBuilder = aEventFiltersBuilder.CreateEventFilter(); + NL_TEST_ASSERT(apSuite, aEventFiltersBuilder.GetError() == CHIP_NO_ERROR); + BuildEventFilterIB(apSuite, eventFilterBuilder); + aEventFiltersBuilder.EndOfEventFilters(); + NL_TEST_ASSERT(apSuite, aEventFiltersBuilder.GetError() == CHIP_NO_ERROR); +} + +void ParseEventFilters(nlTestSuite * apSuite, chip::TLV::TLVReader & aReader) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + EventFilters::Parser eventFiltersParser; + AttributePath::Parser attributePathParser; + + err = eventFiltersParser.Init(aReader); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + +#if CHIP_CONFIG_IM_ENABLE_SCHEMA_CHECK + err = eventFiltersParser.CheckSchemaValidity(); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); +#endif +} + void BuildAttributePath(nlTestSuite * apSuite, AttributePath::Builder & aAttributePathBuilder) { CHIP_ERROR err = CHIP_NO_ERROR; @@ -1039,6 +1090,53 @@ void ParseTimedRequest(nlTestSuite * apSuite, chip::TLV::TLVReader & aReader) NL_TEST_ASSERT(apSuite, timeout == 1 && err == CHIP_NO_ERROR); } +void EventFilterTest(nlTestSuite * apSuite, void * apContext) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + EventFilterIB::Builder eventFilterBuilder; + chip::System::PacketBufferTLVWriter writer; + chip::System::PacketBufferTLVReader reader; + writer.Init(chip::System::PacketBufferHandle::New(chip::System::PacketBuffer::kMaxSize)); + eventFilterBuilder.Init(&writer); + BuildEventFilterIB(apSuite, eventFilterBuilder); + chip::System::PacketBufferHandle buf; + err = writer.Finalize(&buf); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + + DebugPrettyPrint(buf); + + reader.Init(std::move(buf)); + err = reader.Next(); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + + ParseEventFilterIB(apSuite, reader); +} + +void EventFiltersTest(nlTestSuite * apSuite, void * apContext) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + chip::System::PacketBufferTLVWriter writer; + chip::System::PacketBufferTLVReader reader; + EventFilters::Builder eventFiltersBuilder; + + writer.Init(chip::System::PacketBufferHandle::New(chip::System::PacketBuffer::kMaxSize)); + + err = eventFiltersBuilder.Init(&writer); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + + BuildEventFilters(apSuite, eventFiltersBuilder); + chip::System::PacketBufferHandle buf; + err = writer.Finalize(&buf); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + + DebugPrettyPrint(buf); + + reader.Init(std::move(buf)); + err = reader.Next(); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + ParseEventFilters(apSuite, reader); +} + void AttributePathTest(nlTestSuite * apSuite, void * apContext) { CHIP_ERROR err = CHIP_NO_ERROR; @@ -1622,6 +1720,8 @@ void CheckPointRollbackTest(nlTestSuite * apSuite, void * apContext) // clang-format off const nlTest sTests[] = { + NL_TEST_DEF("EventFilterTest", EventFilterTest), + NL_TEST_DEF("EventFiltersTest", EventFiltersTest), NL_TEST_DEF("AttributePathTest", AttributePathTest), NL_TEST_DEF("AttributePathListTest", AttributePathListTest), NL_TEST_DEF("EventPathTest", EventPathTest),