Add changed callback for vconf plugin
authorJinWang An <jinwang.an@samsung.com>
Thu, 5 Sep 2019 01:11:16 +0000 (10:11 +0900)
committerYoungjae Shin <yj99.shin@samsung.com>
Thu, 19 Mar 2020 04:30:19 +0000 (13:30 +0900)
unittests/mdsp_test_vconf.cpp
vconf/VconfPlugin.cpp

index 398852d..a7af18a 100644 (file)
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#include <glib.h>
+#include <vconf.h>
 #include <gtest/gtest.h>
 #include <Plugin.h>
 #include <modes_errors.h>
+#include "plugin-log.h"
 #include "plugin-def.h"
 
 MODES_NAMESPACE_USE;
@@ -23,56 +26,99 @@ MODES_NAMESPACE_USE;
 extern "C" Plugin *objectCreate(void);
 extern "C" void objectDelete(Plugin *plugin);
 
-TEST(PluginTest, setPluginVconfInt)
+class VconfPluginTest : public ::testing::Test {
+protected:
+       void SetUp() override
+       {
+               plugin = objectCreate();
+               loop = g_main_loop_new(NULL, FALSE);
+       }
+
+       void TearDown() override
+       {
+               objectDelete(plugin);
+               g_main_loop_unref(loop);
+               loop = NULL;
+               result = -1;
+       }
+
+       static void valChangedCb(const std::string &key, void *userData)
+       {
+               DBG("%s changed callback called!", key.c_str());
+               g_main_loop_quit(loop);
+       }
+
+       static void valChangedResetStoredCb(const std::string &key, void *userData)
+       {
+               DBG("%s changed callback called!", key.c_str());
+               result = MODES_ERROR_NONE;
+       }
+
+       static void valChangedResetCb(const std::string &key, void *userData)
+       {
+               DBG("%s changed callback called!", key.c_str());
+               g_main_loop_quit(loop);
+       }
+
+       static gboolean changedCallbackidler(gpointer data)
+       {
+               DBG("start change the value db/setting/mode");
+               vconf_set_int("db/setting/psmode", 7);
+               return G_SOURCE_REMOVE;
+       }
+
+       static int result;
+       static GMainLoop *loop;
+       static Plugin *plugin;
+       static int oldval;
+};
+
+int VconfPluginTest::oldval = 0;
+int VconfPluginTest::result = -1;
+Plugin *VconfPluginTest::plugin = nullptr;
+GMainLoop *VconfPluginTest::loop = NULL;
+
+
+TEST_F(VconfPluginTest, setPluginVconfInt)
 {
        int ret;
        int oldval;
-       Plugin *plugin = objectCreate();
 
        ret = plugin->set("db.setting.psmode", 3, nullptr);
        EXPECT_EQ(ret, MODES_ERROR_NONE);
        ret = plugin->set("db.setting.psmode", 1, &oldval);
        EXPECT_EQ(ret, MODES_ERROR_NONE);
        EXPECT_EQ(oldval, 3);
-
-       objectDelete(plugin);
 }
 
-TEST(PluginTest, setPluginVconfDouble)
+TEST_F(VconfPluginTest, setPluginVconfDouble)
 {
        int ret;
        double oldval;
-       Plugin *plugin = objectCreate();
 
        ret = plugin->set("db.system.timechange_external", 1.0, nullptr);
        EXPECT_EQ(ret, MODES_ERROR_NONE);
        ret = plugin->set("db.system.timechange_external", 0.0, &oldval);
        EXPECT_EQ(ret, MODES_ERROR_NONE);
        EXPECT_EQ(oldval, 1.0);
-
-       objectDelete(plugin);
 }
 
-TEST(PluginTest, setPluginVconfBool)
+TEST_F(VconfPluginTest, setPluginVconfBool)
 {
        int ret;
        bool oldval;
-       Plugin *plugin = objectCreate();
 
        ret = plugin->set("db.setting.sound.button_sounds", false, nullptr);
        EXPECT_EQ(ret, MODES_ERROR_NONE);
        ret = plugin->set("db.setting.sound.button_sounds", true, &oldval);
        EXPECT_EQ(ret, MODES_ERROR_NONE);
        EXPECT_EQ(oldval, false);
-
-       objectDelete(plugin);
 }
 
-TEST(PluginTest, setPluginVconfStr)
+TEST_F(VconfPluginTest, setPluginVconfStr)
 {
        int ret;
        std::string oldval;
-       Plugin *plugin = objectCreate();
 
        std::string testVal = "org.tizen.menu-screen.test";
        ret = plugin->set("db.setting.menuscreen.package_name", testVal, nullptr);
@@ -80,8 +126,42 @@ TEST(PluginTest, setPluginVconfStr)
        ret = plugin->set("db.setting.menuscreen.package_name", "org.tizen.menu-screen", &oldval);
        EXPECT_EQ(ret, MODES_ERROR_NONE);
        EXPECT_EQ(oldval.compare(testVal), 0);
+}
+
+TEST_F(VconfPluginTest, callbackPluginVconf)
+{
+       vconf_get_int("db/setting/psmode", &oldval);
+       int ret = plugin->setChangedCallback(valChangedCb,
+                       "db.setting.psmode", nullptr);
+       EXPECT_EQ(ret, MODES_ERROR_NONE);
+       g_idle_add(changedCallbackidler, nullptr);
+       g_main_loop_run(loop);
+
+       ret = plugin->unSetChangedCallback(valChangedCb, "db.setting.psmode", nullptr);
+       EXPECT_EQ(ret, MODES_ERROR_NO_DATA);
+       vconf_set_int("db/setting/psmode", oldval);
+}
+
+TEST_F(VconfPluginTest, callbackPluginVconfReset)
+{
+       int ret;
+       ret = plugin->set("db.setting.psmode", 4, &oldval);
+       EXPECT_EQ(ret, MODES_ERROR_NONE);
+       ret = plugin->setChangedCallback(valChangedResetStoredCb, "db.setting.psmode", nullptr);
+       EXPECT_EQ(ret, MODES_ERROR_NONE);
+       ret = plugin->set("db.setting.psmode", 2, nullptr);
+       EXPECT_EQ(ret, MODES_ERROR_NONE);
+       EXPECT_EQ(MODES_ERROR_NONE, result);
+       ret = plugin->setChangedCallback(valChangedResetCb, "db.setting.psmode", nullptr);
+       EXPECT_EQ(ret, MODES_ERROR_NONE);
+       g_idle_add(changedCallbackidler, nullptr);
+       g_main_loop_run(loop);
 
-       objectDelete(plugin);
+       DBG("loop end and unregister callback start");
+       ret = plugin->unSetChangedCallback(valChangedResetCb, "db.setting.psmode", nullptr);
+       EXPECT_EQ(ret, MODES_ERROR_NO_DATA);
+       vconf_set_int("db/setting/psmode", oldval);
+       EXPECT_EQ(MODES_ERROR_NONE, result);
 }
 
 int main(int argc, char **argv) {
index d362ce5..76d67c7 100644 (file)
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#include <vconf.h>
-#include <modes_errors.h>
+#include <map>
 #include <string>
 #include <algorithm>
+#include <vconf.h>
+#include <modes_errors.h>
 #include <Plugin.h>
 #include "plugin-log.h"
 #include "plugin-def.h"
@@ -25,8 +26,6 @@
 MODES_NAMESPACE_USE;
 
 class VconfPlugin : public Plugin {
-private:
-
 public:
        VconfPlugin();
 
@@ -39,6 +38,19 @@ public:
        double getDouble(const std::string &key) override;
        bool getBool(const std::string &key) override;
        std::string getString(const std::string &key) override;
+
+       int setChangedCallback(valueChangedCb callback, const std::string &key, void *userData);
+       int unSetChangedCallback(valueChangedCb callback, const std::string &key, void *userData);
+private:
+       struct CallbackData {
+               void *userData;
+               valueChangedCb *callback;
+       };
+
+       int handleChange(const std::string &key);
+       static void vconfChangedCallback(keynode_t *node, void *userData);
+
+       static std::map<std::string, CallbackData> callbackMap;
 };
 
 extern "C" API Plugin *objectCreate(void)
@@ -51,6 +63,8 @@ extern "C" API void objectDelete(Plugin *plugin)
        delete plugin;
 }
 
+std::map<std::string, VconfPlugin::CallbackData> VconfPlugin::callbackMap;
+
 VconfPlugin::VconfPlugin()
 {
        setName("vconf");
@@ -72,6 +86,12 @@ int VconfPlugin::set(const std::string &key, int val, int *oldVal)
                }
        }
 
+       ret = handleChange(newKey);
+       if (MODES_ERROR_NONE != ret) {
+               ERR("handleChange(%s) Fail(%d)", newKey.c_str(), ret);
+               return ret;
+       }
+
        ret = vconf_set_int(newKey.c_str(), val);
        if (0 != ret) {
                ERR("vconf_set_int(%s, %d) Fail(%d)", newKey.c_str(), val, ret);
@@ -96,6 +116,12 @@ int VconfPlugin::set(const std::string &key, double val, double *oldVal)
                }
        }
 
+       ret = handleChange(newKey);
+       if (MODES_ERROR_NONE != ret) {
+               ERR("handleChange(%s) Fail(%d)", newKey.c_str(), ret);
+               return ret;
+       }
+
        ret = vconf_set_dbl(newKey.c_str(), val);
        if (0 != ret) {
                ERR("vconf_set_int(%s, %lf) Fail(%d)", newKey.c_str(), val, ret);
@@ -122,6 +148,12 @@ int VconfPlugin::set(const std::string &key, bool val, bool *oldVal)
                *oldVal = prev;
        }
 
+       ret = handleChange(newKey);
+       if (MODES_ERROR_NONE != ret) {
+               ERR("handleChange(%s) Fail(%d)", newKey.c_str(), ret);
+               return ret;
+       }
+
        ret = vconf_set_bool(newKey.c_str(), val);
        if (0 != ret) {
                ERR("vconf_set_bool(%s, %d) Fail(%d)", newKey.c_str(), val, ret);
@@ -147,8 +179,13 @@ int VconfPlugin::set(const std::string &key, const std::string &val, std::string
                free(prevStr);
        }
 
+       int ret = handleChange(newKey);
+       if (MODES_ERROR_NONE != ret) {
+               ERR("handleChange(%s) Fail(%d)", newKey.c_str(), ret);
+               return ret;
+       }
 
-       int ret = vconf_set_str(newKey.c_str(), val.c_str());
+       ret = vconf_set_str(newKey.c_str(), val.c_str());
        if (0 != ret) {
                ERR("vconf_set_str(%s, %s) Fail(%d)", newKey.c_str(), val.c_str(), ret);
                return MODES_ERROR_SYSTEM;
@@ -223,3 +260,81 @@ std::string VconfPlugin::getString(const std::string &key)
 
        return retStr;
 }
+
+int VconfPlugin::setChangedCallback(valueChangedCb callback, const std::string &key, void *userData)
+{
+       std::string newKey(key);
+       std::replace(newKey.begin(), newKey.end(), '.', '/');
+
+       callbackMap[newKey].userData = userData;
+       callbackMap[newKey].callback = callback;
+
+       int ret = vconf_notify_key_changed(newKey.c_str(), VconfPlugin::vconfChangedCallback, &callbackMap[newKey]);
+       if (VCONF_OK != ret) {
+               ERR("vconf_notify_key_changed(%s) Fail(%s)", newKey.c_str(), get_error_message(ret));
+               return MODES_ERROR_SYSTEM;
+       }
+
+       DBG("setChangedCallback(%s) Success", newKey.c_str());
+       return MODES_ERROR_NONE;
+}
+
+int VconfPlugin::unSetChangedCallback(valueChangedCb callback, const std::string &key, void *userData)
+{
+       std::string newKey(key);
+       std::replace(newKey.begin(), newKey.end(), '.', '/');
+
+       auto found = callbackMap.find(newKey);
+       if (found == callbackMap.end()) {
+               ERR("No Changed Callback(%s)", newKey.c_str());
+               return MODES_ERROR_NO_DATA;
+       }
+
+       callbackMap.erase(found);
+       int ret = vconf_ignore_key_changed(newKey.c_str(), VconfPlugin::vconfChangedCallback);
+       if (VCONF_OK != ret) {
+               ERR("vconf_ignore_key_changed(%s) Fail(%s)", newKey.c_str(), get_error_message(ret));
+               return MODES_ERROR_SYSTEM;
+       }
+
+       DBG("unSetChangedCallback(%s) Success", newKey.c_str());
+       return MODES_ERROR_NONE;
+}
+
+int VconfPlugin::handleChange(const std::string &key)
+{
+       auto found = callbackMap.find(key);
+       if (callbackMap.end() != found) {
+               DBG("Acition(%s) already exist", key.c_str());
+               found->second.callback(key, found->second.userData);
+
+               callbackMap.erase(found);
+               int ret = vconf_ignore_key_changed(key.c_str(), VconfPlugin::vconfChangedCallback);
+               if (VCONF_OK != ret) {
+                       ERR("vconf_ignore_key_changed(%s) Fail(%s)", key.c_str(), get_error_message(ret));
+                       return MODES_ERROR_SYSTEM;
+               }
+       }
+       return MODES_ERROR_NONE;
+}
+
+void VconfPlugin::vconfChangedCallback(keynode_t *node, void *userData)
+{
+       CallbackData *cbData = (CallbackData*)userData;
+
+       RET_IF(NULL == userData);
+
+       auto found = callbackMap.find(node->keyname);
+       if (&(found->second) != cbData) {
+               ERR("Unknown callbackData(%s)", node->keyname);
+               return;
+       }
+
+       cbData->callback(node->keyname, cbData->userData);
+       DBG("Action(%s) is Changed", node->keyname);
+
+       callbackMap.erase(found);
+       int ret = vconf_ignore_key_changed(node->keyname, VconfPlugin::vconfChangedCallback);
+       if (VCONF_OK != ret)
+               ERR("vconf_ignore_key_changed(%s) Fail(%s)", node->keyname, get_error_message(ret));
+}