Save properties to persistent storage when host is on

1. When host is on, set properties as requested properties instead
of notify listeners;
2. When host becomes off, and requested properties are not empty, notify
the listners and reset the requested properties.

Add unit tests.

Change-Id: I9359c801c698df0c6e5eab43e12427bb5a6da611
Signed-off-by: Lei YU <mine260309@gmail.com>
diff --git a/manager.cpp b/manager.cpp
index 5a3d928..9810e14 100644
--- a/manager.cpp
+++ b/manager.cpp
@@ -1,4 +1,5 @@
 #include "manager.hpp"
+#include "utils.hpp"
 
 #include <phosphor-logging/log.hpp>
 
@@ -12,9 +13,6 @@
 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() +
@@ -58,9 +56,14 @@
       propertyChangeMatch(bus, MATCH_PROPERTY_CHANGE, onPropertyChanged, this),
       pgoodChangeMatch(bus, MATCH_PGOOD_CHANGE, onPgoodChanged, this)
 {
-    setCurrentTimeMode(getSettings(PROPERTY_TIME_MODE));
-    setCurrentTimeOwner(getSettings(PROPERTY_TIME_OWNER));
     checkHostOn();
+
+    // Restore settings from persistent storage
+    restoreSettings();
+
+    // Check the settings daemon to process the new settings
+    onPropertyChanged(PROPERTY_TIME_MODE, getSettings(PROPERTY_TIME_MODE));
+    onPropertyChanged(PROPERTY_TIME_OWNER, getSettings(PROPERTY_TIME_OWNER));
 }
 
 void Manager::addListener(PropertyChangeListner* listener)
@@ -72,6 +75,20 @@
     listeners.insert(listener);
 }
 
+void Manager::restoreSettings()
+{
+    auto mode = utils::readData<std::string>(modeFile);
+    if (!mode.empty())
+    {
+        timeMode = convertToMode(mode);
+    }
+    auto owner = utils::readData<std::string>(ownerFile);
+    if (!owner.empty())
+    {
+        timeOwner = convertToOwner(owner);
+    }
+}
+
 void Manager::checkHostOn()
 {
     sdbusplus::message::variant<int> pgood = 0;
@@ -92,28 +109,32 @@
 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
-    // as requested time mode/owner.
-    // And when pgood turns back to off, notify the listners.
-
     // TODO: Check dhcp_ntp
 
-    if (key == PROPERTY_TIME_MODE)
+    if (hostOn)
     {
-        setCurrentTimeMode(value);
-        for (const auto& listener : listeners)
-        {
-            listener->onModeChanged(timeMode);
-        }
+        // If host is on, set the values as requested time mode/owner.
+        // And when host becomes off, notify the listners.
+        setPropertyAsRequested(key, value);
     }
-    else if (key == PROPERTY_TIME_OWNER)
+    else
     {
-        setCurrentTimeOwner(value);
-        for (const auto& listener : listeners)
+        // If host is off, notify listners
+        if (key == PROPERTY_TIME_MODE)
         {
-            listener->onOwnerChanged(timeOwner);
+            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);
+            }
         }
     }
 }
@@ -133,18 +154,67 @@
     {
         if (managedProperties.find(item.first) != managedProperties.end())
         {
-            static_cast<Manager*>(userData)
-                ->onPropertyChanged(item.first, item.second.get<std::string>());
+            static_cast<Manager*>(userData)->onPropertyChanged(
+                item.first, item.second.get<std::string>());
         }
     }
     return 0;
 }
 
+void Manager::setPropertyAsRequested(const std::string& key,
+                                     const std::string& value)
+{
+    if (key == PROPERTY_TIME_MODE)
+    {
+        setRequestedMode(value);
+    }
+    else if (key == PROPERTY_TIME_OWNER)
+    {
+        setRequestedOwner(value);
+    }
+    else
+    {
+        // The key shall be already the supported one
+        // TODO: use elog API
+        assert(false);
+    }
+}
+
+void Manager::setRequestedMode(const std::string& mode)
+{
+    requestedMode = mode;
+}
+
+void Manager::setRequestedOwner(const std::string& owner)
+{
+    requestedOwner = owner;
+}
+
 void Manager::onPgoodChanged(bool pgood)
 {
     hostOn = pgood;
-    // TODO: if host is off, check requested time_mode/owner:
-    // and notify the listeners if any.
+    if (hostOn)
+    {
+        return;
+    }
+    if (!requestedMode.empty())
+    {
+        setCurrentTimeMode(requestedMode);
+        for (const auto& listener : listeners)
+        {
+            listener->onModeChanged(timeMode);
+        }
+        setRequestedMode({}); // Clear requested mode
+    }
+    if (!requestedOwner.empty())
+    {
+        setCurrentTimeOwner(requestedOwner);
+        for (const auto& listener : listeners)
+        {
+            listener->onOwnerChanged(timeOwner);
+        }
+        setRequestedOwner({}); // Clear requested owner
+    }
 }
 
 int Manager::onPgoodChanged(sd_bus_message* msg,
@@ -174,6 +244,7 @@
     log<level::INFO>("Time mode is changed",
                      entry("MODE=%s", mode.c_str()));
     timeMode = convertToMode(mode);
+    utils::writeData(modeFile, mode);
 }
 
 void Manager::setCurrentTimeOwner(const std::string& owner)
@@ -181,6 +252,7 @@
     log<level::INFO>("Time owner is changed",
                      entry("OWNER=%s", owner.c_str()));
     timeOwner = convertToOwner(owner);
+    utils::writeData(ownerFile, owner);
 }
 
 std::string Manager::getSettings(const char* value) const