Add implementation of Vconf wrapper created for Universal Switch 10/202110/3
authorOskar Chodowicz <o.chodowicz@samsung.com>
Fri, 22 Mar 2019 17:00:47 +0000 (18:00 +0100)
committerOskar Chodowicz <o.chodowicz@samsung.com>
Mon, 25 Mar 2019 11:58:29 +0000 (12:58 +0100)
Change-Id: Id13305972b9def428d491857277daf8f784d9032

src/Singleton.hpp [new file with mode: 0644]
src/VConf.cpp [new file with mode: 0644]
src/VConf.hpp [new file with mode: 0644]

diff --git a/src/Singleton.hpp b/src/Singleton.hpp
new file mode 100644 (file)
index 0000000..0c5b73e
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2017  Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SINGLETON_HPP
+#define SINGLETON_HPP
+
+#include <type_traits>
+#include <memory>
+
+/*
+ * Classes below provides convenient way to create singletons without writing boilerplate code.
+ * The usage is as follows:
+ *     Define function createImplementation if class is derived from interface
+ *     Singleton<MyClass>::instance.myMethod();
+ * Class provided as template parameter must contain public empty constructor
+ */
+
+template <class T>
+class Singleton
+{
+public:
+       static T &instance()
+       {
+               static std::unique_ptr<T> instance{ createImplementation() };
+               return *instance;
+       }
+
+private:
+       Singleton() = default;
+       Singleton(const Singleton &) = delete;
+       Singleton(Singleton &&) = delete;
+       Singleton &operator = (const Singleton &) = delete;
+       Singleton &operator = (Singleton &&) = delete;
+
+       template<class Q = T>
+       static typename std::enable_if<std::is_default_constructible<Q>::value, std::unique_ptr<Q>>::type createImplementation()
+       {
+               return std::unique_ptr<Q> { new Q };
+       }
+
+       template<class Q = T>
+       static typename std::enable_if < !std::is_default_constructible<Q>::value, std::unique_ptr<Q >>::type createImplementation();
+};
+
+#endif
diff --git a/src/VConf.cpp b/src/VConf.cpp
new file mode 100644 (file)
index 0000000..7206c35
--- /dev/null
@@ -0,0 +1,273 @@
+/*
+ * Copyright 2017  Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "VConf.hpp"
+
+#include <vconf.h>
+#include <unordered_map>
+#include <tuple>
+#include <map>
+#include <limits>
+
+namespace
+{
+       template <class ValueType>
+       using CallbackMap = std::map<int, std::function<void(ValueType)>>;
+       template <class ValueType>
+       using KeyMap = std::unordered_map<std::string, CallbackMap<ValueType>>;
+       static std::string inCallbackKeyGuard;
+
+       static int idxCurrent = std::numeric_limits<int>::max();
+       static KeyMap<int> intCallbackMap;
+       static KeyMap<bool> boolCallbackMap;
+       static KeyMap<double> doubleCallbackMap;
+       static KeyMap<std::string> stringCallbackMap;
+
+       template<class KeyMapType, class VconfCbType, class CallbackType>
+       inline int vconfRegisterKeyChangedCb(KeyMapType &keyMap, VconfCbType &vconfCb, const std::string &key, CallbackType &&callback)
+       {
+               auto idx = --idxCurrent;
+               auto iterator = keyMap.find(key);
+               if (iterator == keyMap.end()) {
+                       iterator = keyMap.emplace(key, typename KeyMapType::mapped_type()).first;
+                       if (vconf_notify_key_changed(key.c_str(), vconfCb, &iterator->second) != VCONF_OK) {
+                               ERROR("Vconf: cant register callback for key: %s", key.c_str());
+                               return idx;
+                       }
+               }
+               iterator->second.emplace(idx, callback);
+               return idx;
+       }
+
+       template<class KeyMapType, class VconfCbType>
+       inline void vconfUnregisterCb(KeyMapType &keyMap, VconfCbType &vconfCb, const std::string &key, int idx)
+       {
+               auto iterator = keyMap.find(key);
+               if (iterator == keyMap.end()) {
+                       ERROR("Vconf: unregister, key: %s not found", key.c_str());
+                       return;
+               }
+               if (iterator->second.erase(idx) != 1)
+                       ERROR("Vconf: could not remove callback for key: %s with idx: %d", key.c_str(), idx);
+               if (iterator->second.empty() && inCallbackKeyGuard != key) {
+                       keyMap.erase(iterator);
+                       if (vconf_ignore_key_changed(key.c_str(), vconfCb) != VCONF_OK) {
+                               ERROR("Vconf: cant unregister callback for key: %s", key.c_str());
+                       }
+               }
+       }
+
+
+
+       template<class KeyMapType, class VconfCbType, class vconfGetType>
+       inline void vconfCbRun(KeyMapType &keyMap, VconfCbType &vconfCb, const vconfGetType &vconfGetFunction, const char *expectedType, int vconfType, keynode_t *keynode, void *data)
+       {
+               std::string key = vconf_keynode_get_name(keynode);
+               if (vconf_keynode_get_type(keynode) != vconfType) {
+                       ERROR("vconf error: got '%s' changed with type: %d, expected type: %s", key.c_str(), vconf_keynode_get_type(keynode), expectedType);
+                       return;
+               }
+               auto value = vconfGetFunction(keynode);
+               auto callbackMapPtr = (typename KeyMapType::mapped_type *)data;
+               ASSERT(!callbackMapPtr->empty(), "vconf error: no callbacks registered");
+
+               //we allow register and unregister callbacks during a callback call
+               {
+                       //make sure we don't delete callbackMap during callbacks execution
+                       inCallbackKeyGuard = key;
+                       int idx = 0;
+                       typename KeyMapType::mapped_type::iterator callbackIterator;
+                       while ((callbackIterator = callbackMapPtr->upper_bound(idx)) != callbackMapPtr->end()) {
+                               idx = callbackIterator->first;
+                               auto callback = callbackIterator->second;
+                               callback(value);
+                       }
+                       inCallbackKeyGuard.clear();
+                       if (callbackMapPtr->empty()) {
+                               //all callbacks removed
+                               keyMap.erase(key);
+                               if (vconf_ignore_key_changed(key.c_str(), vconfCb) != VCONF_OK) {
+                                       ERROR("Vconf: cant unregister callback for key: %s", key.c_str());
+                               }
+                       }
+               }
+       }
+
+       void vconfIntCb(keynode_t *key, void *data)
+       {
+               vconfCbRun(intCallbackMap, vconfIntCb, vconf_keynode_get_int, "int", VCONF_TYPE_INT, key, data);
+       }
+       void vconfBoolCb(keynode_t *key, void *data)
+       {
+               vconfCbRun(boolCallbackMap, vconfBoolCb, vconf_keynode_get_bool, "bool", VCONF_TYPE_BOOL, key, data);
+       }
+       void vconfDoubleCb(keynode_t *key, void *data)
+       {
+               vconfCbRun(doubleCallbackMap, vconfDoubleCb, vconf_keynode_get_dbl, "double", VCONF_TYPE_DOUBLE, key, data);
+       }
+       void vconfStringCb(keynode_t *key, void *data)
+       {
+               vconfCbRun(stringCallbackMap, vconfStringCb, vconf_keynode_get_str, "char*", VCONF_TYPE_STRING, key, data);
+       }
+}
+
+
+class VConfImpl : public VConfInterface
+{
+public:
+       enum class VConfRecordType {INT, BOOL, DOUBLE, STRING};
+
+       struct Handle : public HandleBase {
+               std::string key;
+               int callbackIdx;
+               VConfRecordType vconfRecordType;
+
+               Handle(std::string key, int callbackIdx, VConfRecordType vconfRecordType)
+                       : key{std::move(key)}, callbackIdx{callbackIdx}, vconfRecordType{vconfRecordType}
+               {}
+
+               ~Handle() override
+               {
+                       switch (vconfRecordType) {
+                       case VConfRecordType::INT:
+                               vconfUnregisterCb(intCallbackMap, vconfIntCb, key, callbackIdx);
+                               break;
+                       case VConfRecordType::BOOL:
+                               vconfUnregisterCb(boolCallbackMap, vconfBoolCb, key, callbackIdx);
+                               break;
+                       case VConfRecordType::DOUBLE:
+                               vconfUnregisterCb(doubleCallbackMap, vconfDoubleCb, key, callbackIdx);
+                               break;
+                       case VConfRecordType::STRING:
+                               vconfUnregisterCb(stringCallbackMap, vconfStringCb, key, callbackIdx);
+                               break;
+                       }
+               }
+       };
+
+       int get(const std::string &key, int value) const override
+       {
+               auto k = keyPrefix + key;
+               if (vconf_get_int(k.c_str(), &value) != VCONF_OK)
+                       WARNING("Vconf: cant get value for key: %s, returning default value = %d", k.c_str(), value);
+               return value;
+       }
+
+       bool get(const std::string &key, bool defaultValue) const override
+       {
+               auto k = keyPrefix + key;
+               int value = defaultValue;
+               if (vconf_get_bool(k.c_str(), &value) != VCONF_OK)
+                       WARNING("Vconf: cant get value for key: %s, returning default value = %s", k.c_str(), value ? "true" : "false");
+               return value;
+       }
+
+       double get(const std::string &key, double value) const override
+       {
+               auto k = keyPrefix + key;
+               if (vconf_get_dbl(k.c_str(), &value) != VCONF_OK)
+                       WARNING("Vconf: cant get value for key: %s, returning default value = %lf", k.c_str(), value);
+               return value;
+       }
+
+       std::string get(const std::string &key, const std::string &value) const override
+       {
+               auto k = keyPrefix + key;
+               char *str = vconf_get_str(k.c_str());
+               if (str)
+                       return str;
+
+               WARNING("Vconf: cant get value for key: %s, returning default value = %s", k.c_str(), value.c_str());
+               return value;
+       }
+
+
+       void set(const std::string &key, int value) override
+       {
+               auto k = keyPrefix + key;
+               auto status = vconf_set_int(k.c_str(), value);
+               if (status != VCONF_OK)
+                       ERROR("Vconf error %d: cant set value for key: %s, value = %d", status, k.c_str(), value);
+       }
+
+       void set(const std::string &key, bool value) override
+       {
+               auto k = keyPrefix + key;
+               auto status = vconf_set_bool(k.c_str(), (int)value);
+               if (status != VCONF_OK)
+                       ERROR("Vconf error %d: cant set value for key: %s, value = %s", status, k.c_str(), value ? "true" : "false");
+       }
+
+       void set(const std::string &key, double value) override
+       {
+               auto k = keyPrefix + key;
+               auto status = vconf_set_dbl(k.c_str(), value);
+               if (status != VCONF_OK)
+                       ERROR("Vconf error %d: cant set value for key: %s, value = %lf", status, k.c_str(), value);
+       }
+
+       void set(const std::string &key, const std::string &value) override
+       {
+               auto k = keyPrefix + key;
+               auto status = vconf_set_str(k.c_str(), value.c_str());
+               if (status != VCONF_OK)
+                       ERROR("Vconf error %d: cant set value for key: %s, value = %s", status, k.c_str(), value.c_str());
+       }
+
+
+       CallbackHandle registerKeyChangedCb(const std::string &key, std::function<void(int)> callback) override
+       {
+               auto k = keyPrefix + key;
+               auto idx = vconfRegisterKeyChangedCb(intCallbackMap, vconfIntCb, k, std::move(callback));
+               return CallbackHandle{ new Handle{k, idx, VConfRecordType::INT} };
+       }
+
+       CallbackHandle registerKeyChangedCb(const std::string &key, std::function<void(bool)> callback) override
+       {
+               auto k = keyPrefix + key;
+               auto idx = vconfRegisterKeyChangedCb(boolCallbackMap, vconfBoolCb, k, std::move(callback));
+               return CallbackHandle{ new Handle{k, idx, VConfRecordType::BOOL} };
+       }
+
+       CallbackHandle registerKeyChangedCb(const std::string &key, std::function<void(double)> callback) override
+       {
+               auto k = keyPrefix + key;
+               auto idx = vconfRegisterKeyChangedCb(doubleCallbackMap, vconfDoubleCb, k, std::move(callback));
+               return CallbackHandle{ new Handle{k, idx, VConfRecordType::DOUBLE} };
+       }
+
+       CallbackHandle registerKeyChangedCb(const std::string &key, std::function<void(std::string)> callback) override
+       {
+               auto k = keyPrefix + key;
+               auto idx = vconfRegisterKeyChangedCb(stringCallbackMap, vconfStringCb, k, std::move(callback));
+               return CallbackHandle{ new Handle{k, idx, VConfRecordType::STRING} };
+       }
+
+       void setKeyPrefix(const std::string &prefix) override
+       {
+               keyPrefix = prefix;
+       }
+
+private:
+       std::string keyPrefix;
+};
+
+template<>
+template<>
+std::unique_ptr<VConfInterface> Singleton<VConfInterface>::createImplementation<VConfInterface>()
+{
+       return std::unique_ptr<VConfInterface> { new VConfImpl };
+}
diff --git a/src/VConf.hpp b/src/VConf.hpp
new file mode 100644 (file)
index 0000000..58d43a7
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2017  Samsung Electronics Co., Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef VCONF_HPP
+#define VCONF_HPP
+
+#include "AccessibilitySettingLog.hpp"
+#include "Singleton.hpp"
+
+#include <string>
+#include <functional>
+#include <memory>
+
+/**
+ * Singleton<VConfInterface>::instance() is a c++ singleton wrapper around vconf.
+ *
+ * usage examples:
+ * 1. get key value:
+ * bool enabled = Singleton<VConfInterface>::instance().get("db/setting/accessibility/universal-switch", false);
+ * 2. set key value:
+ * Singleton<VConfInterface>::instance().set("db/setting/accessibility/universal-switch/FEEDBACK_VOICE_SPEECH_VOLUME", 0.50);
+ * 3. register callback on value change:
+ * auto print = [](bool enabled) { std::cout << enabled << std::endl; }
+ * CallbackHandle handle = Singleton<VConfInterface>::instance().registerKeyChangedCb("db/setting/accessibility/universal-switch", print);
+ *   callback will be registered until \p handle exists.
+ * 4. register callback on value change and get current value:
+ * auto print = [](bool enabled) { std::cout << enabled << std::endl; }
+ * CallbackHandle handle = Singleton<VConfInterface>::instance().registerAndGet("db/setting/accessibility/universal-switch", print);
+ *   callback will be registered until \p handle exists.
+ *
+ */
+
+class VConfInterface
+{
+public:
+       struct HandleBase {
+               virtual ~HandleBase() = default;
+       };
+
+       using CallbackHandle = std::unique_ptr<HandleBase>;
+
+       virtual int get(const std::string &key, int defaultValue) const = 0;
+
+       virtual bool get(const std::string &key, bool defaultValue) const = 0;
+
+       virtual double get(const std::string &key, double defaultValue) const = 0;
+
+       virtual std::string get(const std::string &key, const std::string &defaultValue) const = 0;
+
+       virtual void set(const std::string &key, int value) = 0;
+
+       virtual void set(const std::string &key, bool value) = 0;
+
+       virtual void set(const std::string &key, double value) = 0;
+
+       virtual void set(const std::string &key, const std::string &value) = 0;
+
+       virtual CallbackHandle registerKeyChangedCb(const std::string &key, std::function<void(int)> callback) = 0;
+
+       virtual CallbackHandle registerKeyChangedCb(const std::string &key, std::function<void(bool)> callback) = 0;
+
+       virtual CallbackHandle registerKeyChangedCb(const std::string &key, std::function<void(double)> callback) = 0;
+
+       virtual CallbackHandle registerKeyChangedCb(const std::string &key, std::function<void(std::string)> callback) = 0;
+
+       template<class T>
+       inline CallbackHandle registerKeyChangedCb(const std::string &key, std::function<void(T)> callback)
+       {
+               return registerKeyChangedCb(key, callback);
+       }
+
+       template<class T>
+       CallbackHandle registerAndGet(const std::string &key, T defaultValue, std::function<void(T)> callback)
+       {
+               auto handle = registerKeyChangedCb(key, callback);
+               callback(get(key, defaultValue));
+               return handle;
+       }
+
+       virtual void setKeyPrefix(const std::string &prefix) = 0;
+};
+
+#endif