Listener: Register correlated properties callback

This commit implements API to register callback for all interfaces in
correlated_properties.json.

```
Test:
1. Patch debug traces in the callback function.
2. On rainier 2s2u simics, do a busctl set-property on Decorator.Asset
   PartNumber, com.ibm.ipzvpd.VINI PN of LCD Op Panel.
3. Observe vpd-manager logs and observe traces indicating callback is
   getting triggered for above property changes.
```

Change-Id: I1ca2ae65724d8aa27424775fb1290a4139518644
Signed-off-by: Souvik Roy <souvikroyofficial10@gmail.com>
diff --git a/vpd-manager/include/listener.hpp b/vpd-manager/include/listener.hpp
index 9a6d627..734bf21 100644
--- a/vpd-manager/include/listener.hpp
+++ b/vpd-manager/include/listener.hpp
@@ -65,14 +65,15 @@
      * JSON.
      */
     void registerCorrPropCallBack(
-        [[maybe_unused]] const std::string& i_correlatedPropJsonFile =
+        const std::string& i_correlatedPropJsonFile =
             constants::correlatedPropJsonFile) noexcept;
 
     /**
      * @brief API to register properties changed callback.
      *
      * This API registers a properties changed callback for a specific interface
-     * under a service.
+     * under a service by constructing a match object. This API also saves the
+     * constructed match object into map object map data member.
      *
      * @param[in] i_service - Service name.
      * @param[in] i_interface - Interface name.
@@ -81,10 +82,8 @@
      * @throw FirmwareException
      */
     void registerPropChangeCallBack(
-        [[maybe_unused]] const std::string& i_service,
-        [[maybe_unused]] const std::string& i_interface,
-        [[maybe_unused]] std::function<void(sdbusplus::message_t& i_msg)>
-            i_callBackFunction);
+        const std::string& i_service, const std::string& i_interface,
+        std::function<void(sdbusplus::message_t& i_msg)> i_callBackFunction);
 
   private:
     /**
@@ -127,5 +126,8 @@
 
     // Parsed correlated properties JSON.
     nlohmann::json m_correlatedPropJson{};
+
+    // A map of {service name,{interface name,match object}}
+    types::MatchObjectMap m_matchObjectMap;
 };
 } // namespace vpd
diff --git a/vpd-manager/include/types.hpp b/vpd-manager/include/types.hpp
index 31d0085..fa44662 100644
--- a/vpd-manager/include/types.hpp
+++ b/vpd-manager/include/types.hpp
@@ -206,5 +206,9 @@
 using InvalidRecordList = std::vector<InvalidRecordEntry>;
 /* Map of inventory path -> Present property match object */
 using FruPresenceMatchObjectMap = std::map<Path, std::shared_ptr<sdbusplus::bus::match::match>>;
+/* A map of interface to match object*/
+using MatchObjectInterfaceMap = std::map<std::string,std::shared_ptr<sdbusplus::bus::match::match>>;
+/* A map of service name to match object interface map*/
+using MatchObjectMap = std::map<std::string,MatchObjectInterfaceMap>;
 } // namespace types
 } // namespace vpd
diff --git a/vpd-manager/src/listener.cpp b/vpd-manager/src/listener.cpp
index bc9ef94..4084e0a 100644
--- a/vpd-manager/src/listener.cpp
+++ b/vpd-manager/src/listener.cpp
@@ -248,7 +248,7 @@
 }
 
 void Listener::registerCorrPropCallBack(
-    [[maybe_unused]] const std::string& i_correlatedPropJsonFile) noexcept
+    const std::string& i_correlatedPropJsonFile) noexcept
 {
     try
     {
@@ -259,9 +259,32 @@
             throw JsonException("Failed to parse correlated properties JSON",
                                 i_correlatedPropJsonFile);
         }
-        /* TODO:
-        Parse correlated_properties JSON, and register callback for all
-        interfaces under all services */
+
+        const nlohmann::json& l_serviceJsonObjectList =
+            m_correlatedPropJson.get_ref<const nlohmann::json::object_t&>();
+
+        // Iterate through all services in the correlated properties json
+        for (const auto& l_serviceJsonObject : l_serviceJsonObjectList.items())
+        {
+            const auto& l_serviceName = l_serviceJsonObject.key();
+
+            const nlohmann::json& l_correlatedIntfJsonObj =
+                m_correlatedPropJson[l_serviceName]
+                    .get_ref<const nlohmann::json::object_t&>();
+
+            // register properties changed D-Bus signal callback
+            // for all interfaces under this service.
+            std::for_each(l_correlatedIntfJsonObj.items().begin(),
+                          l_correlatedIntfJsonObj.items().end(),
+                          [this, &l_serviceName = std::as_const(l_serviceName)](
+                              const auto& i_interfaceJsonObj) {
+                              registerPropChangeCallBack(
+                                  l_serviceName, i_interfaceJsonObj.key(),
+                                  [this](sdbusplus::message_t& i_msg) {
+                                      correlatedPropChangedCallBack(i_msg);
+                                  });
+                          });
+        } // service loop
     }
     catch (const std::exception& l_ex)
     {
@@ -273,17 +296,27 @@
 }
 
 void Listener::registerPropChangeCallBack(
-    [[maybe_unused]] const std::string& i_service,
-    [[maybe_unused]] const std::string& i_interface,
-    [[maybe_unused]] std::function<void(sdbusplus::message_t& i_msg)>
-        i_callBackFunction)
+    const std::string& i_service, const std::string& i_interface,
+    std::function<void(sdbusplus::message_t& i_msg)> i_callBackFunction)
 {
     try
     {
-        /*TODO:
-        Create match object based on service name, interface and callback
-        function.
-        */
+        if (i_service.empty() || i_interface.empty())
+        {
+            throw std::runtime_error("Invalid service name or interface name");
+        }
+
+        std::shared_ptr<sdbusplus::bus::match::match> l_matchObj =
+            std::make_unique<sdbusplus::bus::match::match>(
+                static_cast<sdbusplus::bus_t&>(*m_asioConnection),
+                "type='signal',member='PropertiesChanged',"
+                "interface='org.freedesktop.DBus.Properties',"
+                "arg0='" +
+                    i_interface + "'",
+                i_callBackFunction);
+
+        // save the match object in map
+        m_matchObjectMap[i_service][i_interface] = l_matchObj;
     }
     catch (const std::exception& l_ex)
     {
@@ -301,9 +334,16 @@
             throw DbusException("Error in reading property change signal.");
         }
 
+        const std::string l_interface{i_msg.get_interface()};
+        const std::string l_objectPath{i_msg.get_path()};
+        const std::string l_signature{i_msg.get_signature()};
+
+        (void)l_interface;
+        (void)l_objectPath;
+        (void)l_signature;
+
         /*TODO:
-        1. Extract interface, object path and property name from the message
-        2. Use correlated JSON to find target {object path, interface,
+        Use correlated JSON to find target {object path, interface,
         property/properties} to update*/
     }
     catch (const std::exception& l_ex)