Add time Manager to handle property changes callback

1. Implement time::Manager who registers property change signal for time
mode and owner;
2. Add PropertyChangeListner interface to handle the callback;
3. Make EpochBase to implement the interface.

Change-Id: I185580ae37353e1ed82a47e4905fb22e269ac09d
Signed-off-by: Lei YU <mine260309@gmail.com>
diff --git a/manager.cpp b/manager.cpp
new file mode 100644
index 0000000..c4732f4
--- /dev/null
+++ b/manager.cpp
@@ -0,0 +1,179 @@
+#include "manager.hpp"
+
+#include <phosphor-logging/log.hpp>
+
+namespace rules = sdbusplus::bus::match::rules;
+
+namespace // anonymous
+{
+constexpr auto SETTINGS_SERVICE = "org.openbmc.settings.Host";
+constexpr auto SETTINGS_PATH = "/org/openbmc/settings/host0";
+constexpr auto SETTINGS_INTERFACE = "org.openbmc.settings.Host";
+constexpr auto PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties";
+constexpr auto METHOD_GET = "Get";
+
+constexpr auto PROPERTY_TIME_MODE = "time_mode";
+constexpr auto PROPERTY_TIME_OWNER = "time_owner";
+
+// TODO: Use new settings in xyz.openbmc_project
+const auto MATCH_PROPERTY_CHANGE =
+    rules::type::signal() +
+    rules::member("PropertiesChanged") +
+    rules::path("/org/openbmc/settings/host0") +
+    rules::interface("org.freedesktop.DBus.Properties");
+
+}
+
+namespace phosphor
+{
+namespace time
+{
+
+using namespace phosphor::logging;
+
+const std::set<std::string>
+Manager::managedProperties = {PROPERTY_TIME_MODE, PROPERTY_TIME_OWNER};
+
+const std::map<std::string, Owner> Manager::ownerMap =
+{
+    { "BMC", Owner::BMC },
+    { "HOST", Owner::HOST },
+    { "SPLIT", Owner::SPLIT },
+    { "BOTH", Owner::BOTH },
+};
+
+Manager::Manager(sdbusplus::bus::bus& bus)
+    : bus(bus),
+      propertyChangeMatch(bus, MATCH_PROPERTY_CHANGE, onPropertyChanged, this)
+{
+    setCurrentTimeMode(getSettings(PROPERTY_TIME_MODE));
+    setCurrentTimeOwner(getSettings(PROPERTY_TIME_OWNER));
+}
+
+void Manager::addListener(PropertyChangeListner* listener)
+{
+    // Notify listener about the initial value
+    listener->onModeChanged(timeMode);
+    listener->onOwnerChanged(timeOwner);
+
+    listeners.insert(listener);
+}
+
+void Manager::onPropertyChanged(const std::string& key,
+                                const std::string& value)
+{
+    // TODO: Check pgood
+    // If it's off, notify listners;
+    // If it's on, hold the values and store in persistent storage.
+    // And when pgood turns back to off, notify the listners.
+
+    // TODO: Check dhcp_ntp
+
+    if (key == PROPERTY_TIME_MODE)
+    {
+        setCurrentTimeMode(value);
+        for (const auto& listener : listeners)
+        {
+            listener->onModeChanged(timeMode);
+        }
+    }
+    else if (key == PROPERTY_TIME_OWNER)
+    {
+        setCurrentTimeOwner(value);
+        for (const auto& listener : listeners)
+        {
+            listener->onOwnerChanged(timeOwner);
+        }
+    }
+}
+
+int Manager::onPropertyChanged(sd_bus_message* msg,
+                               void* userData,
+                               sd_bus_error* retError)
+{
+    using properties = std::map < std::string,
+          sdbusplus::message::variant<int, std::string >>;
+    auto m = sdbusplus::message::message(msg);
+    // message type: sa{sv}as
+    std::string ignore;
+    properties props;
+    m.read(ignore, props);
+    for (const auto& item : props)
+    {
+        if (managedProperties.find(item.first) != managedProperties.end())
+        {
+            static_cast<Manager*>(userData)
+                ->onPropertyChanged(item.first, item.second.get<std::string>());
+        }
+    }
+    return 0;
+}
+
+
+void Manager::setCurrentTimeMode(const std::string& mode)
+{
+    log<level::INFO>("Time mode is changed",
+                     entry("MODE=%s", mode.c_str()));
+    timeMode = convertToMode(mode);
+}
+
+void Manager::setCurrentTimeOwner(const std::string& owner)
+{
+    log<level::INFO>("Time owner is changed",
+                     entry("OWNER=%s", owner.c_str()));
+    timeOwner = convertToOwner(owner);
+}
+
+std::string Manager::getSettings(const char* value) const
+{
+    sdbusplus::message::variant<std::string> mode;
+    auto method = bus.new_method_call(SETTINGS_SERVICE,
+                                      SETTINGS_PATH,
+                                      PROPERTY_INTERFACE,
+                                      METHOD_GET);
+    method.append(SETTINGS_INTERFACE, value);
+    auto reply = bus.call(method);
+    if (reply)
+    {
+        reply.read(mode);
+    }
+    else
+    {
+        log<level::ERR>("Failed to get settings");
+    }
+
+    return mode.get<std::string>();
+}
+
+Mode Manager::convertToMode(const std::string& mode)
+{
+    if (mode == "NTP")
+    {
+        return Mode::NTP;
+    }
+    else if (mode == "MANUAL")
+    {
+        return Mode::MANUAL;
+    }
+    else
+    {
+        log<level::ERR>("Unrecognized mode",
+                        entry("%s", mode.c_str()));
+        return Mode::NTP;
+    }
+}
+
+Owner Manager::convertToOwner(const std::string& owner)
+{
+    auto it = ownerMap.find(owner);
+    if (it == ownerMap.end())
+    {
+        log<level::ERR>("Unrecognized owner",
+                        entry("%s", owner.c_str()));
+        return Owner::BMC;
+    }
+    return it->second;
+}
+
+}
+}