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/Makefile.am b/Makefile.am
index 0af260f..066dd33 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -7,7 +7,8 @@
 libtimemanager_la_SOURCES = \
 	epoch_base.cpp \
 	bmc_epoch.cpp \
-	host_epoch.cpp
+	host_epoch.cpp \
+	manager.cpp
 
 phosphor_timemanager_SOURCES = \
 	main.cpp
diff --git a/epoch_base.cpp b/epoch_base.cpp
index 06232bb..07b3113 100644
--- a/epoch_base.cpp
+++ b/epoch_base.cpp
@@ -5,15 +5,6 @@
 #include <iomanip>
 #include <sstream>
 
-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";
-}
-
 namespace phosphor
 {
 namespace time
@@ -21,90 +12,21 @@
 
 using namespace phosphor::logging;
 
-const std::map<std::string, EpochBase::Owner>
-EpochBase::ownerMap = {
-    { "BMC", EpochBase::Owner::BMC },
-    { "HOST", EpochBase::Owner::HOST },
-    { "SPLIT", EpochBase::Owner::SPLIT },
-    { "BOTH", EpochBase::Owner::BOTH },
-};
-
 EpochBase::EpochBase(sdbusplus::bus::bus& bus,
                      const char* objPath)
-    : sdbusplus::server::object::object<EpochTime>(bus, objPath, true),
+    : sdbusplus::server::object::object<EpochTime>(bus, objPath),
       bus(bus)
 {
-    initialize();
-    // Deferred this until we could get our property correct
-    emit_object_added();
 }
 
-void EpochBase::setCurrentTimeMode(const std::string& mode)
+void EpochBase::onModeChanged(Mode mode)
 {
-    log<level::INFO>("Time mode is changed",
-                     entry("MODE=%s", mode.c_str()));
-    timeMode = convertToMode(mode);
+    timeMode = mode;
 }
 
-void EpochBase::setCurrentTimeOwner(const std::string& owner)
+void EpochBase::onOwnerChanged(Owner owner)
 {
-    log<level::INFO>("Time owner is changed",
-                     entry("OWNER=%s", owner.c_str()));
-    timeOwner = convertToOwner(owner);
-}
-
-void EpochBase::initialize()
-{
-    setCurrentTimeMode(getSettings("time_mode"));
-    setCurrentTimeOwner(getSettings("time_owner"));
-    // TODO: subscribe settingsd's property changes callback
-}
-
-std::string EpochBase::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);
-    }
-
-    return mode.get<std::string>();
-}
-
-EpochBase::Mode EpochBase::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;
-    }
-}
-
-EpochBase::Owner EpochBase::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;
+    timeOwner = owner;
 }
 
 using namespace std::chrono;
diff --git a/epoch_base.hpp b/epoch_base.hpp
index fc505d0..f31a32b 100644
--- a/epoch_base.hpp
+++ b/epoch_base.hpp
@@ -1,5 +1,7 @@
 #pragma once
 
+#include "property_change_listener.hpp"
+
 #include <sdbusplus/bus.hpp>
 #include <xyz/openbmc_project/Time/EpochTime/server.hpp>
 
@@ -16,64 +18,30 @@
  *  DBus API for epoch time.
  */
 class EpochBase : public sdbusplus::server::object::object <
-    sdbusplus::xyz::openbmc_project::Time::server::EpochTime >
+    sdbusplus::xyz::openbmc_project::Time::server::EpochTime >,
+    public PropertyChangeListner
 {
     public:
         friend class TestEpochBase;
 
-        /** @brief Supported time modes
-         *  NTP     Time sourced by Network Time Server
-         *  MANUAL  User of the system need to set the time
-         */
-        enum class Mode
-        {
-            NTP,
-            MANUAL,
-        };
-
-        /** @brief Supported time owners
-         *  BMC     Time source may be NTP or MANUAL but it has to be set natively
-         *          on the BMC. Meaning, host can not set the time. What it also
-         *          means is that when BMC gets IPMI_SET_SEL_TIME, then its ignored.
-         *          similarly, when BMC gets IPMI_GET_SEL_TIME, then the BMC's time
-         *          is returned.
-         *
-         *  HOST    Its only IPMI_SEL_SEL_TIME that will set the time on BMC.
-         *          Meaning, IPMI_GET_SEL_TIME and request to get BMC time will
-         *          result in same value.
-         *
-         *  SPLIT   Both BMC and HOST will maintain their individual clocks but then
-         *          the time information is stored in BMC. BMC can have either NTP
-         *          or MANUAL as it's source of time and will set the time directly
-         *          on the BMC. When IPMI_SET_SEL_TIME is received, then the delta
-         *          between that and BMC's time is calculated and is stored.
-         *          When BMC reads the time, the current time is returned.
-         *          When IPMI_GET_SEL_TIME is received, BMC's time is retrieved and
-         *          then the delta offset is factored in prior to returning.
-         *
-         *  BOTH:   BMC's time is set with whoever that sets the time. Similarly,
-         *          BMC's time is returned to whoever that asks the time.
-         */
-        enum class Owner
-        {
-            BMC,
-            HOST,
-            SPLIT,
-            BOTH,
-        };
-
         EpochBase(sdbusplus::bus::bus& bus,
                   const char* objPath);
 
+        /** @brief Notified on time mode changed */
+        void onModeChanged(Mode mode) override;
+
+        /** @brief Notified on time owner changed */
+        void onOwnerChanged(Owner owner) override;
+
     protected:
         /** @brief Persistent sdbusplus DBus connection */
         sdbusplus::bus::bus& bus;
 
         /** @brief The current time mode */
-        Mode timeMode;
+        Mode timeMode = Mode::NTP;
 
         /** @brief The current time owner */
-        Owner timeOwner;
+        Owner timeOwner = Owner::BMC;
 
         /** @brief Set current time to system
          *
@@ -89,57 +57,6 @@
          * @return Microseconds since UTC
          */
         std::chrono::microseconds getTime() const;
-
-        /** @brief Convert a string to enum Mode
-         *
-         * Convert the time mode string to enum.
-         * Valid strings are "NTP", "MANUAL"
-         * If it's not a valid time mode string, return NTP.
-         *
-         * @param[in] mode - The string of time mode
-         *
-         * @return The Mode enum
-         */
-        static Mode convertToMode(const std::string& mode);
-
-        /** @brief Convert a string to enum Owner
-         *
-         * Convert the time owner string to enum.
-         * Valid strings are "BMC", "HOST", "SPLIT", "BOTH"
-         * If it's not a valid time owner string, return BMC.
-         *
-         * @param[in] owner - The string of time owner
-         *
-         * @return The Owner enum
-         */
-        static Owner convertToOwner(const std::string& owner);
-
-    private:
-        /** @brief Initialize the time mode and owner */
-        void initialize();
-
-        /** @brief Set current time mode
-         *
-         * @param[in] mode - The string of time mode
-         */
-        void setCurrentTimeMode(const std::string& mode);
-
-        /** @brief Set current time owner
-         *
-         * @param[in] owner - The string of time owner
-         */
-        void setCurrentTimeOwner(const std::string& owner);
-
-        /** @brief Get setting value from settings manager
-         *
-         * @param[in] setting - The string of the setting to get
-         *
-         * @return The value of the setting
-         */
-        std::string getSettings(const char* setting) const;
-
-        /** @brief The map maps the string key to enum Owner */
-        static const std::map<std::string, Owner> ownerMap;
 };
 
 } // namespace time
diff --git a/main.cpp b/main.cpp
index b3f4b57..b8a5adb 100644
--- a/main.cpp
+++ b/main.cpp
@@ -3,13 +3,23 @@
 #include "config.h"
 #include "bmc_epoch.hpp"
 #include "host_epoch.hpp"
+#include "manager.hpp"
 
 int main()
 {
     auto bus = sdbusplus::bus::new_default();
+
+    // Add sdbusplus ObjectManager
+    sdbusplus::server::manager::manager bmcEpochObjManager(bus, OBJPATH_BMC);
+    sdbusplus::server::manager::manager hostEpochObjManager(bus, OBJPATH_HOST);
+
+    phosphor::time::Manager manager(bus);
     phosphor::time::BmcEpoch bmc(bus, OBJPATH_BMC);
     phosphor::time::HostEpoch host(bus,OBJPATH_HOST);
 
+    manager.addListener(&bmc);
+    manager.addListener(&host);
+
     bus.request_name(BUSNAME);
 
     while (true)
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;
+}
+
+}
+}
diff --git a/manager.hpp b/manager.hpp
new file mode 100644
index 0000000..65240ac
--- /dev/null
+++ b/manager.hpp
@@ -0,0 +1,135 @@
+#pragma once
+
+#include "types.hpp"
+#include "property_change_listener.hpp"
+
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/bus/match.hpp>
+
+#include <set>
+#include <string>
+
+namespace phosphor
+{
+namespace time
+{
+
+/** @class Manager
+ *  @brief The manager to handle OpenBMC time.
+ *  @details It registers various time related settings and properties signals
+ *  on DBus and handle the changes.
+ *  For certain properties it also notifies the changed events to listeners.
+ */
+class Manager
+{
+    public:
+        friend class TestManager;
+        explicit Manager(sdbusplus::bus::bus& bus);
+
+        /** @brief Add a listener that will be called
+          * when property is changed
+         **/
+        void addListener(PropertyChangeListner* listener);
+
+    private:
+        /** @brief Persistent sdbusplus DBus connection */
+        sdbusplus::bus::bus& bus;
+
+        /** @brief The match of settings property change */
+        sdbusplus::bus::match::match propertyChangeMatch;
+
+        /** @brief The container to hold all the listeners */
+        std::set<PropertyChangeListner*> listeners;
+
+        /** @brief The current time mode */
+        Mode timeMode;
+
+        /** @brief The current time owner */
+        Owner timeOwner;
+
+        /** @brief Get setting from settingsd service
+         *
+         * @param[in] setting - The string of the setting
+         *
+         * @return The setting value in string
+         */
+        std::string getSettings(const char* setting) const;
+
+        /** @brief Set current time mode
+         *
+         * @param[in] mode - The string of time mode
+         */
+        void setCurrentTimeMode(const std::string& mode);
+
+        /** @brief Set current time owner
+         *
+         * @param[in] owner - The string of time owner
+         */
+        void setCurrentTimeOwner(const std::string& owner);
+
+        /** @brief Notified on settings property changed
+         *
+         * @param[in] key - The name of property that is changed
+         * @param[in] value - The value of the property
+         */
+        void onPropertyChanged(const std::string& key,
+                               const std::string& value);
+
+        /** @brief The static function called on settings property changed
+         *
+         * @param[in] msg - Data associated with subscribed signal
+         * @param[in] userData - Pointer to this object instance
+         * @param[out] retError  - Not used but required with signal API
+         */
+        static int onPropertyChanged(sd_bus_message* msg,
+                                     void* userData,
+                                     sd_bus_error* retError);
+
+        /** @brief Convert a string to enum Mode
+         *
+         * Convert the time mode string to enum.
+         * Valid strings are "NTP", "MANUAL"
+         * If it's not a valid time mode string, return NTP.
+         *
+         * @param[in] mode - The string of time mode
+         *
+         * @return The Mode enum
+         */
+        static Mode convertToMode(const std::string& mode);
+
+        /** @brief Convert a string to enum Owner
+         *
+         * Convert the time owner string to enum.
+         * Valid strings are "BMC", "HOST", "SPLIT", "BOTH"
+         * If it's not a valid time owner string, return BMC.
+         *
+         * @param[in] owner - The string of time owner
+         *
+         * @return The Owner enum
+         */
+        static Owner convertToOwner(const std::string& owner);
+
+        using Updater = std::function<void(const std::string&)>;
+
+        /** @brief Map the property string to functions that shall
+         *  be called when the property is changed
+         */
+        const std::map<std::string, Updater> propertyUpdaters =
+        {
+            {"time_mode", std::bind(&Manager::setCurrentTimeMode,
+                                    this, std::placeholders::_1)},
+            {"time_owner", std::bind(&Manager::setCurrentTimeOwner,
+                                     this, std::placeholders::_1)}
+        };
+
+        /** @brief The properties that manager shall notify the
+         *  listeners when changed
+         */
+        static const std::set<std::string> managedProperties;
+
+        /** @brief The map that maps the string to Owners */
+        static const std::map<std::string, Owner> ownerMap;
+};
+
+}
+}
diff --git a/property_change_listener.hpp b/property_change_listener.hpp
new file mode 100644
index 0000000..de23f47
--- /dev/null
+++ b/property_change_listener.hpp
@@ -0,0 +1,23 @@
+#pragma once
+
+#include "types.hpp"
+
+namespace phosphor
+{
+namespace time
+{
+
+class PropertyChangeListner
+{
+    public:
+        virtual ~PropertyChangeListner() {}
+
+        /** @brief Notified on time mode is changed */
+        virtual void onModeChanged(Mode mode) = 0;
+
+        /** @brief Notified on time owner is changed */
+        virtual void onOwnerChanged(Owner owner) = 0;
+};
+
+}
+}
diff --git a/test/Makefile.am b/test/Makefile.am
index bdd258e..b8ea41c 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -8,7 +8,8 @@
 test_SOURCES = \
     TestEpochBase.cpp \
     TestBmcEpoch.cpp \
-    TestHostEpoch.cpp
+    TestHostEpoch.cpp \
+	TestManager.cpp
 
 test_LDADD = $(top_builddir)/libtimemanager.la
 
diff --git a/test/TestBmcEpoch.cpp b/test/TestBmcEpoch.cpp
index da65b3e..2fd55a5 100644
--- a/test/TestBmcEpoch.cpp
+++ b/test/TestBmcEpoch.cpp
@@ -3,6 +3,7 @@
 
 #include "bmc_epoch.hpp"
 #include "config.h"
+#include "types.hpp"
 
 namespace phosphor
 {
@@ -13,9 +14,6 @@
 class TestBmcEpoch : public testing::Test
 {
     public:
-        using Mode = EpochBase::Mode;
-        using Owner = EpochBase::Owner;
-
         sdbusplus::bus::bus bus;
         BmcEpoch bmcEpoch;
 
diff --git a/test/TestEpochBase.cpp b/test/TestEpochBase.cpp
index c3ca7e1..e4a2b68 100644
--- a/test/TestEpochBase.cpp
+++ b/test/TestEpochBase.cpp
@@ -1,6 +1,7 @@
 #include <sdbusplus/bus.hpp>
 #include <gtest/gtest.h>
 
+#include "types.hpp"
 #include "epoch_base.hpp"
 
 namespace phosphor
@@ -11,9 +12,6 @@
 class TestEpochBase : public testing::Test
 {
     public:
-        using Mode = EpochBase::Mode;
-        using Owner = EpochBase::Owner;
-
         sdbusplus::bus::bus bus;
         EpochBase epochBase;
 
@@ -24,40 +22,38 @@
             // Empty
         }
 
-        // Proxies for EpochBase's private members and functions
-        Mode convertToMode(const std::string& mode)
+        Mode getMode()
         {
-            return EpochBase::convertToMode(mode);
+            return epochBase.timeMode;
         }
-        Owner convertToOwner(const std::string& owner)
+        Owner getOwner()
         {
-            return EpochBase::convertToOwner(owner);
+            return epochBase.timeOwner;
         }
 };
 
-TEST_F(TestEpochBase, convertToMode)
+TEST_F(TestEpochBase, onModeChange)
 {
-    EXPECT_EQ(Mode::NTP, convertToMode("NTP"));
-    EXPECT_EQ(Mode::MANUAL, convertToMode("MANUAL"));
+    epochBase.onModeChanged(Mode::NTP);
+    EXPECT_EQ(Mode::NTP, getMode());
 
-    // All unrecognized strings are mapped to Ntp
-    EXPECT_EQ(Mode::NTP, convertToMode(""));
-    EXPECT_EQ(Mode::NTP, convertToMode("Manual"));
-    EXPECT_EQ(Mode::NTP, convertToMode("whatever"));
+    epochBase.onModeChanged(Mode::MANUAL);
+    EXPECT_EQ(Mode::MANUAL, getMode());
 }
 
-
-TEST_F(TestEpochBase, convertToOwner)
+TEST_F(TestEpochBase, onOwnerChange)
 {
-    EXPECT_EQ(Owner::BMC, convertToOwner("BMC"));
-    EXPECT_EQ(Owner::HOST, convertToOwner("HOST"));
-    EXPECT_EQ(Owner::SPLIT, convertToOwner("SPLIT"));
-    EXPECT_EQ(Owner::BOTH, convertToOwner("BOTH"));
+    epochBase.onOwnerChanged(Owner::BMC);
+    EXPECT_EQ(Owner::BMC, getOwner());
 
-    // All unrecognized strings are mapped to Bmc
-    EXPECT_EQ(Owner::BMC, convertToOwner(""));
-    EXPECT_EQ(Owner::BMC, convertToOwner("Split"));
-    EXPECT_EQ(Owner::BMC, convertToOwner("xyz"));
+    epochBase.onOwnerChanged(Owner::HOST);
+    EXPECT_EQ(Owner::HOST, getOwner());
+
+    epochBase.onOwnerChanged(Owner::SPLIT);
+    EXPECT_EQ(Owner::SPLIT, getOwner());
+
+    epochBase.onOwnerChanged(Owner::BOTH);
+    EXPECT_EQ(Owner::BOTH, getOwner());
 }
 
 }
diff --git a/test/TestHostEpoch.cpp b/test/TestHostEpoch.cpp
index dca8d72..ec8ecf1 100644
--- a/test/TestHostEpoch.cpp
+++ b/test/TestHostEpoch.cpp
@@ -3,6 +3,7 @@
 
 #include "host_epoch.hpp"
 #include "config.h"
+#include "types.hpp"
 
 namespace phosphor
 {
@@ -15,9 +16,6 @@
 class TestHostEpoch : public testing::Test
 {
     public:
-        using Mode = EpochBase::Mode;
-        using Owner = EpochBase::Owner;
-
         sdbusplus::bus::bus bus;
         HostEpoch hostEpoch;
 
diff --git a/test/TestManager.cpp b/test/TestManager.cpp
new file mode 100644
index 0000000..4d9ae73
--- /dev/null
+++ b/test/TestManager.cpp
@@ -0,0 +1,76 @@
+#include <sdbusplus/bus.hpp>
+#include <gtest/gtest.h>
+
+#include "types.hpp"
+#include "manager.hpp"
+
+namespace phosphor
+{
+namespace time
+{
+
+class TestManager : public testing::Test
+{
+    public:
+        sdbusplus::bus::bus bus;
+        Manager manager;
+
+        TestManager()
+            : bus(sdbusplus::bus::new_default()),
+              manager(bus)
+        {
+            // Empty
+        }
+
+        // Proxies for Manager's private members and functions
+         Mode getTimeMode()
+        {
+            return manager.timeMode;
+        }
+        Owner getTimeOwner()
+        {
+            return manager.timeOwner;
+        }
+        Mode convertToMode(const std::string& mode)
+        {
+            return Manager::convertToMode(mode);
+        }
+        Owner convertToOwner(const std::string& owner)
+        {
+            return Manager::convertToOwner(owner);
+        }
+};
+
+TEST_F(TestManager, empty)
+{
+    EXPECT_EQ(Mode::NTP, getTimeMode());
+    EXPECT_EQ(Owner::BMC, getTimeOwner());
+}
+
+TEST_F(TestManager, convertToMode)
+{
+    EXPECT_EQ(Mode::NTP, convertToMode("NTP"));
+    EXPECT_EQ(Mode::MANUAL, convertToMode("MANUAL"));
+
+    // All unrecognized strings are mapped to Ntp
+    EXPECT_EQ(Mode::NTP, convertToMode(""));
+    EXPECT_EQ(Mode::NTP, convertToMode("Manual"));
+    EXPECT_EQ(Mode::NTP, convertToMode("whatever"));
+}
+
+
+TEST_F(TestManager, convertToOwner)
+{
+    EXPECT_EQ(Owner::BMC, convertToOwner("BMC"));
+    EXPECT_EQ(Owner::HOST, convertToOwner("HOST"));
+    EXPECT_EQ(Owner::SPLIT, convertToOwner("SPLIT"));
+    EXPECT_EQ(Owner::BOTH, convertToOwner("BOTH"));
+
+    // All unrecognized strings are mapped to Bmc
+    EXPECT_EQ(Owner::BMC, convertToOwner(""));
+    EXPECT_EQ(Owner::BMC, convertToOwner("Split"));
+    EXPECT_EQ(Owner::BMC, convertToOwner("xyz"));
+}
+
+}
+}
diff --git a/types.hpp b/types.hpp
new file mode 100644
index 0000000..9b88965
--- /dev/null
+++ b/types.hpp
@@ -0,0 +1,49 @@
+#pragma once
+
+namespace phosphor
+{
+namespace time
+{
+        /** @brief Supported time modes
+         *  NTP     Time sourced by Network Time Server
+         *  MANUAL  User of the system need to set the time
+         */
+        enum class Mode
+        {
+            NTP,
+            MANUAL,
+        };
+
+        /** @brief Supported time owners
+         *  BMC     Time source may be NTP or MANUAL but it has to be set natively
+         *          on the BMC. Meaning, host can not set the time. What it also
+         *          means is that when BMC gets IPMI_SET_SEL_TIME, then its ignored.
+         *          similarly, when BMC gets IPMI_GET_SEL_TIME, then the BMC's time
+         *          is returned.
+         *
+         *  HOST    Its only IPMI_SEL_SEL_TIME that will set the time on BMC.
+         *          Meaning, IPMI_GET_SEL_TIME and request to get BMC time will
+         *          result in same value.
+         *
+         *  SPLIT   Both BMC and HOST will maintain their individual clocks but then
+         *          the time information is stored in BMC. BMC can have either NTP
+         *          or MANUAL as it's source of time and will set the time directly
+         *          on the BMC. When IPMI_SET_SEL_TIME is received, then the delta
+         *          between that and BMC's time is calculated and is stored.
+         *          When BMC reads the time, the current time is returned.
+         *          When IPMI_GET_SEL_TIME is received, BMC's time is retrieved and
+         *          then the delta offset is factored in prior to returning.
+         *
+         *  BOTH:   BMC's time is set with whoever that sets the time. Similarly,
+         *          BMC's time is returned to whoever that asks the time.
+         */
+        enum class Owner
+        {
+            BMC,
+            HOST,
+            SPLIT,
+            BOTH,
+        };
+}
+}
+