From 52f4c8e98be9fb6b6489cbfa21aba74261b91fb4 Mon Sep 17 00:00:00 2001 From: Oskar Chodowicz Date: Fri, 22 Mar 2019 18:00:47 +0100 Subject: [PATCH] Add implementation of Vconf wrapper created for Universal Switch Change-Id: Id13305972b9def428d491857277daf8f784d9032 --- src/Singleton.hpp | 58 ++++++++++++ src/VConf.cpp | 273 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/VConf.hpp | 96 +++++++++++++++++++ 3 files changed, 427 insertions(+) create mode 100644 src/Singleton.hpp create mode 100644 src/VConf.cpp create mode 100644 src/VConf.hpp diff --git a/src/Singleton.hpp b/src/Singleton.hpp new file mode 100644 index 0000000..0c5b73e --- /dev/null +++ b/src/Singleton.hpp @@ -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 +#include + +/* + * 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::instance.myMethod(); + * Class provided as template parameter must contain public empty constructor + */ + +template +class Singleton +{ +public: + static T &instance() + { + static std::unique_ptr instance{ createImplementation() }; + return *instance; + } + +private: + Singleton() = default; + Singleton(const Singleton &) = delete; + Singleton(Singleton &&) = delete; + Singleton &operator = (const Singleton &) = delete; + Singleton &operator = (Singleton &&) = delete; + + template + static typename std::enable_if::value, std::unique_ptr>::type createImplementation() + { + return std::unique_ptr { new Q }; + } + + template + static typename std::enable_if < !std::is_default_constructible::value, std::unique_ptr>::type createImplementation(); +}; + +#endif diff --git a/src/VConf.cpp b/src/VConf.cpp new file mode 100644 index 0000000..7206c35 --- /dev/null +++ b/src/VConf.cpp @@ -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 +#include +#include +#include +#include + +namespace +{ + template + using CallbackMap = std::map>; + template + using KeyMap = std::unordered_map>; + static std::string inCallbackKeyGuard; + + static int idxCurrent = std::numeric_limits::max(); + static KeyMap intCallbackMap; + static KeyMap boolCallbackMap; + static KeyMap doubleCallbackMap; + static KeyMap stringCallbackMap; + + template + 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 + 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 + 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 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 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 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 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 Singleton::createImplementation() +{ + return std::unique_ptr { new VConfImpl }; +} diff --git a/src/VConf.hpp b/src/VConf.hpp new file mode 100644 index 0000000..58d43a7 --- /dev/null +++ b/src/VConf.hpp @@ -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 +#include +#include + +/** + * Singleton::instance() is a c++ singleton wrapper around vconf. + * + * usage examples: + * 1. get key value: + * bool enabled = Singleton::instance().get("db/setting/accessibility/universal-switch", false); + * 2. set key value: + * Singleton::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::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::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; + + 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 callback) = 0; + + virtual CallbackHandle registerKeyChangedCb(const std::string &key, std::function callback) = 0; + + virtual CallbackHandle registerKeyChangedCb(const std::string &key, std::function callback) = 0; + + virtual CallbackHandle registerKeyChangedCb(const std::string &key, std::function callback) = 0; + + template + inline CallbackHandle registerKeyChangedCb(const std::string &key, std::function callback) + { + return registerKeyChangedCb(key, callback); + } + + template + CallbackHandle registerAndGet(const std::string &key, T defaultValue, std::function callback) + { + auto handle = registerKeyChangedCb(key, callback); + callback(get(key, defaultValue)); + return handle; + } + + virtual void setKeyPrefix(const std::string &prefix) = 0; +}; + +#endif -- 2.7.4