Add EventFilter encoding (#11178)

diff --git a/src/app/BUILD.gn b/src/app/BUILD.gn
index 27a1809..cc55642 100644
--- a/src/app/BUILD.gn
+++ b/src/app/BUILD.gn
@@ -65,6 +65,8 @@
     "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 0000000..9fbb3e0
--- /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 <inttypes.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include <app/AppBuildConfig.h>
+
+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 0000000..e97e3a6
--- /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 <app/AppBuildConfig.h>
+#include <app/util/basic-types.h>
+#include <lib/core/CHIPCore.h>
+#include <lib/core/CHIPTLV.h>
+#include <lib/support/CodeUtils.h>
+#include <lib/support/logging/CHIPLogging.h>
+
+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 0000000..ce72f0b
--- /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 <inttypes.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include <app/AppBuildConfig.h>
+
+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 0000000..baa340d
--- /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 <app/AppBuildConfig.h>
+#include <app/util/basic-types.h>
+#include <lib/core/CHIPCore.h>
+#include <lib/core/CHIPTLV.h>
+#include <lib/support/CodeUtils.h>
+#include <lib/support/logging/CHIPLogging.h>
+
+#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 7b147e8..61ff09e 100644
--- a/src/app/tests/TestMessageDef.cpp
+++ b/src/app/tests/TestMessageDef.cpp
@@ -25,6 +25,7 @@
 #include <app/AppBuildConfig.h>
 #include <app/MessageDef/CommandDataIB.h>
 #include <app/MessageDef/CommandList.h>
+#include <app/MessageDef/EventFilters.h>
 #include <app/MessageDef/InvokeCommand.h>
 #include <app/MessageDef/ReadRequest.h>
 #include <app/MessageDef/ReportData.h>
@@ -72,6 +73,56 @@
     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 @@
     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 @@
 // 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),