Implement Association interface

Implement xyz/openbmc_project/Association/Definitions.interface in
activation and item_updater.

Tested: Verify the association is created after uploading a dummy
        tarball.

Signed-off-by: Lei YU <mine260309@gmail.com>
Change-Id: I217bddc48534a0b3b17359e0f3409f6c7c953f08
diff --git a/src/activation.hpp b/src/activation.hpp
index 6ba2b75..45aa521 100644
--- a/src/activation.hpp
+++ b/src/activation.hpp
@@ -2,7 +2,10 @@
 
 #include "config.h"
 
+#include "types.hpp"
+
 #include <sdbusplus/server.hpp>
+#include <xyz/openbmc_project/Association/Definitions/server.hpp>
 #include <xyz/openbmc_project/Software/Activation/server.hpp>
 #include <xyz/openbmc_project/Software/ExtendedVersion/server.hpp>
 
@@ -15,7 +18,8 @@
 
 using ActivationInherit = sdbusplus::server::object::object<
     sdbusplus::xyz::openbmc_project::Software::server::ExtendedVersion,
-    sdbusplus::xyz::openbmc_project::Software::server::Activation>;
+    sdbusplus::xyz::openbmc_project::Software::server::Activation,
+    sdbusplus::xyz::openbmc_project::Association::server::Definitions>;
 
 /** @class Activation
  *  @brief OpenBMC activation software management implementation.
@@ -32,17 +36,20 @@
      * @param[in] versionId  - The software version id
      * @param[in] extVersion - The extended version
      * @param[in] activationStatus - The status of Activation
+     * @param[in] assocs - Association objects
      */
     Activation(sdbusplus::bus::bus& bus, const std::string& path,
                const std::string& versionId, const std::string& extVersion,
                sdbusplus::xyz::openbmc_project::Software::server::Activation::
-                   Activations activationStatus) :
+                   Activations activationStatus,
+               const AssociationList& assocs) :
         ActivationInherit(bus, path.c_str(), true),
         bus(bus), path(path), versionId(versionId)
     {
         // Set Properties.
         extendedVersion(extVersion);
         activation(activationStatus);
+        associations(assocs);
 
         // Emit deferred signal.
         emit_object_added();
diff --git a/src/item_updater.cpp b/src/item_updater.cpp
index 9a08f2e..cfbadb0 100644
--- a/src/item_updater.cpp
+++ b/src/item_updater.cpp
@@ -87,8 +87,13 @@
     if (activations.find(versionId) == activations.end())
     {
         // Determine the Activation state by processing the given image dir.
+        AssociationList associations;
         auto activationState = server::Activation::Activations::Ready;
 
+        associations.emplace_back(std::make_tuple(ACTIVATION_FWD_ASSOCIATION,
+                                                  ACTIVATION_REV_ASSOCIATION,
+                                                  PSU_INVENTORY_PATH));
+
         fs::path manifestPath(filePath);
         manifestPath /= MANIFEST_FILE;
         std::string extendedVersion =
@@ -99,7 +104,7 @@
                 ->second;
 
         auto activation = createActivationObject(
-            path, versionId, extendedVersion, activationState);
+            path, versionId, extendedVersion, activationState, associations);
         activations.emplace(versionId, std::move(activation));
 
         auto versionPtr =
@@ -145,14 +150,58 @@
     // that are not the running firmware.
 }
 
+void ItemUpdater::createActiveAssociation(const std::string& path)
+{
+    assocs.emplace_back(
+        std::make_tuple(ACTIVE_FWD_ASSOCIATION, ACTIVE_REV_ASSOCIATION, path));
+    associations(assocs);
+}
+
+void ItemUpdater::updateFunctionalAssociation(const std::string& versionId)
+{
+    std::string path = std::string{SOFTWARE_OBJPATH} + '/' + versionId;
+    // remove all functional associations
+    for (auto iter = assocs.begin(); iter != assocs.end();)
+    {
+        if ((std::get<0>(*iter)).compare(FUNCTIONAL_FWD_ASSOCIATION) == 0)
+        {
+            iter = assocs.erase(iter);
+        }
+        else
+        {
+            ++iter;
+        }
+    }
+    assocs.emplace_back(std::make_tuple(FUNCTIONAL_FWD_ASSOCIATION,
+                                        FUNCTIONAL_REV_ASSOCIATION, path));
+    associations(assocs);
+}
+
+void ItemUpdater::removeAssociation(const std::string& path)
+{
+    for (auto iter = assocs.begin(); iter != assocs.end();)
+    {
+        if ((std::get<2>(*iter)).compare(path) == 0)
+        {
+            iter = assocs.erase(iter);
+            associations(assocs);
+        }
+        else
+        {
+            ++iter;
+        }
+    }
+}
+
 std::unique_ptr<Activation> ItemUpdater::createActivationObject(
     const std::string& path, const std::string& versionId,
     const std::string& extVersion,
     sdbusplus::xyz::openbmc_project::Software::server::Activation::Activations
-        activationStatus)
+        activationStatus,
+    const AssociationList& assocs)
 {
     return std::make_unique<Activation>(bus, path, versionId, extVersion,
-                                        activationStatus);
+                                        activationStatus, assocs);
 }
 
 std::unique_ptr<Version> ItemUpdater::createVersionObject(
diff --git a/src/item_updater.hpp b/src/item_updater.hpp
index bc86af1..96006f4 100644
--- a/src/item_updater.hpp
+++ b/src/item_updater.hpp
@@ -3,9 +3,11 @@
 #include "config.h"
 
 #include "activation.hpp"
+#include "types.hpp"
 #include "version.hpp"
 
 #include <sdbusplus/server.hpp>
+#include <xyz/openbmc_project/Association/Definitions/server.hpp>
 #include <xyz/openbmc_project/Collection/DeleteAll/server.hpp>
 
 namespace phosphor
@@ -18,6 +20,7 @@
 class Version;
 
 using ItemUpdaterInherit = sdbusplus::server::object::object<
+    sdbusplus::xyz::openbmc_project::Association::server::Definitions,
     sdbusplus::xyz::openbmc_project::Collection::server::DeleteAll>;
 namespace MatchRules = sdbusplus::bus::match::rules;
 
@@ -54,6 +57,26 @@
     void deleteAll();
 
   private:
+    /** @brief Creates an active association to the
+     *  newly active software image
+     *
+     * @param[in]  path - The path to create the association to.
+     */
+    void createActiveAssociation(const std::string& path);
+
+    /** @brief Updates the functional association to the
+     *  new "running" PSU images
+     *
+     * @param[in]  versionId - The id of the image to update the association to.
+     */
+    void updateFunctionalAssociation(const std::string& versionId);
+
+    /** @brief Removes the associations from the provided software image path
+     *
+     * @param[in]  path - The path to remove the association from.
+     */
+    void removeAssociation(const std::string& path);
+
     /** @brief Callback function for Software.Version match.
      *  @details Creates an Activation D-Bus object.
      *
@@ -66,7 +89,8 @@
         const std::string& path, const std::string& versionId,
         const std::string& extVersion,
         sdbusplus::xyz::openbmc_project::Software::server::Activation::
-            Activations activationStatus);
+            Activations activationStatus,
+        const AssociationList& assocs);
 
     /** @brief Create Version object */
     std::unique_ptr<Version>
@@ -90,6 +114,9 @@
 
     /** @brief sdbusplus signal match for Software.Version */
     sdbusplus::bus::match_t versionMatch;
+
+    /** @brief This entry's associations */
+    AssociationList assocs;
 };
 
 } // namespace updater
diff --git a/src/meson.build b/src/meson.build
index 4a13228..c3c04e5 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -10,6 +10,13 @@
 cdata.set_quoted('FILEPATH_IFACE', 'xyz.openbmc_project.Common.FilePath')
 cdata.set_quoted('BUSNAME_UPDATER', 'xyz.openbmc_project.Software.Psu.Updater')
 cdata.set_quoted('MANIFEST_FILE', 'MANIFEST')
+cdata.set_quoted('ACTIVATION_FWD_ASSOCIATION', 'inventory')
+cdata.set_quoted('ACTIVATION_REV_ASSOCIATION', 'activation')
+cdata.set_quoted('PSU_INVENTORY_PATH', '/xyz/openbmc_project/inventory/system/chassis')
+cdata.set_quoted('ACTIVE_FWD_ASSOCIATION', 'active')
+cdata.set_quoted('ACTIVE_REV_ASSOCIATION', 'software_version')
+cdata.set_quoted('FUNCTIONAL_FWD_ASSOCIATION', 'functional')
+cdata.set_quoted('FUNCTIONAL_REV_ASSOCIATION', 'software_version')
 
 configure_file(output: 'config.h',
   configuration: cdata,
diff --git a/src/types.hpp b/src/types.hpp
new file mode 100644
index 0000000..9ba4383
--- /dev/null
+++ b/src/types.hpp
@@ -0,0 +1,18 @@
+#pragma once
+
+#include <string>
+#include <tuple>
+#include <vector>
+
+namespace phosphor
+{
+namespace software
+{
+namespace updater
+{
+
+using AssociationList =
+    std::vector<std::tuple<std::string, std::string, std::string>>;
+}
+} // namespace software
+} // namespace phosphor