summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJiwoong Im <jiwoong.im@samsung.com>2015-05-29 16:54:32 +0900
committerJiwoong Im <jiwoong.im@samsung.com>2015-06-03 14:08:32 +0900
commitba3841dce24c90e1795436997fa408696ed21311 (patch)
tree7389037a1716a7d30de213ce2c545ad87a35664b
parent864cfb623d9467091502d16b006daa307869ed83 (diff)
downloadapplication-tizen_3.0.2015.q2_common.tar.gz
application-tizen_3.0.2015.q2_common.tar.bz2
application-tizen_3.0.2015.q2_common.zip
change logic and apply inotify. Change-Id: If361b058c81cf1cb235c661e967417fc9cca9e19 Signed-off-by: Jiwoong Im <jiwoong.im@samsung.com>
-rw-r--r--include/app_preference.h176
-rwxr-xr-xinclude/app_preference_internal.h142
-rwxr-xr-xinclude/app_preference_log.h92
-rw-r--r--preference/CMakeLists.txt1
-rw-r--r--preference/preference.c1688
-rwxr-xr-xpreference/preference_db.c915
-rwxr-xr-xpreference/preference_inoti.c416
7 files changed, 2816 insertions, 614 deletions
diff --git a/include/app_preference.h b/include/app_preference.h
index 9aeb3af..8b2ff86 100644
--- a/include/app_preference.h
+++ b/include/app_preference.h
@@ -11,7 +11,7 @@
* 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.
+ * limitations under the License.
*/
@@ -25,32 +25,38 @@ extern "C" {
#endif
/**
- * @addtogroup CAPI_PREFERENCE_MODULE
+ * @file app_preference.h
+ */
+
+/**
+ * @addtogroup CAPI_PREFERENCE_MODULE
* @{
*/
/**
- * @brief Enumerations of error code for Preference.
+ * @brief Enumeration for Preference Error.
+ * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
*/
typedef enum
{
PREFERENCE_ERROR_NONE = TIZEN_ERROR_NONE, /**< Successful */
PREFERENCE_ERROR_INVALID_PARAMETER = TIZEN_ERROR_INVALID_PARAMETER, /**< Invalid parameter */
PREFERENCE_ERROR_OUT_OF_MEMORY = TIZEN_ERROR_OUT_OF_MEMORY, /**< Out of memory */
- PREFERENCE_ERROR_NO_KEY = TIZEN_ERROR_KEY_NOT_AVAILABLE, /**< Required key not available */
+ PREFERENCE_ERROR_NO_KEY = TIZEN_ERROR_APPLICATION | 0x30, /**< Required key not available */
PREFERENCE_ERROR_IO_ERROR = TIZEN_ERROR_IO_ERROR , /**< Internal I/O Error */
} preference_error_e;
/**
- * @brief Called when the given key's value in the preference changes.
+ * @brief Called when the given key's value in the preference changes.
*
- * @details When the @a key is added or removed, this callback function is skipped. (only update can be handled)
+ * @details When the @a key is added or removed, this callback function is skipped(only update can be handled).
*
- * @param [in] key The name of the key in the preference
- * @param [in] user_data The user data passed from the callback registration function
- * @pre This function is invoked when the value of the key is overwritten after you register this callback using preference_set_changed_cb()
+ * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @param[in] key The name of the key in the preference
+ * @param[in] user_data The user data passed from the callback registration function
+ * @pre This function is invoked when the value of the key is overwritten after you register this callback using preference_set_changed_cb().
* @see preference_set_changed_cb()
* @see preference_unset_changed_cb()
* @see preference_set_boolean()
@@ -62,42 +68,47 @@ typedef void (*preference_changed_cb) (const char *key, void *user_data);
/**
-* @brief Called to get key string once for each key-value pair in the preference.
-*
-* @remarks You should not free @a key returned by this function.
-*
-* @param [in] key The key of the value added to the preference
-* @param [in] value The value associated with the key
-* @param [in] user_data The user data passed from the foreach function
-* @return @c true to continue with the next iteration of the loop, \n @c false to break out of the loop.
-* @pre preference_foreach_item() will invoke this callback function.
-* @see preference_foreach_item()
-*/
+ * @brief Called to get key string, once for each key-value pair in the preference.
+ *
+ * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @remarks You should not free the @a key returned by this function.
+ *
+ * @param[in] key The key of the value added to the preference
+ * @param[in] value The value associated with the key
+ * @param[in] user_data The user data passed from the foreach function
+ * @return @c true to continue with the next iteration of the loop,
+ * otherwise @c false to break out of the loop
+ * @pre preference_foreach_item() will invoke this callback function.
+ * @see preference_foreach_item()
+ */
typedef bool (*preference_item_cb)(const char *key, void *user_data);
/**
* @brief Sets an integer value in the preference.
*
- * @param [in] key The name of the key to modify
- * @param [in] value The new @c int value for the given key
- * @return 0 on success, otherwise a negative error value.
+ * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @param[in] key The name of the key to modify
+ * @param[in] value The new @c int value for the given key
+ * @return @c 0 on success,
+ * otherwise a negative error value
* @retval #PREFERENCE_ERROR_NONE Successful
* @retval #PREFERENCE_ERROR_INVALID_PARAMETER Invalid parameter
* @retval #PREFERENCE_ERROR_IO_ERROR Internal I/O Error
* @see preference_get_int()
- *
*/
int preference_set_int(const char *key, int value);
/**
- * @brief Gets a integer value from the preference.
+ * @brief Gets an integer value from the preference.
*
- * @param [in] key The name of the key to retrieve
- * @param [out] value The @c int value for the given key
- * @return 0 on success, otherwise a negative error value.
- * @retval #PREFERENCE_ERROR_NONE Successful
+ * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @param[in] key The name of the key to retrieve
+ * @param[out] value The @c int value for the given key
+ * @return @c 0 on success,
+ * otherwise a negative error value
+ * @retval #PREFERENCE_ERROR_NONE Successful
* @retval #PREFERENCE_ERROR_INVALID_PARAMETER Invalid parameter
* @retval #PREFERENCE_ERROR_NO_KEY Required key not available
* @retval #PREFERENCE_ERROR_IO_ERROR Internal I/O Error
@@ -109,14 +120,15 @@ int preference_get_int(const char *key, int *value);
/**
* @brief Sets a double value in the preference.
*
- * @param [in] key The name of the key to modify
- * @param [in] value The new @c double value associated with given key
- * @return 0 on success, otherwise a negative error value.
+ * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @param[in] key The name of the key to modify
+ * @param[in] value The new @c double value associated with the given key
+ * @return @c 0 on success,
+ * otherwise a negative error value
* @retval #PREFERENCE_ERROR_NONE Successful
* @retval #PREFERENCE_ERROR_INVALID_PARAMETER Invalid parameter
* @retval #PREFERENCE_ERROR_IO_ERROR Internal I/O Error
* @see preference_get_double()
- *
*/
int preference_set_double(const char *key, double value);
@@ -124,15 +136,16 @@ int preference_set_double(const char *key, double value);
/**
* @brief Gets a double value from the preference.
*
- * @param [in] key The name of the key to retrieve
- * @param [out] value The @c double value associated with given key
- * @return 0 on success, otherwise a negative error value.
+ * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @param[in] key The name of the key to retrieve
+ * @param[out] value The @c double value associated with the given key
+ * @return @c 0 on success,
+ * otherwise a negative error value
* @retval #PREFERENCE_ERROR_NONE Successful
* @retval #PREFERENCE_ERROR_INVALID_PARAMETER Invalid parameter
* @retval #PREFERENCE_ERROR_NO_KEY Required key not available
* @retval #PREFERENCE_ERROR_IO_ERROR Internal I/O Error
* @see preference_set_double()
- *
*/
int preference_get_double(const char *key, double *value);
@@ -141,15 +154,16 @@ int preference_get_double(const char *key, double *value);
* @brief Sets a string value in the preference.
*
* @details It makes a deep copy of the added string value.
- *
- * @param [in] key The name of the key to modify
- * @param [in] value The new @c string value associated with given key
- * @return 0 on success, otherwise a negative error value.
+ *
+ * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @param[in] key The name of the key to modify
+ * @param[in] value The new @c string value associated with the given key
+ * @return @c 0 on success,
+ * otherwise a negative error value
* @retval #PREFERENCE_ERROR_NONE Successful
* @retval #PREFERENCE_ERROR_INVALID_PARAMETER Invalid parameter
* @retval #PREFERENCE_ERROR_IO_ERROR Internal I/O Error
* @see preference_get_string()
- *
*/
int preference_set_string(const char *key, const char *value);
@@ -157,11 +171,13 @@ int preference_set_string(const char *key, const char *value);
/**
* @brief Gets a string value from the preference.
*
- * @remarks @a value must be released with free() by you.
- * @param [in] key The name of the key to retrieve
- * @param [out] value The @c string value associated with given key
- * @return 0 on success, otherwise a negative error value.
- * @retval #PREFERENCE_ERROR_NONE Successful
+ * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @remarks @a value must be released using free().
+ * @param[in] key The name of the key to retrieve
+ * @param[out] value The @c string value associated with the given key
+ * @return @c 0 on success,
+ * otherwise a negative error value
+ * @retval #PREFERENCE_ERROR_NONE Successful
* @retval #PREFERENCE_ERROR_INVALID_PARAMETER Invalid parameter
* @retval #PREFERENCE_ERROR_OUT_OF_MEMORY Out of memory
* @retval #PREFERENCE_ERROR_NO_KEY Required key not available
@@ -174,12 +190,14 @@ int preference_get_string(const char *key, char **value);
/**
* @brief Sets a boolean value in the preference.
*
- * @param [in] key The name of the key to modify
- * @param [in] value The new boolean @c value associated with given key
- * @return 0 on success, otherwise a negative error value.
+ * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @param[in] key The name of the key to modify
+ * @param[in] value The new @c boolean value associated with the given key
+ * @return @c 0 on success,
+ * otherwise a negative error value
* @retval #PREFERENCE_ERROR_NONE Successful
* @retval #PREFERENCE_ERROR_INVALID_PARAMETER Invalid parameter
- * @retval #PREFERENCE_ERROR_IO_ERROR Internal I/O Error
+ * @retval #PREFERENCE_ERROR_IO_ERROR Internal I/O Error
* @see preference_get_boolean()
*/
int preference_set_boolean(const char *key, bool value);
@@ -188,9 +206,11 @@ int preference_set_boolean(const char *key, bool value);
/**
* @brief Gets a boolean value from the preference.
*
- * @param [in] key The name of the key to retrieve
- * @param [out] value The boolean @c value associated with given key
- * @return 0 on success, otherwise a negative error value.
+ * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @param[in] key The name of the key to retrieve
+ * @param[out] value The @c boolean value associated with the given key
+ * @return @c 0 on success,
+ * otherwise a negative error value
* @retval #PREFERENCE_ERROR_NONE Successful
* @retval #PREFERENCE_ERROR_INVALID_PARAMETER Invalid parameter
* @retval #PREFERENCE_ERROR_NO_KEY Required key not available
@@ -203,22 +223,26 @@ int preference_get_boolean(const char *key, bool *value);
/**
* @brief Removes any value with the given @a key from the preference.
*
- * @param [in] key The name of the key to remove
- * @return 0 on success, otherwise a negative error value.
+ * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @param[in] key The name of the key to remove
+ * @return @c 0 on success,
+ * otherwise a negative error value
* @retval #PREFERENCE_ERROR_NONE Successful
- * @retval #PREFERENCE_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #PREFERENCE_ERROR_INVALID_PARAMETER Invalid parameter
* @retval #PREFERENCE_ERROR_IO_ERROR Internal I/O Error
- *
*/
int preference_remove(const char *key);
/**
- * @brief Checks whether if the given @a key exists in the preference.
+ * @brief Checks whether the given @a key exists in the preference.
*
- * @param [in] key The name of the key to check
- * @param [out] existing @c true if the @a key exists in the preference, otherwise @c false
- * @return 0 on success, otherwise a negative error value.
+ * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @param[in] key The name of the key to check
+ * @param[out] existing If @c true the @a key exists in the preference,
+ * otherwise @c false
+ * @return @c 0 on success,
+ * otherwise a negative error value
* @retval #PREFERENCE_ERROR_NONE Successful
* @retval #PREFERENCE_ERROR_INVALID_PARAMETER Invalid parameter
* @retval #PREFERENCE_ERROR_IO_ERROR Internal I/O Error
@@ -229,7 +253,9 @@ int preference_is_existing(const char *key, bool *existing);
/**
* @brief Removes all key-value pairs from the preference.
*
- * @return 0 on success, otherwise a negative error value.
+ * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @return @c 0 on success,
+ * otherwise a negative error value
* @retval #PREFERENCE_ERROR_NONE Successful
* @retval #PREFERENCE_ERROR_IO_ERROR Internal I/O Error
* @see preference_remove()
@@ -240,10 +266,12 @@ int preference_remove_all(void);
/**
* @brief Registers a callback function to be invoked when value of the given key in the preference changes.
*
- * @param [in] key The name of the key to monitor
- * @param [in] callback The callback function to register
- * @param [in] user_data The user data to be passed to the callback function
- * @return 0 on success, otherwise a negative error value.
+ * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @param[in] key The name of the key to monitor
+ * @param[in] callback The callback function to register
+ * @param[in] user_data The user data to be passed to the callback function
+ * @return @c 0 on success,
+ * otherwise a negative error value
* @retval #PREFERENCE_ERROR_NONE Successful
* @retval #PREFERENCE_ERROR_INVALID_PARAMETER Invalid parameter
* @retval #PREFERENCE_ERROR_OUT_OF_MEMORY Out of memory
@@ -259,8 +287,10 @@ int preference_set_changed_cb(const char *key, preference_changed_cb callback, v
/**
* @brief Unregisters the callback function.
*
- * @param [in] key The name of the key to monitor
- * @return 0 on success, otherwise a negative error value.
+ * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @param[in] key The name of the key to monitor
+ * @return @c 0 on success,
+ * otherwise a negative error value
* @retval #PREFERENCE_ERROR_NONE Successful
* @retval #PREFERENCE_ERROR_INVALID_PARAMETER Invalid parameter
* @retval #PREFERENCE_ERROR_IO_ERROR Internal I/O Error
@@ -272,9 +302,11 @@ int preference_unset_changed_cb(const char *key);
/**
* @brief Retrieves all key-value pairs in the preference by invoking the callback function.
*
- * @param [in] callback The callback function to get key value once for each key-value pair in the preference
- * @param [in] user_data The user data to be passed to the callback function
- * @return 0 on success, otherwise a negative error value.
+ * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ * @param[in] callback The callback function to get key value once for each key-value pair in the preference
+ * @param[in] user_data The user data to be passed to the callback function
+ * @return @c 0 on success,
+ * otherwise a negative error value
* @retval #PREFERENCE_ERROR_NONE Successful
* @retval #PREFERENCE_ERROR_INVALID_PARAMETER Invalid parameter
* @retval #PREFERENCE_ERROR_IO_ERROR Internal I/O Error
diff --git a/include/app_preference_internal.h b/include/app_preference_internal.h
new file mode 100755
index 0000000..8108903
--- /dev/null
+++ b/include/app_preference_internal.h
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 __TIZEN_APPFW_PREFERENCE_INTERNAL_H__
+#define __TIZEN_APPFW_PREFERENCE_INTERNAL_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "app_preference_log.h"
+
+#define BUF_LEN (4096)
+#define PREF_DIR ".pref/"
+
+#define PREFERENCE_KEY_PATH_LEN 1024
+#define ERR_LEN 128
+
+#define PREF_DB_NAME ".pref.db"
+#define PREF_TBL_NAME "pref"
+#define PREF_F_KEY_NAME "pref_key"
+#define PREF_F_TYPE_NAME "pref_type"
+#define PREF_F_DATA_NAME "pref_data"
+
+/**
+ * @brief Definition for PREFERENCE_ERROR_WRONG_PREFIX.
+ */
+#define PREFERENCE_ERROR_WRONG_PREFIX -2
+
+/**
+ * @brief Definition for PREFERENCE_ERROR_WRONG_TYPE.
+ */
+#define PREFERENCE_ERROR_WRONG_TYPE -3
+
+/**
+ * @brief Definition for PREFERENCE_ERROR_FILE_OPEN.
+ */
+#define PREFERENCE_ERROR_FILE_OPEN -21
+
+/**
+ * @brief Definition for PREFERENCE_ERROR_FILE_FREAD.
+ */
+#define PREFERENCE_ERROR_FILE_FREAD -22
+
+/**
+ * @brief Definition for PREFERENCE_ERROR_FILE_FGETS.
+ */
+#define PREFERENCE_ERROR_FILE_FGETS -23
+
+/**
+ * @brief Definition for PREFERENCE_ERROR_FILE_WRITE.
+ */
+#define PREFERENCE_ERROR_FILE_WRITE -24
+
+/**
+ * @brief Definition for PREFERENCE_ERROR_FILE_SYNC.
+ */
+#define PREFERENCE_ERROR_FILE_SYNC -25
+
+/**
+ * @brief Definition for PREFERENCE_ERROR_FILE_CHMOD.
+ */
+#define PREFERENCE_ERROR_FILE_CHMOD -28
+
+/**
+ * @brief Definition for PREFERENCE_ERROR_FILE_LOCK.
+ */
+#define PREFERENCE_ERROR_FILE_LOCK -29
+
+typedef enum
+{
+ PREFERENCE_TYPE_NONE = 0,
+ PREFERENCE_TYPE_STRING,
+ PREFERENCE_TYPE_INT,
+ PREFERENCE_TYPE_DOUBLE,
+ PREFERENCE_TYPE_BOOLEAN,
+} preference_type_e;
+
+typedef struct _pref_changed_cb_node_t{
+ char *key;
+ preference_changed_cb cb;
+ void *user_data;
+ struct _pref_changed_cb_node_t *prev;
+ struct _pref_changed_cb_node_t *next;
+} pref_changed_cb_node_t;
+
+typedef struct _keynode_t {
+ char *keyname; /**< Keyname for keynode */
+ int type; /**< Keynode type */
+ union {
+ int i; /**< Integer type */
+ int b; /**< Bool type */
+ double d; /**< Double type */
+ char *s; /**< String type */
+ } value; /**< Value for keynode */
+ struct _keynode_t *next; /**< Next keynode */
+} keynode_t;
+
+/**
+ * @brief The structure type for opaque type. It must be used via accessor functions.
+ * @since_tizen @if MOBILE 2.3 @elseif WEARABLE 2.3.1 @endif
+ */
+typedef struct _keylist_t {
+ int num; /**< Number of list */
+ keynode_t *head; /**< Head node */
+ keynode_t *cursor; /**< Cursor node */
+} keylist_t;
+
+
+int _preference_kdb_add_notify
+ (const char *keyname, preference_changed_cb cb, void *data);
+int _preference_kdb_del_notify
+ (const char *keyname);
+
+int _preference_get_key_path(const char *keyname, char *path);
+int _preference_get_key(keynode_t *keynode);
+
+int _preference_keynode_set_keyname(keynode_t *keynode, const char *keyname);
+inline void _preference_keynode_set_null(keynode_t *keynode);
+inline keynode_t *_preference_keynode_new(void);
+inline void _preference_keynode_free(keynode_t *keynode);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __TIZEN_APPFW_PREFERENCE_INTERNAL_H__ */
diff --git a/include/app_preference_log.h b/include/app_preference_log.h
new file mode 100755
index 0000000..8bd87a7
--- /dev/null
+++ b/include/app_preference_log.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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.
+ */
+
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif
+
+#define LOG_TAG "CAPI_APPFW_APPLICATION_PREFERENCE"
+#define DBG_MODE (1)
+
+#ifndef __PREFERENCE_LOG_H__
+#define __PREFERENCE_LOG_H__
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <dlog.h>
+
+
+#define INFO(fmt, arg...)
+#define DBG(fmt, arg...) SECURE_SLOGI(fmt, ##arg)
+#define ERR(fmt, arg...) LOGE(fmt, ##arg)
+#define SECURE_ERR(fmt, arg...) SECURE_SLOGE(fmt, ##arg)
+#define FATAL(fmt, arg...) SECURE_SLOGF(fmt, ##arg)
+#define WARN(fmt, arg...) SECURE_SLOGW(fmt, ##arg)
+
+
+/************** Return ***************/
+#define ret_if(expr) \
+ do { \
+ if (expr) { \
+ ERR("(%s) -> %s() return", #expr, __FUNCTION__); \
+ return; \
+ } \
+ } while (0)
+#define retv_if(expr, val) \
+ do { \
+ if (expr) { \
+ ERR("(%s) -> %s() return", #expr, __FUNCTION__); \
+ return (val); \
+ } \
+ } while (0)
+#define retm_if(expr, fmt, arg...) \
+ do { \
+ if (expr) { \
+ ERR(fmt, ##arg); \
+ return; \
+ } \
+ } while (0)
+#define retvm_if(expr, val, fmt, arg...) \
+ do { \
+ if (expr) { \
+ ERR(fmt, ##arg); \
+ return (val); \
+ } \
+ } while (0)
+#define retex_if(expr, fmt, arg...) \
+ do { \
+ if (expr) { \
+ ERR(fmt, ##arg); \
+ goto CATCH; \
+ } \
+ } while (0)
+
+
+/************** TimeCheck ***************/
+#ifdef PREFERENCE_TIMECHECK
+#define START_TIME_CHECK \
+ init_time();\
+ startT = set_start_time();
+#define END_TIME_CHECK \
+ PREFERENCE_DEBUG("time = %f ms\n", exec_time(startT));
+#else
+#define START_TIME_CHECK
+#define END_TIME_CHECK
+#endif
+
+
+#endif /* __PREFERENCE_LOG_H__ */
diff --git a/preference/CMakeLists.txt b/preference/CMakeLists.txt
index 73b2d4e..21da809 100644
--- a/preference/CMakeLists.txt
+++ b/preference/CMakeLists.txt
@@ -32,6 +32,7 @@ SET(CMAKE_EXE_LINKER_FLAGS "-Wl,--as-needed -Wl,--rpath=${LIB_INSTALL_DIR}")
add_library(${fw_name} SHARED
preference.c
+ preference_inoti.c
)
TARGET_LINK_LIBRARIES(${fw_name} capi-appfw-app-common ${${fw_name}_LDFLAGS})
diff --git a/preference/preference.c b/preference/preference.c
index 98faf5d..c19b641 100644
--- a/preference/preference.c
+++ b/preference/preference.c
@@ -8,796 +8,1400 @@
* 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,
+ * 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 <stdio.h>
#include <stdlib.h>
-#include <unistd.h>
+#include <limits.h>
#include <string.h>
-#include <sqlite3.h>
-
-#include <app_private.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/xattr.h>
+#include <ctype.h>
+#include <string.h>
+#include <execinfo.h>
#include <app_preference.h>
-#include <app_preference_private.h>
+#include <app_preference_internal.h>
+#include <app_common.h>
-#include <dlog.h>
-
-#ifdef LOG_TAG
-#undef LOG_TAG
+#ifndef API
+#define API __attribute__ ((visibility("default")))
#endif
-#define LOG_TAG "CAPI_APPFW_APPLICATION_PREFERENCE"
-#define DBG_MODE (1)
+#define PREFERENCE_ERROR_RETRY_CNT 7
+#define PREFERENCE_ERROR_RETRY_SLEEP_UTIME 10000
+
+#define DELIMITER 29
+
+static int g_posix_errno;
+static int g_preference_errno;
+static char *g_pref_dir_path = NULL;
-static sqlite3 *pref_db;
-static bool is_update_hook_registered;
-static pref_changed_cb_node_t *head;
-static void _finish(void *data)
+enum preference_op_t {
+ PREFERENCE_OP_GET = 0,
+ PREFERENCE_OP_SET = 1
+};
+
+#ifdef PREFERENCE_TIMECHECK
+double correction, startT;
+
+double set_start_time(void)
{
- if (pref_db != NULL) {
- sqlite3_close(pref_db);
- pref_db = NULL;
- }
+ struct timeval tv;
+ double curtime;
+
+ gettimeofday(&tv, NULL);
+ curtime = tv.tv_sec * 1000 + (double)tv.tv_usec / 1000;
+ return curtime;
}
-static int _busy_handler(void *pData, int count)
+double exec_time(double start)
{
- if (count < 5) {
- LOGD("Busy Handler Called! : PID(%d) / CNT(%d)\n",
- getpid(), count+1);
- usleep((count+1)*100000);
- return 1;
- } else {
- LOGD("Busy Handler will be returned SQLITE_BUSY error : PID(%d)\n",
- getpid());
- return 0;
- }
+ double end = set_start_time();
+ return (end - start - correction);
}
-static int _initialize(void)
+int init_time(void)
{
- char *data_path;
- char db_path[TIZEN_PATH_MAX];
- int ret;
- char *errmsg;
+ double temp_t;
+ temp_t = set_start_time();
+ correction = exec_time(temp_t);
- data_path = app_get_data_path();
- if (data_path == NULL) {
- LOGE("IO_ERROR(0x%08x) : fail to get data directory",
- PREFERENCE_ERROR_IO_ERROR);
- return PREFERENCE_ERROR_IO_ERROR;
- }
- snprintf(db_path, sizeof(db_path), "%s/%s", data_path, PREF_DB_NAME);
- free(data_path);
+ return 0;
+}
+#endif
- ret = sqlite3_open_v2(db_path, &pref_db, SQLITE_OPEN_CREATE, NULL);
- if (ret != SQLITE_OK) {
- LOGE("IO_ERROR(0x%08x) : fail to open db(%s)",
- PREFERENCE_ERROR_IO_ERROR, sqlite3_errmsg(pref_db));
- _finish(NULL);
- return PREFERENCE_ERROR_IO_ERROR;
+char* _preference_get_pref_dir_path()
+{
+ char *app_data_path = NULL;
+
+ if (!g_pref_dir_path)
+ {
+ g_pref_dir_path = (char *)malloc(PREFERENCE_KEY_PATH_LEN + 1);
+ if ((app_data_path = app_get_data_path()) == NULL)
+ {
+ ERR("IO_ERROR(0x%08x) : fail to get data directory", PREFERENCE_ERROR_IO_ERROR);
+ free(g_pref_dir_path);
+ g_pref_dir_path = NULL;
+ return NULL;
+ }
+ snprintf(g_pref_dir_path, PREFERENCE_KEY_PATH_LEN, "%s%s", app_data_path, PREF_DIR);
+ INFO("pref_dir_path: %s", g_pref_dir_path);
+ free(app_data_path);
}
+ return g_pref_dir_path;
+}
- ret = sqlite3_busy_handler(pref_db, _busy_handler, NULL);
- if (ret != SQLITE_OK) {
- LOGW("IO_ERROR(0x%08x) : fail to register busy handler(%s)\n",
- PREFERENCE_ERROR_IO_ERROR, sqlite3_errmsg(pref_db));
- }
+int _preference_keynode_set_keyname(keynode_t *keynode, const char *keyname)
+{
+ if (keynode->keyname) free(keynode->keyname);
+ keynode->keyname = strndup(keyname, PREFERENCE_KEY_PATH_LEN);
+ retvm_if(keynode->keyname == NULL, PREFERENCE_ERROR_IO_ERROR, "strndup Fails");
+ return PREFERENCE_ERROR_NONE;
+}
- ret = sqlite3_exec(pref_db,
- "CREATE TABLE IF NOT EXISTS pref ( pref_key TEXT PRIMARY KEY, pref_type TEXT, pref_data TEXT)",
- NULL, NULL, &errmsg);
- if (ret != SQLITE_OK) {
- LOGE("IO_ERROR(0x%08x) : fail to create db table(%s)",
- PREFERENCE_ERROR_IO_ERROR, errmsg);
- sqlite3_free(errmsg);
- _finish(NULL);
- return PREFERENCE_ERROR_IO_ERROR;
- }
+static inline void _preference_keynode_set_value_int(keynode_t *keynode, const int value)
+{
+ keynode->type = PREFERENCE_TYPE_INT;
+ keynode->value.i = value;
+}
- app_finalizer_add(_finish, NULL);
+static inline void _preference_keynode_set_value_boolean(keynode_t *keynode, const int value)
+{
+ keynode->type = PREFERENCE_TYPE_BOOLEAN;
+ keynode->value.b = !!value;
+}
- return PREFERENCE_ERROR_NONE;
+static inline void _preference_keynode_set_value_double(keynode_t *keynode, const double value)
+{
+ keynode->type = PREFERENCE_TYPE_DOUBLE;
+ keynode->value.d = value;
}
-static int _prepare_and_bind_stmt(char *buf, const char *type,
- const char *data, const char *key, sqlite3_stmt **stmt)
+static inline void _preference_keynode_set_value_string(keynode_t *keynode, const char *value)
{
- int ret;
+ keynode->type = PREFERENCE_TYPE_STRING;
+ keynode->value.s = strdup(value);
+}
- ret = sqlite3_prepare(pref_db, buf, -1, stmt, NULL);
- if (ret != SQLITE_OK) {
- LOGE("IO_ERROR(0x%08x) : fail to prepare query (%d/%s)",
- PREFERENCE_ERROR_IO_ERROR,
- sqlite3_extended_errcode(pref_db),
- sqlite3_errmsg(pref_db));
- return PREFERENCE_ERROR_IO_ERROR;
- }
+inline keynode_t *_preference_keynode_new(void)
+{
+ keynode_t *keynode;
+ keynode = calloc(1, sizeof(keynode_t));
- ret = sqlite3_bind_text(*stmt, 1, type, -1, SQLITE_STATIC);
- if (ret != SQLITE_OK) {
- LOGE("IO_ERROR(0x%08x) : fail to bind(1) query (%d/%s)",
- PREFERENCE_ERROR_IO_ERROR,
- sqlite3_extended_errcode(pref_db),
- sqlite3_errmsg(pref_db));
- sqlite3_finalize(*stmt);
- return PREFERENCE_ERROR_IO_ERROR;
- }
- ret = sqlite3_bind_text(*stmt, 2, data, -1, SQLITE_STATIC);
- if (ret != SQLITE_OK) {
- LOGE("IO_ERROR(0x%08x) : fail to bind(2) query (%d/%s)",
- PREFERENCE_ERROR_IO_ERROR,
- sqlite3_extended_errcode(pref_db),
- sqlite3_errmsg(pref_db));
- sqlite3_finalize(*stmt);
- return PREFERENCE_ERROR_IO_ERROR;
+ return keynode;
+}
+
+inline void _preference_keynode_free(keynode_t *keynode)
+{
+ if(keynode) {
+ if (keynode->keyname)
+ free(keynode->keyname);
+ if (keynode->type == PREFERENCE_TYPE_STRING && keynode->value.s)
+ free(keynode->value.s);
+ free(keynode);
}
- ret = sqlite3_bind_text(*stmt, 3, key, -1, SQLITE_STATIC);
- if (ret != SQLITE_OK) {
- LOGE("IO_ERROR(0x%08x) : fail to bind(3) query (%d/%s)",
- PREFERENCE_ERROR_IO_ERROR,
- sqlite3_extended_errcode(pref_db),
- sqlite3_errmsg(pref_db));
- sqlite3_finalize(*stmt);
- return PREFERENCE_ERROR_IO_ERROR;
+}
+
+int _preference_get_key_name(const char *keyfile, char *keyname)
+{
+ char convert_key[PREFERENCE_KEY_PATH_LEN] = {0,};
+ char *chrptr = NULL;
+
+ strncpy(convert_key, keyfile, strlen(keyfile));
+
+ chrptr = strchr((const char*)convert_key, DELIMITER);
+ if(chrptr) {
+ chrptr = strchr((const char*)convert_key, DELIMITER);
+ while(chrptr) {
+ convert_key[chrptr-convert_key] = '/';
+ chrptr = strchr((const char*)chrptr+1, DELIMITER);
+ }
}
+ snprintf(keyname, PREFERENCE_KEY_PATH_LEN, "%s", (const char*)convert_key);
return PREFERENCE_ERROR_NONE;
}
-static int _write_data(const char *key, const char *type, const char *data)
+
+int _preference_get_key_path(const char *keyname, char *path)
{
- int ret;
- bool exist = false;
- sqlite3_stmt *stmt;
- char buf[BUF_LEN];
+ const char *key = NULL;
- if (key == NULL || key[0] == '\0' || data == NULL) {
- LOGE("INVALID_PARAMETER(0x%08x)",
- PREFERENCE_ERROR_INVALID_PARAMETER);
- return PREFERENCE_ERROR_INVALID_PARAMETER;
+ if(!keyname) {
+ ERR("keyname is null");
+ return PREFERENCE_ERROR_WRONG_PREFIX;
}
- /* insert data or update data if data already exist */
- ret = preference_is_existing(key, &exist);
- if (ret != PREFERENCE_ERROR_NONE)
- return ret;
-
- if (exist)
- snprintf(buf, sizeof(buf), "UPDATE %s SET %s=?1, %s=?2 WHERE %s=?3;",
- PREF_TBL_NAME, PREF_F_TYPE_NAME,
- PREF_F_DATA_NAME, PREF_F_KEY_NAME);
- else
- snprintf(buf, sizeof(buf),
- "INSERT INTO %s (%s, %s, %s) values (?3, ?1, ?2);",
- PREF_TBL_NAME, PREF_F_KEY_NAME,
- PREF_F_TYPE_NAME, PREF_F_DATA_NAME);
- ret = _prepare_and_bind_stmt(buf, type, data, key, &stmt);
+ char convert_key[PREFERENCE_KEY_PATH_LEN] = {0,};
+ char *chrptr = NULL;
+ char *pref_dir_path = NULL;
- if (ret != PREFERENCE_ERROR_NONE)
- return ret;
+ strncpy(convert_key, keyname, strlen(keyname));
- ret = sqlite3_step(stmt);
- if (ret != SQLITE_DONE) {
- LOGE("IO_ERROR(0x%08x): fail to write data(%d/%s)",
- PREFERENCE_ERROR_IO_ERROR,
- sqlite3_extended_errcode(pref_db),
- sqlite3_errmsg(pref_db));
- sqlite3_finalize(stmt);
+ pref_dir_path = _preference_get_pref_dir_path();
+ if (!pref_dir_path)
+ {
+ LOGE("_preference_get_pref_dir_path() failed.");
return PREFERENCE_ERROR_IO_ERROR;
}
- sqlite3_finalize(stmt);
+ chrptr = strchr((const char*)convert_key, (int)'/');
+ if(!chrptr) {
+ key = (const char*)convert_key;
+ }
+ else {
+ chrptr = strchr((const char*)convert_key, (int)'/');
+ while(chrptr) {
+ convert_key[chrptr-convert_key] = DELIMITER;
+ chrptr = strchr((const char*)chrptr+1, (int)'/');
+ }
+ key = (const char*)convert_key;
+ }
+
+ snprintf(path, PREFERENCE_KEY_PATH_LEN, "%s%s", pref_dir_path, key);
return PREFERENCE_ERROR_NONE;
}
-static int _read_data(const char *key, char *type, char *data)
+static int _preference_set_key_check_pref_dir()
{
- int ret;
- char *buf;
- char **result;
- int rows;
- int columns;
- char *errmsg;
+ char *pref_dir_path = NULL;
+ mode_t dir_mode = 0664 | 0111;
- if (key == NULL || key[0] == '\0' || data == NULL) {
- LOGE("INVALID_PARAMETER(0x%08x)",
- PREFERENCE_ERROR_INVALID_PARAMETER);
- return PREFERENCE_ERROR_INVALID_PARAMETER;
+ pref_dir_path = _preference_get_pref_dir_path();
+ if (!pref_dir_path)
+ {
+ LOGE("_preference_get_pref_dir_path() failed.");
+ return PREFERENCE_ERROR_IO_ERROR;
}
- if (pref_db == NULL) {
- if (_initialize() != PREFERENCE_ERROR_NONE) {
- LOGE("IO_ERROR(0x%08x) : fail to initialize db",
- PREFERENCE_ERROR_IO_ERROR);
+ if (access(pref_dir_path, F_OK) < 0)
+ {
+ if (mkdir(pref_dir_path, dir_mode) < 0)
+ {
+ ERR("mkdir() failed(%d/%s)", errno, strerror(errno));
return PREFERENCE_ERROR_IO_ERROR;
}
}
- buf = sqlite3_mprintf("SELECT %s, %s, %s FROM %s WHERE %s=%Q;",
- PREF_F_KEY_NAME, PREF_F_TYPE_NAME, PREF_F_DATA_NAME,
- PREF_TBL_NAME, PREF_F_KEY_NAME, key);
+ return PREFERENCE_ERROR_NONE;
+}
- if (buf == NULL) {
- LOGE("IO_ERROR(0x%08x) : fail to create query string",
- PREFERENCE_ERROR_IO_ERROR);
+static int _preference_set_key_creation(const char* path)
+{
+ int fd;
+ mode_t temp;
+ temp = umask(0000);
+ fd = open(path, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP);
+ umask(temp);
+ if(fd == -1) {
+ ERR("open(rdwr,create) error: %d(%s)", errno, strerror(errno));
return PREFERENCE_ERROR_IO_ERROR;
}
+ close(fd);
- ret = sqlite3_get_table(pref_db, buf, &result, &rows, &columns, &errmsg);
- sqlite3_free(buf);
- if (ret != SQLITE_OK) {
- LOGE("IO_ERROR(0x%08x) : fail to read data (%s)",
- PREFERENCE_ERROR_IO_ERROR, errmsg);
- sqlite3_free(errmsg);
- return PREFERENCE_ERROR_IO_ERROR;
- }
+ return PREFERENCE_ERROR_NONE;
+}
- if (rows == 0) {
- LOGE("NO_KEY(0x%08x) : fail to find given key(%s)",
- PREFERENCE_ERROR_NO_KEY, key);
- sqlite3_free_table(result);
- return PREFERENCE_ERROR_NO_KEY;
- }
+static int _preference_set_file_lock(int fd, short type)
+{
+ struct flock l;
- snprintf(type, 2, "%s", result[4]);
- snprintf(data, BUF_LEN, "%s", result[5]);
+ l.l_type = type;
+ l.l_start= 0; /*Start at begin*/
+ l.l_whence = SEEK_SET;
+ l.l_len = 0; /*Do it with whole file*/
- sqlite3_free_table(result);
+ return fcntl(fd, F_SETLK, &l);
+}
- return PREFERENCE_ERROR_NONE;
+static int _preference_get_pid_of_file_lock_owner(int fd, short type)
+{
+ struct flock l;
+
+ l.l_type = type;
+ l.l_start= 0; /*Start at begin*/
+ l.l_whence = SEEK_SET;
+ l.l_len = 0; /*Do it with whole file*/
+
+ if(fcntl(fd, F_GETLK, &l) < 0) {
+ WARN("error in getting lock info");
+ return -1;
+ }
+
+ if(l.l_type == F_UNLCK)
+ return 0;
+ else
+ return l.l_pid;
}
-int preference_set_int(const char *key, int value)
+static int _preference_set_read_lock(int fd)
{
- char type[2];
- char data[BUF_LEN];
- snprintf(type, 2, "%d", PREFERENCE_TYPE_INT);
- snprintf(data, BUF_LEN, "%d", value);
- return _write_data(key, type, data);
+ return _preference_set_file_lock(fd, F_RDLCK);
}
-int preference_get_int(const char *key, int *value)
+static int _preference_set_write_lock(int fd)
{
- char type[2];
- char data[BUF_LEN];
- int ret;
+ return _preference_set_file_lock(fd, F_WRLCK);
+}
- if (value == NULL) {
- LOGE("INVALID_PARAMETER(0x%08x)",
- PREFERENCE_ERROR_INVALID_PARAMETER);
- return PREFERENCE_ERROR_INVALID_PARAMETER;
- }
+static int _preference_set_unlock(int fd)
+{
+ return _preference_set_file_lock(fd, F_UNLCK);
+}
- ret = _read_data(key, type, data);
- if (ret == PREFERENCE_ERROR_NONE) {
- if (atoi(type) == PREFERENCE_TYPE_INT)
- *value = atoi(data);
- else {
- LOGE("INVALID_PARAMETER(0x%08x) : param type(%d)",
- PREFERENCE_ERROR_INVALID_PARAMETER, atoi(type));
- return PREFERENCE_ERROR_INVALID_PARAMETER;
+static int _preference_check_retry_err(keynode_t *keynode, int preference_errno, int io_errno, int op_type)
+{
+ int is_busy_err = 0;
+
+ if (preference_errno == PREFERENCE_ERROR_FILE_OPEN)
+ {
+ switch (io_errno)
+ {
+ case ENOENT :
+ {
+ if(op_type == PREFERENCE_OP_SET)
+ {
+ int rc = 0;
+ char path[PREFERENCE_KEY_PATH_LEN] = {0,};
+ rc = _preference_get_key_path(keynode->keyname, path);
+ if (rc != PREFERENCE_ERROR_NONE) {
+ ERR("_preference_get_key_path error");
+ break;
+ }
+
+ rc = _preference_set_key_check_pref_dir();
+ if (rc != PREFERENCE_ERROR_NONE) {
+ ERR("_preference_set_key_check_pref_dir() failed.");
+ break;
+ }
+
+ preference_errno = _preference_set_key_creation(path);
+ if (rc != PREFERENCE_ERROR_NONE) {
+ ERR("_preference_set_key_creation error : %s", path);
+ break;
+ }
+ INFO("%s key is created", keynode->keyname);
+
+ is_busy_err = 1;
+ }
+ break;
+ }
+ case EAGAIN :
+ case EMFILE :
+ case ENFILE :
+ case ETXTBSY :
+ {
+ is_busy_err = 1;
+ }
+ }
+ }
+ else if (preference_errno == PREFERENCE_ERROR_FILE_CHMOD)
+ {
+ switch (io_errno)
+ {
+ case EINTR :
+ case EBADF :
+ {
+ is_busy_err = 1;
+ }
+ }
+ }
+ else if (preference_errno == PREFERENCE_ERROR_FILE_LOCK)
+ {
+ switch (io_errno)
+ {
+ case EBADF :
+ case EAGAIN :
+ case ENOLCK :
+ {
+ is_busy_err = 1;
+ }
}
}
+ else if (preference_errno == PREFERENCE_ERROR_FILE_WRITE)
+ {
+ switch (io_errno)
+ {
+ case 0 :
+ case EAGAIN :
+ case EINTR :
+ case EIO :
+ case ENOMEM :
+ {
+ is_busy_err = 1;
+ }
+ }
+ }
+ else if (preference_errno == PREFERENCE_ERROR_FILE_FREAD)
+ {
+ switch (io_errno)
+ {
+ case EAGAIN :
+ case EINTR :
+ case EIO :
+ {
+ is_busy_err = 1;
+ }
+ }
+ }
+ else
+ {
+ is_busy_err = 0;
+ }
- return ret;
+ if (is_busy_err == 1) {
+ return 1;
+ }
+ else
+ {
+ ERR("key(%s), check retry err: %d/(%d/%s).",keynode->keyname, preference_errno, io_errno, strerror(io_errno));
+ return 0;
+ }
}
-int preference_set_double(const char *key, double value)
+static int _preference_set_key_filesys(keynode_t *keynode, int *io_errno)
{
- char type[2];
- char data[BUF_LEN];
- snprintf(type, 2, "%d", PREFERENCE_TYPE_DOUBLE);
- snprintf(data, BUF_LEN, "%f", value);
- return _write_data(key, type, data);
-}
+ char path[PREFERENCE_KEY_PATH_LEN] = {0,};
+ FILE *fp = NULL;
+ int ret = -1;
+ int func_ret = PREFERENCE_ERROR_NONE;
+ int err_no = 0;
+ char err_buf[100] = { 0, };
+ int is_write_error = 0;
+ int retry_cnt = 0;
+
+retry_open :
+ errno = 0;
+ err_no = 0;
+ func_ret = PREFERENCE_ERROR_NONE;
+
+ ret = _preference_get_key_path(keynode->keyname, path);
+ retv_if(ret != PREFERENCE_ERROR_NONE, ret);
+
+ if( (fp = fopen(path, "r+")) == NULL ) {
+ func_ret = PREFERENCE_ERROR_FILE_OPEN;
+ err_no = errno;
+ goto out_return;
+ }
-int preference_get_double(const char *key, double *value)
-{
- char type[2];
- char data[BUF_LEN];
+retry :
+ errno = 0;
+ err_no = 0;
+ func_ret = PREFERENCE_ERROR_NONE;
+
+ ret = _preference_set_write_lock(fileno(fp));
+ if(ret == -1) {
+ func_ret = PREFERENCE_ERROR_FILE_LOCK;
+ err_no = errno;
+ ERR("file(%s) lock owner(%d)",
+ keynode->keyname,
+ _preference_get_pid_of_file_lock_owner(fileno(fp), F_WRLCK));
+ goto out_return;
+ }
- int ret;
+ /* write key type */
+ ret = fwrite((void *)&(keynode->type), sizeof(int), 1, fp);
+ if(ret <= 0)
+ {
+ if(!errno) {
+ LOGW("number of written items is 0. try again");
+ errno = EAGAIN;
+ }
+ err_no = errno;
+ func_ret = PREFERENCE_ERROR_FILE_WRITE;
+ goto out_unlock;
+ }
- if (value == NULL) {
- LOGE("INVALID_PARAMETER(0x%08x)",
- PREFERENCE_ERROR_INVALID_PARAMETER);
- return PREFERENCE_ERROR_INVALID_PARAMETER;
+ /* write key value */
+ switch(keynode->type)
+ {
+ case PREFERENCE_TYPE_INT:
+ ret = fwrite((void *)&(keynode->value.i), sizeof(int), 1, fp);
+ if(ret <= 0) is_write_error = 1;
+ break;
+ case PREFERENCE_TYPE_DOUBLE:
+ ret = fwrite((void *)&(keynode->value.d), sizeof(double), 1, fp);
+ if(ret <= 0) is_write_error = 1;
+ break;
+ case PREFERENCE_TYPE_BOOLEAN:
+ ret = fwrite((void *)&(keynode->value.b), sizeof(int), 1, fp);
+ if(ret <= 0) is_write_error = 1;
+ break;
+ case PREFERENCE_TYPE_STRING:
+ ret = fprintf(fp,"%s",keynode->value.s);
+ if(ret < strlen(keynode->value.s)) is_write_error = 1;
+ if (ftruncate(fileno(fp), ret) == -1) {
+ is_write_error = 1;
+ }
+ break;
+ default :
+ func_ret = PREFERENCE_ERROR_WRONG_TYPE;
+ goto out_unlock;
+ }
+ if(is_write_error)
+ {
+ if(!errno) {
+ LOGW("number of written items is 0. try again");
+ errno = EAGAIN;
+ }
+ err_no = errno;
+ func_ret = PREFERENCE_ERROR_FILE_WRITE;
+ goto out_unlock;
}
- ret = _read_data(key, type, data);
- if (ret == PREFERENCE_ERROR_NONE) {
- if (atoi(type) == PREFERENCE_TYPE_DOUBLE)
- *value = atof(data);
- else {
- LOGE("INVALID_PARAMETER(0x%08x) : param type(%d)",
- PREFERENCE_ERROR_INVALID_PARAMETER, atoi(type));
- return PREFERENCE_ERROR_INVALID_PARAMETER;
+ fflush(fp);
+
+out_unlock :
+ ret = _preference_set_unlock(fileno(fp));
+ if(ret == -1) {
+ func_ret = PREFERENCE_ERROR_FILE_LOCK;
+ err_no = errno;
+ goto out_return;
+ }
+
+out_return :
+ if (func_ret != PREFERENCE_ERROR_NONE)
+ {
+ strerror_r(err_no, err_buf, 100);
+ if (_preference_check_retry_err(keynode, func_ret, err_no, PREFERENCE_OP_SET))
+ {
+ if (retry_cnt < PREFERENCE_ERROR_RETRY_CNT)
+ {
+ WARN("_preference_set_key_filesys(%d-%s) step(%d) failed(%d / %s) retry(%d)", keynode->type, keynode->keyname, func_ret, err_no, err_buf, retry_cnt);
+ retry_cnt++;
+ usleep((retry_cnt)*PREFERENCE_ERROR_RETRY_SLEEP_UTIME);
+
+ if (fp)
+ goto retry;
+ else
+ goto retry_open;
+ }
+ else
+ {
+ ERR("_preference_set_key_filesys(%d-%s) step(%d) faild(%d / %s) over the retry count.",
+ keynode->type, keynode->keyname, func_ret, err_no, err_buf);
+ }
+ }
+ else
+ {
+ ERR("_preference_set_key_filesys(%d-%s) step(%d) failed(%d / %s)\n", keynode->type, keynode->keyname, func_ret, err_no, err_buf);
+ }
+ } else {
+ if(retry_cnt > 0) {
+ DBG("_preference_set_key_filesys ok with retry cnt(%d)", retry_cnt);
}
}
+ if (fp)
+ {
+ if(func_ret == PREFERENCE_ERROR_NONE)
+ {
+ ret = fdatasync(fileno(fp));
+ if(ret == -1) {
+ err_no = errno;
+ func_ret = PREFERENCE_ERROR_FILE_SYNC;
+ }
+ }
+ fclose(fp);
+ }
+ *io_errno = err_no;
+
+ return func_ret;
+}
+
+static int _preference_set_key(keynode_t *keynode)
+{
+ int ret = 0;
+ int io_errno = 0;
+ char err_buf[100] = { 0, };
+
+ ret = _preference_set_key_filesys(keynode, &io_errno);
+ if (ret == PREFERENCE_ERROR_NONE)
+ {
+ g_posix_errno = PREFERENCE_ERROR_NONE;
+ g_preference_errno = PREFERENCE_ERROR_NONE;
+ }
+ else
+ {
+ strerror_r(io_errno, err_buf, 100);
+ ERR("_preference_set_key(%s) step(%d) failed(%d / %s)", keynode->keyname, ret, io_errno, err_buf);
+ g_posix_errno = io_errno;
+ g_preference_errno = ret;
+ }
+
return ret;
}
-int preference_set_string(const char *key, const char *value)
+
+/*
+ * This function set the integer value of given key
+ * @param[in] key key
+ * @param[in] intval integer value to set
+ * @return 0 on success, -1 on error
+ */
+API int preference_set_int(const char *key, int intval)
{
- char type[2];
+ START_TIME_CHECK
+
+ retvm_if(key == NULL, PREFERENCE_ERROR_INVALID_PARAMETER, "Invalid argument: key is NULL");
+
+ int func_ret = PREFERENCE_ERROR_NONE;
- snprintf(type, 2, "%d", PREFERENCE_TYPE_STRING);
- if (strlen(value) > (BUF_LEN-1)) {
- LOGE("INVALID_PARAMETER(0x%08x) : param type(%d)",
- PREFERENCE_ERROR_INVALID_PARAMETER, atoi(type));
- return PREFERENCE_ERROR_INVALID_PARAMETER;
+ keynode_t* pKeyNode = _preference_keynode_new();
+ retvm_if(pKeyNode == NULL, PREFERENCE_ERROR_OUT_OF_MEMORY, "key malloc fail");
+
+ func_ret = _preference_keynode_set_keyname(pKeyNode, key);
+ if(func_ret != PREFERENCE_ERROR_NONE) {
+ _preference_keynode_free(pKeyNode);
+ ERR("set key name error");
+ return PREFERENCE_ERROR_IO_ERROR;
}
- return _write_data(key, type, value);
+ _preference_keynode_set_value_int(pKeyNode, intval);
+
+ if (_preference_set_key(pKeyNode) != PREFERENCE_ERROR_NONE) {
+ ERR("preference_set_int(%d) : key(%s/%d) error", getpid(), key, intval);
+ func_ret = PREFERENCE_ERROR_IO_ERROR;
+ } else{
+ INFO("%s(%d) success", key, intval);
+ }
+
+ _preference_keynode_free(pKeyNode);
+
+ END_TIME_CHECK
+
+ return func_ret;
}
-int preference_get_string(const char *key, char **value)
+/*
+* This function set the boolean value of given key
+* @param[in] key key
+* @param[in] boolval boolean value to set
+ (Integer value 1 is 'True', and 0 is 'False')
+* @return 0 on success, -1 on error
+*/
+API int preference_set_boolean(const char *key, bool boolval)
{
- char type[2];
- char data[BUF_LEN];
+ START_TIME_CHECK
- int ret;
+ retvm_if(key == NULL, PREFERENCE_ERROR_INVALID_PARAMETER, "Invalid argument: key is NULL");
- if (value == NULL) {
- LOGE("INVALID_PARAMETER(0x%08x)",
- PREFERENCE_ERROR_INVALID_PARAMETER);
- return PREFERENCE_ERROR_INVALID_PARAMETER;
+ int func_ret = PREFERENCE_ERROR_NONE;
+ keynode_t* pKeyNode = _preference_keynode_new();
+ retvm_if(pKeyNode == NULL, PREFERENCE_ERROR_OUT_OF_MEMORY, "key malloc fail");
+
+ func_ret = _preference_keynode_set_keyname(pKeyNode, key);
+ if(func_ret != PREFERENCE_ERROR_NONE) {
+ _preference_keynode_free(pKeyNode);
+ ERR("set key name error");
+ return PREFERENCE_ERROR_IO_ERROR;
}
+ _preference_keynode_set_value_boolean(pKeyNode, boolval);
- ret = _read_data(key, type, data);
- if (ret == PREFERENCE_ERROR_NONE) {
- if (atoi(type) == PREFERENCE_TYPE_STRING) {
- *value = strdup(data);
- if (value == NULL) {
- LOGE("OUT_OF_MEMORY(0x%08x)",
- PREFERENCE_ERROR_OUT_OF_MEMORY);
- return PREFERENCE_ERROR_OUT_OF_MEMORY;
- }
- } else {
- LOGE("INVALID_PARAMETER(0x%08x) : param type(%d)",
- PREFERENCE_ERROR_INVALID_PARAMETER, atoi(type));
- return PREFERENCE_ERROR_INVALID_PARAMETER;
- }
+ if (_preference_set_key(pKeyNode) != PREFERENCE_ERROR_NONE) {
+ ERR("preference_set_boolean(%d) : key(%s/%d) error", getpid(), key, boolval);
+ func_ret = PREFERENCE_ERROR_IO_ERROR;
+ } else {
+ INFO("%s(%d) success", key, boolval);
}
- return ret;
+ _preference_keynode_free(pKeyNode);
+
+ END_TIME_CHECK
+
+ return func_ret;
}
-int preference_set_boolean(const char *key, bool value)
+/*
+ * This function set the double value of given key
+ * @param[in] key key
+ * @param[in] dblval double value to set
+ * @return 0 on success, -1 on error
+ */
+API int preference_set_double(const char *key, double dblval)
{
- char type[2];
- char data[BUF_LEN];
- snprintf(type, 2, "%d", PREFERENCE_TYPE_BOOLEAN);
- snprintf(data, BUF_LEN, "%d", value);
- return _write_data(key, type, data);
+ START_TIME_CHECK
+
+ retvm_if(key == NULL, PREFERENCE_ERROR_INVALID_PARAMETER, "Invalid argument: key is NULL");
+
+ int func_ret = PREFERENCE_ERROR_NONE;
+ keynode_t* pKeyNode = _preference_keynode_new();
+ retvm_if(pKeyNode == NULL, PREFERENCE_ERROR_OUT_OF_MEMORY, "key malloc fail");
+
+ func_ret = _preference_keynode_set_keyname(pKeyNode, key);
+ if(func_ret != PREFERENCE_ERROR_NONE) {
+ _preference_keynode_free(pKeyNode);
+ ERR("set key name error");
+ return PREFERENCE_ERROR_IO_ERROR;
+ }
+ _preference_keynode_set_value_double(pKeyNode, dblval);
+
+ if (_preference_set_key(pKeyNode) != PREFERENCE_ERROR_NONE) {
+ ERR("preference_set_double(%d) : key(%s/%f) error", getpid(), key, dblval);
+ func_ret = PREFERENCE_ERROR_IO_ERROR;
+ } else {
+ INFO("%s(%f) success", key, dblval);
+ }
+
+ _preference_keynode_free(pKeyNode);
+
+ END_TIME_CHECK
+
+ return func_ret;
}
-int preference_get_boolean(const char *key, bool *value)
+/*
+ * This function set the string value of given key
+ * @param[in] key key
+ * @param[in] strval string value to set
+ * @return 0 on success, -1 on error
+ */
+API int preference_set_string(const char *key, const char *strval)
{
- char type[2];
- char data[BUF_LEN];
+ START_TIME_CHECK
- int ret;
+ retvm_if(key == NULL, PREFERENCE_ERROR_INVALID_PARAMETER, "Invalid argument: key is NULL");
+ retvm_if(strval == NULL, PREFERENCE_ERROR_INVALID_PARAMETER, "Invalid argument: value is NULL");
- if (value == NULL) {
- LOGE("INVALID_PARAMETER(0x%08x)",
- PREFERENCE_ERROR_INVALID_PARAMETER);
- return PREFERENCE_ERROR_INVALID_PARAMETER;
+ int func_ret = PREFERENCE_ERROR_NONE;
+ keynode_t* pKeyNode = _preference_keynode_new();
+ retvm_if(pKeyNode == NULL, PREFERENCE_ERROR_OUT_OF_MEMORY, "key malloc fail");
+
+ func_ret = _preference_keynode_set_keyname(pKeyNode, key);
+ if(func_ret != PREFERENCE_ERROR_NONE) {
+ _preference_keynode_free(pKeyNode);
+ ERR("set key name error");
+ return PREFERENCE_ERROR_IO_ERROR;
}
+ _preference_keynode_set_value_string(pKeyNode, strval);
- ret = _read_data(key, type, data);
- if (ret == PREFERENCE_ERROR_NONE) {
- if (atoi(type) == PREFERENCE_TYPE_BOOLEAN)
- *value = (bool)atoi(data);
- else {
- LOGE("INVALID_PARAMETER(0x%08x) : param type(%d)",
- PREFERENCE_ERROR_INVALID_PARAMETER, atoi(type));
- return PREFERENCE_ERROR_INVALID_PARAMETER;
- }
+ if (_preference_set_key(pKeyNode) != PREFERENCE_ERROR_NONE) {
+ ERR("preference_set_string(%d) : key(%s/%s) error", getpid(), key, strval);
+ func_ret = PREFERENCE_ERROR_IO_ERROR;
+ } else {
+ INFO("%s(%s) success", key, strval);
}
- return ret;
-}
+ _preference_keynode_free(pKeyNode);
+ END_TIME_CHECK
-/* TODO: below operation is too heavy, let's find the light way to check. */
-int preference_is_existing(const char *key, bool *exist)
+ return func_ret;
+}
+
+static int _preference_get_key_filesys(keynode_t *keynode, int* io_errno)
{
- int ret;
- char *buf;
- char **result;
- int rows;
- int columns;
- char *errmsg;
+ char path[PREFERENCE_KEY_PATH_LEN] = {0,};
+ int ret = -1;
+ int func_ret = PREFERENCE_ERROR_NONE;
+ char err_buf[100] = { 0, };
+ int err_no = 0;
+ int type = 0;
+ FILE *fp = NULL;
+ int retry_cnt = 0;
+ int read_size = 0;
+
+retry_open :
+ errno = 0;
+ func_ret = PREFERENCE_ERROR_NONE;
+
+ ret = _preference_get_key_path(keynode->keyname, path);
+ retv_if(ret != PREFERENCE_ERROR_NONE, ret);
+
+ if( (fp = fopen(path, "r")) == NULL ) {
+ func_ret = PREFERENCE_ERROR_FILE_OPEN;
+ err_no = errno;
+ goto out_return;
+ }
- if (key == NULL || key[0] == '\0' || exist == NULL) {
- LOGE("INVALID_PARAMETER(0x%08x)",
- PREFERENCE_ERROR_INVALID_PARAMETER);
- return PREFERENCE_ERROR_INVALID_PARAMETER;
+retry :
+ err_no = 0;
+ func_ret = PREFERENCE_ERROR_NONE;
+
+ ret = _preference_set_read_lock(fileno(fp));
+ if(ret == -1) {
+ func_ret = PREFERENCE_ERROR_FILE_LOCK;
+ err_no = errno;
+ goto out_return;
}
- if (pref_db == NULL) {
- if (_initialize() != PREFERENCE_ERROR_NONE) {
- LOGE("IO_ERROR(0x%08x) : fail to initialize db",
- PREFERENCE_ERROR_IO_ERROR);
- return PREFERENCE_ERROR_IO_ERROR;
+
+ /* read data type */
+ read_size = fread((void*)&type, sizeof(int), 1, fp);
+ if((read_size <= 0) || (read_size > sizeof(int))) {
+ if(!ferror(fp)) {
+ LOGW("number of read items for type is 0 with false ferror. err : %d", errno);
+ errno = ENODATA;
}
+ err_no = errno;
+ func_ret = PREFERENCE_ERROR_FILE_FREAD;
+ goto out_unlock;
}
- /* check data is exist */
- buf = sqlite3_mprintf("SELECT %s FROM %s WHERE %s=%Q;",
- PREF_F_KEY_NAME, PREF_TBL_NAME, PREF_F_KEY_NAME, key);
+ /* read data value */
+ switch(type)
+ {
+ case PREFERENCE_TYPE_INT:
+ {
+ int value_int = 0;
+ int read_size = 0;
+ read_size = fread((void*)&value_int, sizeof(int), 1, fp);
+ if((read_size <= 0) || (read_size > sizeof(int))) {
+ if(!ferror(fp)) {
+ LOGW("number of read items for value is wrong. err : %d", errno);
+ }
+ err_no = errno;
+ func_ret = PREFERENCE_ERROR_FILE_FREAD;
+ goto out_unlock;
+ } else {
+ _preference_keynode_set_value_int(keynode, value_int);
+ }
- if (buf == NULL) {
- LOGE("IO_ERROR(0x%08x) : fail to create query string",
- PREFERENCE_ERROR_IO_ERROR);
- return PREFERENCE_ERROR_IO_ERROR;
+ break;
+ }
+ case PREFERENCE_TYPE_DOUBLE:
+ {
+ double value_dbl = 0;
+ int read_size = 0;
+ read_size = fread((void*)&value_dbl, sizeof(double), 1, fp);
+ if((read_size <= 0) || (read_size > sizeof(double))) {
+ if(!ferror(fp)) {
+ LOGW("number of read items for value is wrong. err : %d", errno);
+ }
+ err_no = errno;
+ func_ret = PREFERENCE_ERROR_FILE_FREAD;
+ goto out_unlock;
+ } else {
+ _preference_keynode_set_value_double(keynode, value_dbl);
+ }
+
+ break;
+ }
+ case PREFERENCE_TYPE_BOOLEAN:
+ {
+ int value_int = 0;
+ int read_size = 0;
+ read_size = fread((void*)&value_int, sizeof(int), 1, fp);
+ if((read_size <= 0) || (read_size > sizeof(int))) {
+ if(!ferror(fp)) {
+ LOGW("number of read items for value is wrong. err : %d", errno);
+ }
+ err_no = errno;
+ func_ret = PREFERENCE_ERROR_FILE_FREAD;
+ goto out_unlock;
+ } else {
+ _preference_keynode_set_value_boolean(keynode, value_int);
+ }
+
+ break;
+ }
+ case PREFERENCE_TYPE_STRING:
+ {
+ char file_buf[BUF_LEN] = {0,};
+ char *value = NULL;
+ int value_size = 0;
+
+ while(fgets(file_buf, sizeof(file_buf), fp))
+ {
+ if(value) {
+ value_size = value_size + strlen(file_buf);
+ value = (char *) realloc(value, value_size);
+ if(value == NULL) {
+ func_ret = PREFERENCE_ERROR_OUT_OF_MEMORY;
+ break;
+ }
+ strncat(value, file_buf, strlen(file_buf));
+ } else {
+ value_size = strlen(file_buf) + 1;
+ value = (char *)malloc(value_size);
+ if(value == NULL) {
+ func_ret = PREFERENCE_ERROR_OUT_OF_MEMORY;
+ break;
+ }
+ memset(value, 0x00, value_size);
+ strncpy(value, file_buf, strlen(file_buf));
+ }
+ }
+
+ if(ferror(fp)) {
+ err_no = errno;
+ func_ret = PREFERENCE_ERROR_FILE_FGETS;
+ } else {
+ if(value) {
+ _preference_keynode_set_value_string(keynode, value);
+ } else {
+ _preference_keynode_set_value_string(keynode, "");
+ }
+ }
+ if(value)
+ free(value);
+
+ break;
+ }
+ default :
+ func_ret = PREFERENCE_ERROR_WRONG_TYPE;
}
- ret = sqlite3_get_table(pref_db, buf, &result, &rows, &columns, &errmsg);
- sqlite3_free(buf);
- if (ret != SQLITE_OK) {
- LOGE("IO_ERROR(0x%08x) : fail to read data(%s)",
- PREFERENCE_ERROR_IO_ERROR, errmsg);
- sqlite3_free(errmsg);
- return PREFERENCE_ERROR_IO_ERROR;
+out_unlock :
+ ret = _preference_set_unlock(fileno(fp));
+ if(ret == -1) {
+ func_ret = PREFERENCE_ERROR_FILE_LOCK;
+ err_no = errno;
+ goto out_return;
}
- if (rows > 0)
- *exist = true;
- else
- *exist = false;
- sqlite3_free_table(result);
- return PREFERENCE_ERROR_NONE;
-}
+out_return :
+ if (func_ret != PREFERENCE_ERROR_NONE)
+ {
+ strerror_r(err_no, err_buf, 100);
+
+ if (_preference_check_retry_err(keynode, func_ret, err_no, PREFERENCE_OP_GET))
+ {
+ if (retry_cnt < PREFERENCE_ERROR_RETRY_CNT)
+ {
+ WARN("_preference_get_key_filesys(%s) step(%d) failed(%d / %s) retry(%d)",
+ keynode->keyname, func_ret, err_no, err_buf, retry_cnt);
+ retry_cnt++;
+ usleep((retry_cnt)*PREFERENCE_ERROR_RETRY_SLEEP_UTIME);
+
+ if (fp)
+ goto retry;
+ else
+ goto retry_open;
+ }
+ else
+ {
+ ERR("_preference_get_key_filesys(%s) step(%d) faild(%d / %s) over the retry count.",
+ keynode->keyname, func_ret, err_no, err_buf);
+ }
+ }
+ else
+ {
+ ERR("_preference_get_key_filesys(%s) step(%d) failed(%d / %s) retry(%d) ",
+ keynode->keyname, func_ret, err_no, err_buf, retry_cnt);
+ }
+ } else {
+ if(retry_cnt > 0) {
+ DBG("preference get filesys ok with retry cnt(%d)", retry_cnt);
+ }
+ }
-static pref_changed_cb_node_t *_find_node(const char *key)
-{
- pref_changed_cb_node_t *tmp_node;
+ if (fp)
+ fclose(fp);
- if (key == NULL || key[0] == '\0')
- return NULL;
+ *io_errno = err_no;
- tmp_node = head;
+ return func_ret;
+}
- while (tmp_node) {
- if (strcmp(tmp_node->key, key) == 0)
- break;
- tmp_node = tmp_node->next;
+int _preference_get_key(keynode_t *keynode)
+{
+ int ret = 0;
+ int io_errno = 0;
+ char err_buf[100] = {0,};
+
+ ret = _preference_get_key_filesys(keynode, &io_errno);
+ if(ret == PREFERENCE_ERROR_NONE) {
+ g_posix_errno = PREFERENCE_ERROR_NONE;
+ g_preference_errno = PREFERENCE_ERROR_NONE;
+ }
+ else
+ {
+ if (io_errno == ENOENT)
+ ret = PREFERENCE_ERROR_NO_KEY;
+ else
+ ret = PREFERENCE_ERROR_IO_ERROR;
+
+ strerror_r(io_errno, err_buf, 100);
+ ERR("_preference_get_key(%s) step(%d) failed(%d / %s)\n", keynode->keyname, ret, io_errno, err_buf);
+ g_posix_errno = io_errno;
+ g_preference_errno = ret;
}
- return tmp_node;
+ return ret;
}
-static int _add_node(const char *key, preference_changed_cb cb, void *user_data)
+/*
+ * This function get the integer value of given key
+ * @param[in] key key
+ * @param[out] intval output buffer
+ * @return 0 on success, -1 on error
+ */
+API int preference_get_int(const char *key, int *intval)
{
- pref_changed_cb_node_t *tmp_node;
+ START_TIME_CHECK
- if (key == NULL || key[0] == '\0' || cb == NULL) {
- LOGE("INVALID_PARAMETER(0x%08x)",
- PREFERENCE_ERROR_INVALID_PARAMETER);
- return PREFERENCE_ERROR_INVALID_PARAMETER;
- }
+ retvm_if(key == NULL, PREFERENCE_ERROR_INVALID_PARAMETER, "Invalid argument: key is null");
+ retvm_if(intval == NULL, PREFERENCE_ERROR_INVALID_PARAMETER, "Invalid argument: output buffer is null");
+
+ int func_ret = PREFERENCE_ERROR_IO_ERROR;
+ keynode_t* pKeyNode = _preference_keynode_new();
+ retvm_if(pKeyNode == NULL, PREFERENCE_ERROR_OUT_OF_MEMORY, "key malloc fail");
- tmp_node = _find_node(key);
+ _preference_keynode_set_keyname(pKeyNode, key);
- if (tmp_node != NULL) {
- tmp_node->cb = cb;
- tmp_node->user_data = user_data;
+ func_ret = _preference_get_key(pKeyNode);
+
+ if (func_ret != PREFERENCE_ERROR_NONE) {
+ ERR("preference_get_int(%d) : key(%s) error", getpid(), key);
} else {
- tmp_node =
- (pref_changed_cb_node_t *)malloc(sizeof(pref_changed_cb_node_t));
- if (tmp_node == NULL) {
- LOGE("OUT_OF_MEMORY(0x%08x)",
- PREFERENCE_ERROR_OUT_OF_MEMORY);
- return PREFERENCE_ERROR_OUT_OF_MEMORY;
+ *intval = pKeyNode->value.i;
+ if(pKeyNode->type == PREFERENCE_TYPE_INT) {
+ INFO("%s(%d) success", key, *intval);
+ func_ret = PREFERENCE_ERROR_NONE;
+ } else {
+ ERR("The type(%d) of keynode(%s) is not INT", pKeyNode->type, pKeyNode->keyname);
+ func_ret = PREFERENCE_ERROR_INVALID_PARAMETER;
}
+ }
- tmp_node->key = strdup(key);
- if (tmp_node->key == NULL) {
- free(tmp_node);
- LOGE("OUT_OF_MEMORY(0x%08x)", PREFERENCE_ERROR_OUT_OF_MEMORY);
- return PREFERENCE_ERROR_OUT_OF_MEMORY;
- }
+ _preference_keynode_free(pKeyNode);
- if (head != NULL)
- head->prev = tmp_node;
- tmp_node->cb = cb;
- tmp_node->user_data = user_data;
- tmp_node->prev = NULL;
- tmp_node->next = head;
- head = tmp_node;
- }
+ END_TIME_CHECK
- return PREFERENCE_ERROR_NONE;
+ return func_ret;
}
-static int _remove_node(const char *key)
+/*
+ * This function get the boolean value of given key
+ * @param[in] key key
+ * @param[out] boolval output buffer
+ * @return 0 on success, -1 on error
+ */
+API int preference_get_boolean(const char *key, bool *boolval)
{
- pref_changed_cb_node_t *tmp_node;
+ START_TIME_CHECK
- if (key == NULL || key[0] == '\0') {
- LOGE("INVALID_PARAMETER(0x%08x)",
- PREFERENCE_ERROR_INVALID_PARAMETER);
- return PREFERENCE_ERROR_INVALID_PARAMETER;
- }
+ retvm_if(key == NULL, PREFERENCE_ERROR_INVALID_PARAMETER, "Invalid argument: key is null");
+ retvm_if(boolval == NULL, PREFERENCE_ERROR_INVALID_PARAMETER, "Invalid argument: output buffer is null");
- tmp_node = _find_node(key);
+ int func_ret = PREFERENCE_ERROR_IO_ERROR;
+ keynode_t* pKeyNode = _preference_keynode_new();
+ retvm_if(pKeyNode == NULL, PREFERENCE_ERROR_OUT_OF_MEMORY, "key malloc fail");
- if (tmp_node == NULL)
- return PREFERENCE_ERROR_NONE;
+ _preference_keynode_set_keyname(pKeyNode, key);
- if (tmp_node->prev != NULL)
- tmp_node->prev->next = tmp_node->next;
- else
- head = tmp_node->next;
+ func_ret = _preference_get_key(pKeyNode);
- if (tmp_node->next != NULL)
- tmp_node->next->prev = tmp_node->prev;
+ if (func_ret != PREFERENCE_ERROR_NONE) {
+ ERR("preference_get_boolean(%d) : %s error", getpid(), key);
+ } else {
+ *boolval = !!(pKeyNode->value.b);
+ if(pKeyNode->type == PREFERENCE_TYPE_BOOLEAN) {
+ INFO("%s(%d) success", key, *boolval);
+ func_ret = PREFERENCE_ERROR_NONE;
+ } else {
+ ERR("The type(%d) of keynode(%s) is not BOOL", pKeyNode->type, pKeyNode->keyname);
+ func_ret = PREFERENCE_ERROR_INVALID_PARAMETER;
+ }
+ }
- if (tmp_node->key)
- free(tmp_node->key);
+ _preference_keynode_free(pKeyNode);
- free(tmp_node);
+ END_TIME_CHECK
- return PREFERENCE_ERROR_NONE;
+ return func_ret;
}
-
-static void _remove_all_node(void)
+/*
+ * This function get the double value of given key
+ * @param[in] key key
+ * @param[out] dblval output buffer
+ * @return 0 on success, -1 on error
+ */
+API int preference_get_double(const char *key, double *dblval)
{
- pref_changed_cb_node_t *tmp_node;
+ START_TIME_CHECK
- while (head) {
- tmp_node = head;
- head = tmp_node->next;
+ retvm_if(key == NULL, PREFERENCE_ERROR_INVALID_PARAMETER, "Invalid argument: key is null");
+ retvm_if(dblval == NULL, PREFERENCE_ERROR_INVALID_PARAMETER, "Invalid argument: output buffer is null");
- if (tmp_node->key)
- free(tmp_node->key);
+ int func_ret = PREFERENCE_ERROR_IO_ERROR;
+ keynode_t* pKeyNode = _preference_keynode_new();
+ retvm_if(pKeyNode == NULL, PREFERENCE_ERROR_OUT_OF_MEMORY, "key malloc fail");
- free(tmp_node);
+ _preference_keynode_set_keyname(pKeyNode, key);
+
+ func_ret = _preference_get_key(pKeyNode);
+
+ if (func_ret != PREFERENCE_ERROR_NONE) {
+ ERR("preference_get_double(%d) : %s error", getpid(), key);
+ } else {
+ *dblval = pKeyNode->value.d;
+
+ if(pKeyNode->type == PREFERENCE_TYPE_DOUBLE) {
+ INFO("%s(%f) success", key, *dblval);
+ func_ret = PREFERENCE_ERROR_NONE;
+ } else {
+ ERR("The type(%d) of keynode(%s) is not DBL", pKeyNode->type, pKeyNode->keyname);
+ func_ret = PREFERENCE_ERROR_INVALID_PARAMETER;
+ }
}
-}
+ _preference_keynode_free(pKeyNode);
+
+ END_TIME_CHECK
-static void _update_cb(void *data, int action, char const *db_name,
- char const *table_name, sqlite_int64 rowid)
+ return func_ret;
+}
+
+/*
+ * This function get the string value of given key
+ * @param[in] key key
+ * @return pointer of key value on success, NULL on error
+ */
+API int preference_get_string(const char *key, char **value)
{
- int ret;
- char *buf;
- char **result;
- int rows;
- int columns;
- char *errmsg;
- pref_changed_cb_node_t *tmp_node;
+ START_TIME_CHECK
- if (action != SQLITE_UPDATE)
- return;
+ retvm_if(key == NULL, PREFERENCE_ERROR_INVALID_PARAMETER, "Invalid argument: key is null");
+ retvm_if(value == NULL, PREFERENCE_ERROR_INVALID_PARAMETER, "Invalid argument: output buffer is null");
- if (strcmp(table_name, PREF_TBL_NAME) != 0) {
- SECURE_LOGE("given table name (%s) is not same", table_name);
- return;
- }
+ int func_ret = PREFERENCE_ERROR_IO_ERROR;
+ keynode_t* pKeyNode = _preference_keynode_new();
+ retvm_if(pKeyNode == NULL, PREFERENCE_ERROR_OUT_OF_MEMORY, "key malloc fail");
- buf = sqlite3_mprintf("SELECT %s FROM %s WHERE rowid='%lld';",
- PREF_F_KEY_NAME, PREF_TBL_NAME, rowid);
- if (buf == NULL)
- return;
+ _preference_keynode_set_keyname(pKeyNode, key);
- ret = sqlite3_get_table(pref_db, buf, &result, &rows, &columns, &errmsg);
- sqlite3_free(buf);
- if (ret != SQLITE_OK) {
- LOGI("fail to read data(%s)", errmsg);
- sqlite3_free(errmsg);
- return;
- }
+ char *tempstr = NULL;
+
+ func_ret = _preference_get_key(pKeyNode);
+
+ if (func_ret != PREFERENCE_ERROR_NONE) {
+ ERR("preference_get_string(%d) : %s error", getpid(), key);
+ } else {
+ if(pKeyNode->type == PREFERENCE_TYPE_STRING)
+ tempstr = pKeyNode->value.s;
+ else {
+ ERR("The type(%d) of keynode(%s) is not STR", pKeyNode->type, pKeyNode->keyname);
+ func_ret = PREFERENCE_ERROR_INVALID_PARAMETER;
+ }
- if (rows == 0) {
- sqlite3_free_table(result);
- return;
+ if(tempstr) {
+ *value = strdup(tempstr);
+ INFO("%s(%s) success", key, value);
+ }
}
- tmp_node = _find_node(result[1]);
+ _preference_keynode_free(pKeyNode);
- if (tmp_node != NULL && tmp_node->cb != NULL)
- tmp_node->cb(result[1], tmp_node->user_data);
+ END_TIME_CHECK
- sqlite3_free_table(result);
+ return func_ret;
}
-
-int preference_remove(const char *key)
+/*
+ * This function unset given key
+ * @param[in] key key
+ * @return 0 on success, -1 on error
+ */
+API int preference_remove(const char *key)
{
- int ret;
- char buf[BUF_LEN];
- bool exist;
- sqlite3_stmt *stmt;
+ START_TIME_CHECK
- ret = preference_is_existing(key, &exist);
- if (ret != PREFERENCE_ERROR_NONE)
- return ret;
+ char path[PREFERENCE_KEY_PATH_LEN] = {0,};
+ int ret = -1;
+ int err_retry = PREFERENCE_ERROR_RETRY_CNT;
+ int func_ret = PREFERENCE_ERROR_NONE;
- if (!exist)
- return PREFERENCE_ERROR_NO_KEY;
+ retvm_if(key == NULL, PREFERENCE_ERROR_INVALID_PARAMETER, "Invalid argument: key is null");
+
+ ret = _preference_get_key_path(key, path);
+ retvm_if(ret != PREFERENCE_ERROR_NONE, PREFERENCE_ERROR_INVALID_PARAMETER, "Invalid argument: key is not valid");
- snprintf(buf, sizeof(buf), "DELETE FROM %s WHERE %s = ?",
- PREF_TBL_NAME, PREF_F_KEY_NAME);
+ retvm_if(access(path, F_OK) == -1, PREFERENCE_ERROR_NO_KEY, "Error : key(%s) is not exist", key);
- ret = sqlite3_prepare(pref_db, buf, -1, &stmt, NULL);
- if (ret != SQLITE_OK) {
- LOGE("IO_ERROR(0x%08x) : fail to prepare query (%d/%s)",
- PREFERENCE_ERROR_IO_ERROR,
- sqlite3_extended_errcode(pref_db),
- sqlite3_errmsg(pref_db));
+ do {
+ ret = remove(path);
+ if(ret == -1) {
+ ERR("preference_remove() failed. ret=%d(%s), key(%s)", errno, strerror(errno), key);
+ func_ret = PREFERENCE_ERROR_IO_ERROR;
+ } else {
+ func_ret = PREFERENCE_ERROR_NONE;
+ break;
+ }
+ } while(err_retry--);
+
+ END_TIME_CHECK
+
+ return func_ret;
+}
+
+API int preference_remove_all(void)
+{
+ START_TIME_CHECK
+
+ int ret = -1;
+ int err_retry = PREFERENCE_ERROR_RETRY_CNT;
+ int func_ret = PREFERENCE_ERROR_NONE;
+ DIR *dir;
+ struct dirent *dent = NULL;
+ char *pref_dir_path = NULL;
+
+ pref_dir_path = _preference_get_pref_dir_path();
+ if (!pref_dir_path)
+ {
+ LOGE("_preference_get_pref_dir_path() failed.");
return PREFERENCE_ERROR_IO_ERROR;
}
- ret = sqlite3_bind_text(stmt, 1, key, -1, SQLITE_STATIC);
- if (ret != SQLITE_OK) {
- LOGE("IO_ERROR(0x%08x) : fail to bind(1) query (%d/%s)",
- PREFERENCE_ERROR_IO_ERROR,
- sqlite3_extended_errcode(pref_db),
- sqlite3_errmsg(pref_db));
- sqlite3_finalize(stmt);
+ dir = opendir(pref_dir_path);
+ if (dir == NULL)
+ {
+ LOGE("opendir() failed. pref_path: %s, error: %d(%s)", pref_dir_path, errno, strerror(errno));
return PREFERENCE_ERROR_IO_ERROR;
}
- ret = sqlite3_step(stmt);
- if (ret != SQLITE_DONE) {
- LOGE("IO_ERROR(0x%08x): fail to delete data(%d/%s)",
- PREFERENCE_ERROR_IO_ERROR,
- sqlite3_extended_errcode(pref_db),
- sqlite3_errmsg(pref_db));
- sqlite3_finalize(stmt);
- return PREFERENCE_ERROR_IO_ERROR;
+ while ((dent = readdir(dir)))
+ {
+ const char *entry = dent->d_name;
+ char keyname[PREFERENCE_KEY_PATH_LEN] = {0,};
+ char path[PREFERENCE_KEY_PATH_LEN] = {0,};
+
+ if (entry[0] == '.')
+ {
+ continue;
+ }
+
+ ret = _preference_get_key_name(entry, keyname);
+ if (ret != PREFERENCE_ERROR_NONE)
+ {
+ ERR("_preference_get_key_name() failed(%d)", ret);
+ closedir(dir);
+ return ret;
+ }
+
+ ret = preference_unset_changed_cb(keyname);
+ if (ret != PREFERENCE_ERROR_NONE)
+ {
+ ERR("preference_unset_changed_cb() failed(%d)", ret);
+ closedir(dir);
+ return PREFERENCE_ERROR_IO_ERROR;
+ }
+
+ ret = _preference_get_key_path(keyname, path);
+ if (ret != PREFERENCE_ERROR_NONE)
+ {
+ ERR("_preference_get_key_path() failed(%d)", ret);
+ closedir(dir);
+ return ret;
+ }
+
+ // delete
+ do {
+ ret = remove(path);
+ if(ret == -1) {
+ ERR("preference_remove_all error: %d(%s)", errno, strerror(errno));
+ func_ret = PREFERENCE_ERROR_IO_ERROR;
+ } else {
+ func_ret = PREFERENCE_ERROR_NONE;
+ break;
+ }
+ } while(err_retry--);
}
- sqlite3_finalize(stmt);
+ closedir(dir);
- _remove_node(key);
+ END_TIME_CHECK
- return PREFERENCE_ERROR_NONE;
+ return func_ret;
}
-
-int preference_remove_all(void)
+int preference_is_existing(const char *key, bool *exist)
{
- int ret;
- char *buf;
- char *errmsg;
+ START_TIME_CHECK
- if (pref_db == NULL) {
- if (_initialize() != PREFERENCE_ERROR_NONE) {
- LOGE("IO_ERROR(0x%08x) : fail to initialize db",
- PREFERENCE_ERROR_IO_ERROR);
- return PREFERENCE_ERROR_IO_ERROR;
- }
- }
+ char path[PREFERENCE_KEY_PATH_LEN] = {0,};
+ int ret = -1;
+ int func_ret = PREFERENCE_ERROR_NONE;
- /* insert data or update data if data already exist */
- buf = sqlite3_mprintf("DELETE FROM %s;", PREF_TBL_NAME);
- if (buf == NULL) {
- LOGE("IO_ERROR(0x%08x) : fail to create query string",
- PREFERENCE_ERROR_IO_ERROR);
- return PREFERENCE_ERROR_IO_ERROR;
- }
+ retvm_if(key == NULL, PREFERENCE_ERROR_INVALID_PARAMETER, "Invalid argument: key is null");
+ retvm_if(exist == NULL, PREFERENCE_ERROR_INVALID_PARAMETER, "Invalid argument: key is null");
- ret = sqlite3_exec(pref_db, buf, NULL, NULL, &errmsg);
- sqlite3_free(buf);
- if (ret != SQLITE_OK) {
- LOGE("IO_ERROR(0x%08x) : fail to delete data (%s)",
- PREFERENCE_ERROR_IO_ERROR, errmsg);
- sqlite3_free(errmsg);
- return PREFERENCE_ERROR_IO_ERROR;
+ ret = _preference_get_key_path(key, path);
+ retv_if(ret != PREFERENCE_ERROR_NONE, ret);
+
+ ret = access(path, F_OK);
+ if (ret == -1)
+ {
+ ERR("Error : key(%s) is not exist", key);
+ *exist = 0;
+ }
+ else
+ {
+ *exist = 1;
}
- _remove_all_node();
+ END_TIME_CHECK
- return PREFERENCE_ERROR_NONE;
+ return func_ret;
}
-int preference_set_changed_cb(const char *key,
- preference_changed_cb callback, void *user_data)
+API int preference_set_changed_cb(const char *key, preference_changed_cb callback, void *user_data)
{
- int ret;
+ START_TIME_CHECK
+
+ int ret = -1;
bool exist;
+ retvm_if(key == NULL, PREFERENCE_ERROR_INVALID_PARAMETER, "Invalid argument: key is null");
+ retvm_if(callback == NULL, PREFERENCE_ERROR_INVALID_PARAMETER, "Invalid argument: cb(%p)", callback);
+
ret = preference_is_existing(key, &exist);
if (ret != PREFERENCE_ERROR_NONE)
+ {
return ret;
+ }
- if (!exist) {
- LOGE("NO_KEY(0x%08x) : fail to find given key(%s)",
- PREFERENCE_ERROR_NO_KEY, key);
+ if (!exist)
+ {
+ LOGE("NO_KEY(0x%08x) : fail to find given key(%s)", PREFERENCE_ERROR_NO_KEY, key);
return PREFERENCE_ERROR_NO_KEY;
}
- if (!is_update_hook_registered) {
- sqlite3_update_hook(pref_db, _update_cb, NULL);
- is_update_hook_registered = true;
+ if (_preference_kdb_add_notify(key, callback, user_data)) {
+ if(errno != 0 && errno != ENOENT) {
+ ERR("preference_notify_key_changed : key(%s) add notify fail", key);
+ return PREFERENCE_ERROR_IO_ERROR;
+ }
}
+ INFO("%s noti is added", key);
+
+ END_TIME_CHECK
- return _add_node(key, callback, user_data);
+ return PREFERENCE_ERROR_NONE;
}
-int preference_unset_changed_cb(const char *key)
+API int preference_unset_changed_cb(const char *key)
{
- int ret;
+ START_TIME_CHECK
+
+ int ret = -1;
bool exist;
+ retvm_if(key == NULL, PREFERENCE_ERROR_INVALID_PARAMETER, "Invalid argument: key is null");
+
ret = preference_is_existing(key, &exist);
if (ret != PREFERENCE_ERROR_NONE)
+ {
return ret;
+ }
- if (!exist) {
- LOGE("NO_KEY(0x%08x) : fail to find given key(%s)",
- PREFERENCE_ERROR_NO_KEY, key);
+ if (!exist)
+ {
+ LOGE("NO_KEY(0x%08x) : fail to find given key(%s)", PREFERENCE_ERROR_NO_KEY, key);
return PREFERENCE_ERROR_NO_KEY;
}
- return _remove_node(key);
+ if (_preference_kdb_del_notify(key)) {
+ if (errno != 0 && errno != ENOENT) {
+ ERR("preference_unset_changed_cb() failed: key(%s) error(%d/%s)", key, errno, strerror(errno));
+ return PREFERENCE_ERROR_IO_ERROR;
+ }
+ }
+ INFO("%s noti removed", key);
+
+ END_TIME_CHECK
+
+ return PREFERENCE_ERROR_NONE;
}
-int preference_foreach_item(preference_item_cb callback, void *user_data)
+
+API int preference_foreach_item(preference_item_cb callback, void *user_data)
{
- int ret;
- char *buf;
- char **result;
- int rows;
- int columns;
- char *errmsg;
- int i;
+ START_TIME_CHECK
- if (callback == NULL) {
- LOGE("INVALID_PARAMETER(0x%08x)",
- PREFERENCE_ERROR_INVALID_PARAMETER);
- return PREFERENCE_ERROR_INVALID_PARAMETER;
- }
+ retvm_if(callback == NULL, PREFERENCE_ERROR_INVALID_PARAMETER, "Invalid argument: cb(%p)", callback);
- if (pref_db == NULL) {
- if (_initialize() != PREFERENCE_ERROR_NONE) {
- LOGE("IO_ERROR(0x%08x) : fail to initialize db",
- PREFERENCE_ERROR_IO_ERROR);
- return PREFERENCE_ERROR_IO_ERROR;
- }
- }
+ int ret = 0;
+ DIR *dir;
+ struct dirent *dent = NULL;
+ char *pref_dir_path = NULL;
- buf = sqlite3_mprintf("SELECT %s FROM %s;",
- PREF_F_KEY_NAME, PREF_TBL_NAME);
- if (buf == NULL) {
- LOGE("IO_ERROR(0x%08x) : fail to create query string",
- PREFERENCE_ERROR_IO_ERROR);
+ pref_dir_path = _preference_get_pref_dir_path();
+ if (!pref_dir_path)
+ {
+ LOGE("_preference_get_pref_dir_path() failed.");
return PREFERENCE_ERROR_IO_ERROR;
}
- ret = sqlite3_get_table(pref_db, buf, &result, &rows, &columns, &errmsg);
- sqlite3_free(buf);
- if (ret != SQLITE_OK) {
- LOGE("IO_ERROR(0x%08x) : fail to read data (%s)",
- PREFERENCE_ERROR_IO_ERROR, errmsg);
- sqlite3_free(errmsg);
+ dir = opendir(pref_dir_path);
+ if (dir == NULL)
+ {
+ LOGE("opendir() failed. path: %s, error: %d(%s)", pref_dir_path, errno, strerror(errno));
return PREFERENCE_ERROR_IO_ERROR;
}
- for (i = 1; i <= rows; i++) {
- if (callback(result[i], user_data) != true)
- break;
+ while((dent = readdir(dir)))
+ {
+ const char *entry = dent->d_name;
+ char keyname[PREFERENCE_KEY_PATH_LEN] = {0,};
+
+ if (entry[0] == '.')
+ {
+ continue;
+ }
+
+ ret = _preference_get_key_name(entry, keyname);
+ retv_if(ret != PREFERENCE_ERROR_NONE, ret);
+
+ callback(keyname, user_data);
}
- sqlite3_free_table(result);
+ closedir(dir);
+ END_TIME_CHECK
return PREFERENCE_ERROR_NONE;
}
diff --git a/preference/preference_db.c b/preference/preference_db.c
new file mode 100755
index 0000000..31e9e9f
--- /dev/null
+++ b/preference/preference_db.c
@@ -0,0 +1,915 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sqlite3.h>
+
+#include <app_internal.h>
+
+#include <app_preference.h>
+#include <app_preference_internal.h>
+
+#include <dlog.h>
+
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif
+
+#define LOG_TAG "CAPI_APPFW_APPLICATION_PREFERENCE"
+#define DBG_MODE (1)
+
+static sqlite3 *pref_db = NULL;
+static bool is_update_hook_registered = false;
+static pref_changed_cb_node_t *head = NULL;
+
+static void _finish(void *data)
+{
+ if (pref_db != NULL)
+ {
+ sqlite3_close(pref_db);
+ pref_db = NULL;
+ }
+}
+
+static int _busy_handler(void *pData, int count)
+{
+ if(5 - count > 0) {
+ LOGD("Busy Handler Called! : PID(%d) / CNT(%d)\n", getpid(), count+1);
+ usleep((count+1)*100000);
+ return 1;
+ } else {
+ LOGD("Busy Handler will be returned SQLITE_BUSY error : PID(%d) \n", getpid());
+ return 0;
+ }
+}
+
+static int _initialize(void)
+{
+ char *data_path = NULL;
+ char db_path[TIZEN_PATH_MAX] = {0, };
+ int ret;
+ char *errmsg;
+
+ if ((data_path = app_get_data_path()) == NULL)
+ {
+ LOGE("IO_ERROR(0x%08x) : fail to get data directory", PREFERENCE_ERROR_IO_ERROR);
+ return PREFERENCE_ERROR_IO_ERROR;
+ }
+ snprintf(db_path, sizeof(db_path), "%s/%s", data_path, PREF_DB_NAME);
+ free(data_path);
+
+ ret = sqlite3_open(db_path, &pref_db);
+ if (ret != SQLITE_OK)
+ {
+ LOGE("IO_ERROR(0x%08x) : fail to open db(%s)", PREFERENCE_ERROR_IO_ERROR, sqlite3_errmsg(pref_db));
+ pref_db = NULL;
+ return PREFERENCE_ERROR_IO_ERROR;
+ }
+
+ ret = sqlite3_busy_handler(pref_db, _busy_handler, NULL);
+ if (ret != SQLITE_OK) {
+ LOGW("IO_ERROR(0x%08x) : fail to register busy handler(%s)\n", PREFERENCE_ERROR_IO_ERROR, sqlite3_errmsg(pref_db));
+ }
+
+ ret = sqlite3_exec(pref_db, "CREATE TABLE IF NOT EXISTS pref ( pref_key TEXT PRIMARY KEY, pref_type TEXT, pref_data TEXT)",
+ NULL, NULL, &errmsg);
+ if (ret != SQLITE_OK)
+ {
+ LOGE("IO_ERROR(0x%08x) : fail to create db table(%s)", PREFERENCE_ERROR_IO_ERROR, errmsg);
+ sqlite3_free(errmsg);
+ sqlite3_close(pref_db);
+ pref_db = NULL;
+ return PREFERENCE_ERROR_IO_ERROR;
+ }
+
+ app_finalizer_add(_finish, NULL);
+
+ return PREFERENCE_ERROR_NONE;
+}
+
+static int _write_data(const char *key, const char *type, const char *data)
+{
+ int ret;
+ char *buf = NULL;
+ bool exist = false;
+ sqlite3_stmt *stmt;
+
+ if (key == NULL || key[0] == '\0' || data == NULL)
+ {
+ LOGE("INVALID_PARAMETER(0x%08x)", PREFERENCE_ERROR_INVALID_PARAMETER);
+ return PREFERENCE_ERROR_INVALID_PARAMETER;
+ }
+
+ /* insert data or update data if data already exist */
+ ret = preference_is_existing(key, &exist);
+ if (ret != PREFERENCE_ERROR_NONE)
+ {
+ return ret;
+ }
+
+ // to use sqlite3_update_hook, we have to use INSERT/UPDATE operation instead of REPLACE operation
+ if (exist)
+ {
+ buf = sqlite3_mprintf("UPDATE %s SET %s=?, %s=? WHERE %s=?;",
+ PREF_TBL_NAME, PREF_F_TYPE_NAME, PREF_F_DATA_NAME, PREF_F_KEY_NAME);
+ }
+ else
+ {
+ buf = sqlite3_mprintf("INSERT INTO %s (%s, %s, %s) values (?, ?, ?);",
+ PREF_TBL_NAME, PREF_F_KEY_NAME, PREF_F_TYPE_NAME, PREF_F_DATA_NAME);
+ }
+
+ if (buf == NULL)
+ {
+ LOGE("IO_ERROR(0x%08x) : fail to create query string", PREFERENCE_ERROR_IO_ERROR);
+ return PREFERENCE_ERROR_IO_ERROR;
+ }
+
+ ret = sqlite3_prepare(pref_db, buf, strlen(buf), &stmt, NULL);
+ if (ret != SQLITE_OK) {
+ LOGE("IO_ERROR(0x%08x) : fail to prepare query (%d/%s)",
+ PREFERENCE_ERROR_IO_ERROR,
+ sqlite3_extended_errcode(pref_db),
+ sqlite3_errmsg(pref_db));
+ return PREFERENCE_ERROR_IO_ERROR;
+ }
+
+ if(exist)
+ {
+ ret = sqlite3_bind_text(stmt, 1, type, strlen(type), SQLITE_STATIC);
+ if(ret != SQLITE_OK) {
+ LOGE("IO_ERROR(0x%08x) : fail to bind(1) query (%d/%s)",
+ PREFERENCE_ERROR_IO_ERROR,
+ sqlite3_extended_errcode(pref_db),
+ sqlite3_errmsg(pref_db));
+ sqlite3_finalize(stmt);
+ return PREFERENCE_ERROR_IO_ERROR;
+ }
+ ret = sqlite3_bind_text(stmt, 2, data, strlen(data), SQLITE_STATIC);
+ if(ret != SQLITE_OK) {
+ LOGE("IO_ERROR(0x%08x) : fail to bind(2) query (%d/%s)",
+ PREFERENCE_ERROR_IO_ERROR,
+ sqlite3_extended_errcode(pref_db),
+ sqlite3_errmsg(pref_db));
+ sqlite3_finalize(stmt);
+ return PREFERENCE_ERROR_IO_ERROR;
+ }
+ ret = sqlite3_bind_text(stmt, 3, key, strlen(key), SQLITE_STATIC);
+ if(ret != SQLITE_OK) {
+ LOGE("IO_ERROR(0x%08x) : fail to bind(3) query (%d/%s)",
+ PREFERENCE_ERROR_IO_ERROR,
+ sqlite3_extended_errcode(pref_db),
+ sqlite3_errmsg(pref_db));
+ sqlite3_finalize(stmt);
+ return PREFERENCE_ERROR_IO_ERROR;
+ }
+ }
+ else
+ {
+ ret = sqlite3_bind_text(stmt, 1, key, strlen(key), SQLITE_STATIC);
+ if(ret != SQLITE_OK) {
+ LOGE("IO_ERROR(0x%08x) : fail to bind(1) query (%d/%s)",
+ PREFERENCE_ERROR_IO_ERROR,
+ sqlite3_extended_errcode(pref_db),
+ sqlite3_errmsg(pref_db));
+ sqlite3_finalize(stmt);
+ return PREFERENCE_ERROR_IO_ERROR;
+ }
+ ret = sqlite3_bind_text(stmt, 2, type, strlen(type), SQLITE_STATIC);
+ if(ret != SQLITE_OK) {
+ LOGE("IO_ERROR(0x%08x) : fail to bind(2) query (%d/%s)",
+ PREFERENCE_ERROR_IO_ERROR,
+ sqlite3_extended_errcode(pref_db),
+ sqlite3_errmsg(pref_db));
+ sqlite3_finalize(stmt);
+ return PREFERENCE_ERROR_IO_ERROR;
+ }
+ ret = sqlite3_bind_text(stmt, 3, data, strlen(data), SQLITE_STATIC);
+ if(ret != SQLITE_OK) {
+ LOGE("IO_ERROR(0x%08x) : fail to bind(3) query (%d/%s)",
+ PREFERENCE_ERROR_IO_ERROR,
+ sqlite3_extended_errcode(pref_db),
+ sqlite3_errmsg(pref_db));
+ sqlite3_finalize(stmt);
+ return PREFERENCE_ERROR_IO_ERROR;
+ }
+ }
+
+ ret = sqlite3_step(stmt);
+ if (ret != SQLITE_DONE) {
+ LOGE("IO_ERROR(0x%08x): fail to write data(%d/%s)",
+ PREFERENCE_ERROR_IO_ERROR,
+ sqlite3_extended_errcode(pref_db),
+ sqlite3_errmsg(pref_db));
+ sqlite3_finalize(stmt);
+ return PREFERENCE_ERROR_IO_ERROR;
+ }
+
+ sqlite3_finalize(stmt);
+ if(buf) {
+ sqlite3_free(buf);
+ buf = NULL;
+ }
+
+ return PREFERENCE_ERROR_NONE;
+}
+
+//static int _read_data(const char *key, preference_type_e *type, char *data)
+static int _read_data(const char *key, char *type, char *data)
+{
+ int ret;
+ char *buf;
+ char **result;
+ int rows;
+ int columns;
+ char *errmsg;
+
+ if (key == NULL || key[0] == '\0' || data == NULL)
+ {
+ LOGE("INVALID_PARAMETER(0x%08x)", PREFERENCE_ERROR_INVALID_PARAMETER);
+ return PREFERENCE_ERROR_INVALID_PARAMETER;
+ }
+
+ if (pref_db == NULL)
+ {
+ if (_initialize() != PREFERENCE_ERROR_NONE)
+ {
+ LOGE("IO_ERROR(0x%08x) : fail to initialize db", PREFERENCE_ERROR_IO_ERROR);
+ return PREFERENCE_ERROR_IO_ERROR;
+ }
+ }
+
+ buf = sqlite3_mprintf("SELECT %s, %s, %s FROM %s WHERE %s=%Q;",
+ PREF_F_KEY_NAME, PREF_F_TYPE_NAME, PREF_F_DATA_NAME, PREF_TBL_NAME, PREF_F_KEY_NAME, key);
+
+ if (buf == NULL)
+ {
+ LOGE("IO_ERROR(0x%08x) : fail to create query string", PREFERENCE_ERROR_IO_ERROR);
+ return PREFERENCE_ERROR_IO_ERROR;
+ }
+
+ ret = sqlite3_get_table(pref_db, buf, &result, &rows, &columns, &errmsg);
+ sqlite3_free(buf);
+ if (ret != SQLITE_OK)
+ {
+ LOGE("IO_ERROR(0x%08x) : fail to read data (%s)", PREFERENCE_ERROR_IO_ERROR, errmsg);
+ sqlite3_free(errmsg);
+ return PREFERENCE_ERROR_IO_ERROR;
+ }
+
+ if (rows == 0)
+ {
+ LOGE("NO_KEY(0x%08x) : fail to find given key(%s)", PREFERENCE_ERROR_NO_KEY, key);
+ sqlite3_free_table(result);
+ return PREFERENCE_ERROR_NO_KEY;
+ }
+
+ snprintf(type, 2, "%s", result[4]); // get type value
+ snprintf(data, BUF_LEN, "%s", result[5]); // get data value
+
+ sqlite3_free_table(result);
+
+ return PREFERENCE_ERROR_NONE;
+}
+
+
+int preference_set_int(const char *key, int value)
+{
+ char type[2];
+ char data[BUF_LEN];
+ snprintf(type, 2, "%d", PREFERENCE_TYPE_INT);
+ snprintf(data, BUF_LEN, "%d", value);
+ return _write_data(key, type, data);
+}
+
+int preference_get_int(const char *key, int *value)
+{
+ char type[2];
+ char data[BUF_LEN];
+ int ret;
+
+ if (value == NULL) {
+ LOGE("INVALID_PARAMETER(0x%08x)", PREFERENCE_ERROR_INVALID_PARAMETER);
+ return PREFERENCE_ERROR_INVALID_PARAMETER;
+ }
+
+ ret = _read_data(key, type, data);
+ if (ret == PREFERENCE_ERROR_NONE)
+ {
+ if (atoi(type) == PREFERENCE_TYPE_INT)
+ {
+ *value = atoi(data);
+ }
+ else
+ {
+ LOGE("INVALID_PARAMETER(0x%08x) : param type(%d)", PREFERENCE_ERROR_INVALID_PARAMETER, atoi(type));
+ return PREFERENCE_ERROR_INVALID_PARAMETER;
+ }
+ }
+
+ return ret;
+}
+
+int preference_set_double(const char *key, double value)
+{
+ char type[2];
+ char data[BUF_LEN];
+
+ locale_t loc = newlocale(LC_NUMERIC_MASK, "C", NULL);
+ uselocale(loc);
+
+ snprintf(type, 2, "%d", PREFERENCE_TYPE_DOUBLE);
+ snprintf(data, BUF_LEN, "%f", value);
+
+ freelocale(loc);
+ uselocale(LC_GLOBAL_LOCALE);
+
+ return _write_data(key, type, data);
+}
+
+int preference_get_double(const char *key, double *value)
+{
+ char type[2];
+ char data[BUF_LEN];
+
+ int ret;
+
+ if (value == NULL) {
+ LOGE("INVALID_PARAMETER(0x%08x)", PREFERENCE_ERROR_INVALID_PARAMETER);
+ return PREFERENCE_ERROR_INVALID_PARAMETER;
+ }
+
+ ret = _read_data(key, type, data);
+ if (ret == PREFERENCE_ERROR_NONE)
+ {
+ if (atoi(type) == PREFERENCE_TYPE_DOUBLE)
+ {
+ locale_t loc = newlocale(LC_NUMERIC_MASK, "C", NULL);
+ uselocale(loc);
+
+ *value = atof(data);
+
+ freelocale(loc);
+ uselocale(LC_GLOBAL_LOCALE);
+ }
+ else
+ {
+ LOGE("INVALID_PARAMETER(0x%08x) : param type(%d)", PREFERENCE_ERROR_INVALID_PARAMETER, atoi(type));
+ return PREFERENCE_ERROR_INVALID_PARAMETER;
+ }
+ }
+
+ return ret;
+}
+
+int preference_set_string(const char *key, const char *value)
+{
+ char type[2];
+
+ snprintf(type, 2, "%d", PREFERENCE_TYPE_STRING);
+ if (strlen(value) > (BUF_LEN-1))
+ {
+ LOGE("INVALID_PARAMETER(0x%08x) : param type(%d)", PREFERENCE_ERROR_INVALID_PARAMETER, atoi(type));
+ return PREFERENCE_ERROR_INVALID_PARAMETER;
+ }
+ return _write_data(key, type, value);
+}
+
+int preference_get_string(const char *key, char **value)
+{
+ char type[2];
+ char data[BUF_LEN];
+
+ int ret;
+
+ if (value == NULL)
+ {
+ LOGE("INVALID_PARAMETER(0x%08x)", PREFERENCE_ERROR_INVALID_PARAMETER);
+ return PREFERENCE_ERROR_INVALID_PARAMETER;
+ }
+
+ ret = _read_data(key, type, data);
+ if (ret == PREFERENCE_ERROR_NONE)
+ {
+ if (atoi(type) == PREFERENCE_TYPE_STRING)
+ {
+ *value = strdup(data);
+ if (value == NULL)
+ {
+ LOGE("OUT_OF_MEMORY(0x%08x)", PREFERENCE_ERROR_OUT_OF_MEMORY);
+ return PREFERENCE_ERROR_OUT_OF_MEMORY;
+ }
+ }
+ else
+ {
+ LOGE("INVALID_PARAMETER(0x%08x) : param type(%d)", PREFERENCE_ERROR_INVALID_PARAMETER, atoi(type));
+ return PREFERENCE_ERROR_INVALID_PARAMETER;
+ }
+ }
+
+ return ret;
+}
+
+int preference_set_boolean(const char *key, bool value)
+{
+ char type[2];
+ char data[BUF_LEN];
+ snprintf(type, 2, "%d", PREFERENCE_TYPE_BOOLEAN);
+ snprintf(data, BUF_LEN, "%d", value);
+ return _write_data(key, type, data);
+}
+
+int preference_get_boolean(const char *key, bool *value)
+{
+ char type[2];
+ char data[BUF_LEN];
+
+ int ret;
+
+ if (value == NULL) {
+ LOGE("INVALID_PARAMETER(0x%08x)", PREFERENCE_ERROR_INVALID_PARAMETER);
+ return PREFERENCE_ERROR_INVALID_PARAMETER;
+ }
+
+ ret = _read_data(key, type, data);
+ if (ret == PREFERENCE_ERROR_NONE)
+ {
+ if (atoi(type) == PREFERENCE_TYPE_BOOLEAN)
+ {
+ *value = (bool)atoi(data);
+ }
+ else
+ {
+ LOGE("INVALID_PARAMETER(0x%08x) : param type(%d)", PREFERENCE_ERROR_INVALID_PARAMETER, atoi(type));
+ return PREFERENCE_ERROR_INVALID_PARAMETER;
+ }
+ }
+
+ return ret;
+}
+
+
+// TODO: below operation is too heavy, let's find the light way to check.
+int preference_is_existing(const char *key, bool *exist)
+{
+ int ret;
+ char *buf;
+ char **result;
+ int rows;
+ int columns;
+ char *errmsg;
+
+ if (key == NULL || key[0] == '\0' || exist == NULL)
+ {
+ LOGE("INVALID_PARAMETER(0x%08x)", PREFERENCE_ERROR_INVALID_PARAMETER);
+ return PREFERENCE_ERROR_INVALID_PARAMETER;
+ }
+
+ if (pref_db == NULL)
+ {
+ if (_initialize() != PREFERENCE_ERROR_NONE)
+ {
+ LOGE("IO_ERROR(0x%08x) : fail to initialize db", PREFERENCE_ERROR_IO_ERROR);
+ return PREFERENCE_ERROR_IO_ERROR;
+ }
+ }
+
+ /* check data is exist */
+ buf = sqlite3_mprintf("SELECT %s FROM %s WHERE %s=%Q;", PREF_F_KEY_NAME, PREF_TBL_NAME, PREF_F_KEY_NAME, key);
+
+ if (buf == NULL)
+ {
+ LOGE("IO_ERROR(0x%08x) : fail to create query string", PREFERENCE_ERROR_IO_ERROR);
+ return PREFERENCE_ERROR_IO_ERROR;
+ }
+
+ ret = sqlite3_get_table(pref_db, buf, &result, &rows, &columns, &errmsg);
+ sqlite3_free(buf);
+ if (ret != SQLITE_OK)
+ {
+ LOGE("IO_ERROR(0x%08x) : fail to read data(%s)", PREFERENCE_ERROR_IO_ERROR, errmsg);
+ sqlite3_free(errmsg);
+ return PREFERENCE_ERROR_IO_ERROR;
+ }
+
+ if (rows > 0)
+ {
+ *exist = true;
+ }
+ else
+ {
+ *exist = false;
+ }
+
+ sqlite3_free_table(result);
+ return PREFERENCE_ERROR_NONE;
+}
+
+static pref_changed_cb_node_t* _find_node(const char *key)
+{
+ pref_changed_cb_node_t *tmp_node;
+
+ if (key == NULL || key[0] == '\0' )
+ {
+ return NULL;
+ }
+
+ tmp_node = head;
+
+ while (tmp_node)
+ {
+ if (strcmp(tmp_node->key, key) == 0)
+ {
+ break;
+ }
+ tmp_node = tmp_node->next;
+ }
+
+ return tmp_node;
+}
+
+
+static int _add_node(const char *key, preference_changed_cb cb, void *user_data)
+{
+ pref_changed_cb_node_t *tmp_node;
+
+ if (key == NULL || key[0] == '\0' || cb == NULL)
+ {
+ LOGE("INVALID_PARAMETER(0x%08x)", PREFERENCE_ERROR_INVALID_PARAMETER);
+ return PREFERENCE_ERROR_INVALID_PARAMETER;
+ }
+
+ tmp_node = _find_node(key);
+
+ if (tmp_node != NULL)
+ {
+ tmp_node->cb = cb;
+ tmp_node->user_data = user_data;
+ }
+ else
+ {
+ tmp_node = (pref_changed_cb_node_t*)malloc(sizeof(pref_changed_cb_node_t));
+ if (tmp_node == NULL)
+ {
+ LOGE("OUT_OF_MEMORY(0x%08x)", PREFERENCE_ERROR_OUT_OF_MEMORY);
+ return PREFERENCE_ERROR_OUT_OF_MEMORY;
+ }
+
+ tmp_node->key = strdup(key);
+ if (tmp_node->key == NULL)
+ {
+ free(tmp_node);
+ LOGE("OUT_OF_MEMORY(0x%08x)", PREFERENCE_ERROR_OUT_OF_MEMORY);
+ return PREFERENCE_ERROR_OUT_OF_MEMORY;
+ }
+
+ if (head != NULL)
+ head->prev = tmp_node;
+ tmp_node->cb = cb;
+ tmp_node->user_data = user_data;
+ tmp_node->prev = NULL;
+ tmp_node->next = head;
+ head = tmp_node;
+ }
+
+ return PREFERENCE_ERROR_NONE;
+}
+
+static int _remove_node(const char *key)
+{
+ pref_changed_cb_node_t *tmp_node;
+
+ if (key == NULL || key[0] == '\0' )
+ {
+ LOGE("INVALID_PARAMETER(0x%08x)", PREFERENCE_ERROR_INVALID_PARAMETER);
+ return PREFERENCE_ERROR_INVALID_PARAMETER;
+ }
+
+ tmp_node = _find_node(key);
+
+ if (tmp_node == NULL)
+ {
+ return PREFERENCE_ERROR_NONE;
+ }
+
+ if (tmp_node->prev != NULL)
+ {
+ tmp_node->prev->next = tmp_node->next;
+ }
+ else
+ {
+ head = tmp_node->next;
+ }
+
+ if (tmp_node->next != NULL)
+ {
+ tmp_node->next->prev = tmp_node->prev;
+ }
+
+ if (tmp_node->key)
+ {
+ free(tmp_node->key);
+ }
+
+ free(tmp_node);
+
+ return PREFERENCE_ERROR_NONE;
+}
+
+
+static void _remove_all_node(void)
+{
+ pref_changed_cb_node_t *tmp_node;
+
+ while (head)
+ {
+ tmp_node = head;
+ head = tmp_node->next;
+
+ if (tmp_node->key)
+ {
+ free(tmp_node->key);
+ }
+
+ free(tmp_node);
+ }
+}
+
+
+static void _update_cb(void *data, int action, char const *db_name, char const *table_name, sqlite_int64 rowid)
+{
+ int ret;
+ char *buf;
+ char **result;
+ int rows;
+ int columns;
+ char *errmsg;
+ pref_changed_cb_node_t *tmp_node;
+
+ // skip INSERT/DELETE event
+ if (action != SQLITE_UPDATE)
+ {
+ return;
+ }
+
+ if (strcmp(table_name, PREF_TBL_NAME) != 0)
+ {
+ SECURE_LOGE("given table name (%s) is not same", table_name);
+ return;
+ }
+
+ buf = sqlite3_mprintf("SELECT %s FROM %s WHERE rowid='%lld';", PREF_F_KEY_NAME, PREF_TBL_NAME, rowid);
+ if (buf == NULL)
+ {
+ return;
+ }
+ ret = sqlite3_get_table(pref_db, buf, &result, &rows, &columns, &errmsg);
+ sqlite3_free(buf);
+ if (ret != SQLITE_OK)
+ {
+ LOGI("fail to read data(%s)", errmsg);
+ sqlite3_free(errmsg);
+ return;
+ }
+
+ if (rows == 0)
+ {
+ sqlite3_free_table(result);
+ return;
+ }
+
+ tmp_node = _find_node(result[1]);
+
+ if (tmp_node != NULL && tmp_node->cb != NULL)
+ {
+ tmp_node->cb(result[1], tmp_node->user_data);
+ }
+
+ sqlite3_free_table(result);
+}
+
+
+int preference_remove(const char *key)
+{
+ int ret;
+ char *buf;
+ bool exist;
+ sqlite3_stmt *stmt;
+
+ ret = preference_is_existing(key, &exist);
+ if (ret != PREFERENCE_ERROR_NONE)
+ {
+ return ret;
+ }
+
+ if (!exist)
+ {
+ return PREFERENCE_ERROR_NO_KEY;
+ }
+
+ /* insert data or update data if data already exist */
+ buf = sqlite3_mprintf("DELETE FROM %s WHERE %s = ?",
+ PREF_TBL_NAME, PREF_F_KEY_NAME, key);
+ if (buf == NULL)
+ {
+ LOGE("IO_ERROR(0x%08x) : fail to create query string", PREFERENCE_ERROR_IO_ERROR);
+ return PREFERENCE_ERROR_IO_ERROR;
+ }
+
+ ret = sqlite3_prepare(pref_db, buf, strlen(buf), &stmt, NULL);
+ if (ret != SQLITE_OK) {
+ LOGE("IO_ERROR(0x%08x) : fail to prepare query (%d/%s)",
+ PREFERENCE_ERROR_IO_ERROR,
+ sqlite3_extended_errcode(pref_db),
+ sqlite3_errmsg(pref_db));
+ return PREFERENCE_ERROR_IO_ERROR;
+ }
+
+ ret = sqlite3_bind_text(stmt, 1, key, strlen(key), SQLITE_STATIC);
+ if(ret != SQLITE_OK) {
+ LOGE("IO_ERROR(0x%08x) : fail to bind(1) query (%d/%s)",
+ PREFERENCE_ERROR_IO_ERROR,
+ sqlite3_extended_errcode(pref_db),
+ sqlite3_errmsg(pref_db));
+ sqlite3_finalize(stmt);
+ return PREFERENCE_ERROR_IO_ERROR;
+ }
+
+ ret = sqlite3_step(stmt);
+ if (ret != SQLITE_DONE) {
+ LOGE("IO_ERROR(0x%08x): fail to delete data(%d/%s)",
+ PREFERENCE_ERROR_IO_ERROR,
+ sqlite3_extended_errcode(pref_db),
+ sqlite3_errmsg(pref_db));
+ sqlite3_finalize(stmt);
+ return PREFERENCE_ERROR_IO_ERROR;
+ }
+
+ sqlite3_finalize(stmt);
+ if(buf) {
+ sqlite3_free(buf);
+ buf = NULL;
+ }
+
+ // if exist, remove changed cb
+ _remove_node(key);
+
+ return PREFERENCE_ERROR_NONE;
+}
+
+
+int preference_remove_all(void)
+{
+ int ret;
+ char *buf;
+ char *errmsg;
+
+ if (pref_db == NULL)
+ {
+ if (_initialize() != PREFERENCE_ERROR_NONE)
+ {
+ LOGE("IO_ERROR(0x%08x) : fail to initialize db", PREFERENCE_ERROR_IO_ERROR);
+ return PREFERENCE_ERROR_IO_ERROR;
+ }
+ }
+
+ /* insert data or update data if data already exist */
+ buf = sqlite3_mprintf("DELETE FROM %s;", PREF_TBL_NAME);
+ if (buf == NULL)
+ {
+ LOGE("IO_ERROR(0x%08x) : fail to create query string", PREFERENCE_ERROR_IO_ERROR);
+ return PREFERENCE_ERROR_IO_ERROR;
+ }
+
+ ret = sqlite3_exec(pref_db, buf, NULL, NULL, &errmsg);
+ sqlite3_free(buf);
+ if (ret != SQLITE_OK)
+ {
+ LOGE("IO_ERROR(0x%08x) : fail to delete data (%s)", PREFERENCE_ERROR_IO_ERROR, errmsg);
+ sqlite3_free(errmsg);
+ return PREFERENCE_ERROR_IO_ERROR;
+ }
+
+ // if exist, remove changed cb
+ _remove_all_node();
+
+ return PREFERENCE_ERROR_NONE;
+}
+
+
+int preference_set_changed_cb(const char *key, preference_changed_cb callback, void *user_data)
+{
+ int ret;
+ bool exist;
+
+ ret = preference_is_existing(key, &exist);
+ if (ret != PREFERENCE_ERROR_NONE)
+ {
+ return ret;
+ }
+
+ if (!exist)
+ {
+ LOGE("NO_KEY(0x%08x) : fail to find given key(%s)", PREFERENCE_ERROR_NO_KEY, key);
+ return PREFERENCE_ERROR_NO_KEY;
+ }
+
+ if (!is_update_hook_registered)
+ {
+ sqlite3_update_hook(pref_db, _update_cb, NULL);
+ is_update_hook_registered = true;
+ }
+
+ return _add_node(key, callback, user_data);
+}
+
+int preference_unset_changed_cb(const char *key)
+{
+ int ret;
+ bool exist;
+
+ ret = preference_is_existing(key, &exist);
+ if (ret != PREFERENCE_ERROR_NONE)
+ {
+ return ret;
+ }
+
+ if (!exist)
+ {
+ LOGE("NO_KEY(0x%08x) : fail to find given key(%s)", PREFERENCE_ERROR_NO_KEY, key);
+ return PREFERENCE_ERROR_NO_KEY;
+ }
+
+ return _remove_node(key);
+}
+
+int preference_foreach_item(preference_item_cb callback, void *user_data)
+{
+ int ret;
+ char *buf;
+ char **result;
+ int rows;
+ int columns;
+ char *errmsg;
+ int i;
+
+ if (callback == NULL)
+ {
+ LOGE("INVALID_PARAMETER(0x%08x)", PREFERENCE_ERROR_INVALID_PARAMETER);
+ return PREFERENCE_ERROR_INVALID_PARAMETER;
+ }
+
+ if (pref_db == NULL)
+ {
+ if (_initialize() != PREFERENCE_ERROR_NONE)
+ {
+ LOGE("IO_ERROR(0x%08x) : fail to initialize db", PREFERENCE_ERROR_IO_ERROR);
+ return PREFERENCE_ERROR_IO_ERROR;
+ }
+ }
+
+ buf = sqlite3_mprintf("SELECT %s FROM %s;", PREF_F_KEY_NAME, PREF_TBL_NAME);
+ if (buf == NULL)
+ {
+ LOGE("IO_ERROR(0x%08x) : fail to create query string", PREFERENCE_ERROR_IO_ERROR);
+ return PREFERENCE_ERROR_IO_ERROR;
+ }
+
+ ret = sqlite3_get_table(pref_db, buf, &result, &rows, &columns, &errmsg);
+ sqlite3_free(buf);
+ if (ret != SQLITE_OK)
+ {
+ LOGE("IO_ERROR(0x%08x) : fail to read data (%s)", PREFERENCE_ERROR_IO_ERROR, errmsg);
+ sqlite3_free(errmsg);
+ return PREFERENCE_ERROR_IO_ERROR;
+ }
+
+ for (i = 1; i <= rows; i++)
+ {
+ if (callback(result[i], user_data) != true)
+ {
+ break;
+ }
+ }
+
+ sqlite3_free_table(result);
+
+ return PREFERENCE_ERROR_NONE;
+}
+
diff --git a/preference/preference_inoti.c b/preference/preference_inoti.c
new file mode 100755
index 0000000..dbdea63
--- /dev/null
+++ b/preference/preference_inoti.c
@@ -0,0 +1,416 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 <sys/types.h>
+#include <sys/inotify.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <linux/version.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <glib.h>
+
+#include <app_preference.h>
+#include <app_preference_internal.h>
+
+#include <glib.h>
+
+#define INOTY_EVENT_MASK (IN_CLOSE_WRITE | IN_DELETE_SELF)
+
+/* inotify */
+struct noti_node {
+ int wd;
+ char *keyname;
+ preference_changed_cb cb;
+ void *cb_data;
+ struct noti_node *next;
+};
+typedef struct noti_node noti_node_s;
+static GList *g_notilist;
+
+static int _preference_inoti_comp_with_wd(gconstpointer a, gconstpointer b)
+{
+ int r;
+
+ noti_node_s *key1 = (noti_node_s *) a;
+ noti_node_s *key2 = (noti_node_s *) b;
+
+ r = key1->wd - key2->wd;
+ return r;
+}
+
+static int _kdb_inoti_fd;
+
+static pthread_mutex_t _kdb_inoti_fd_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t _kdb_g_ns_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static GSource *_kdb_handler;
+
+static GList* _preference_copy_noti_list(GList *orig_notilist)
+{
+ GList *copy_notilist = NULL;
+ struct noti_node *n = NULL;
+ struct noti_node *t = NULL;
+
+ if (!orig_notilist)
+ return NULL;
+
+ orig_notilist = g_list_first(orig_notilist);
+ if (!orig_notilist)
+ return NULL;
+
+ while (orig_notilist) {
+ do {
+ t = orig_notilist->data;
+
+ if (t == NULL) {
+ WARN("noti item data is null");
+ break;
+ }
+
+ if ((t->keyname == NULL) || (strlen(t->keyname) == 0)) {
+ WARN("noti item data key name is null");
+ break;
+ }
+
+ n = calloc(1, sizeof(noti_node_s));
+ if (n == NULL) {
+ ERR("_preference_copy_noti_list : calloc failed. memory full");
+ break;
+ }
+
+ n->keyname = strndup(t->keyname, PREFERENCE_KEY_PATH_LEN);
+ if (n->keyname == NULL)
+ {
+ ERR("The memory is insufficient, errno: %d (%s)", errno, strerror(errno));
+ free(n);
+ break;
+ }
+ n->wd = t->wd;
+ n->cb_data = t->cb_data;
+ n->cb = t->cb;
+
+ copy_notilist = g_list_append(copy_notilist, n);
+ } while (0);
+
+ orig_notilist = g_list_next(orig_notilist);
+ }
+ return copy_notilist;
+}
+
+static void _preference_free_noti_node(gpointer data)
+{
+ struct noti_node *n = (struct noti_node*)data;
+ g_free(n->keyname);
+ g_free(n);
+}
+
+static void _preference_free_noti_list(GList *noti_list)
+{
+ g_list_free_full(noti_list, _preference_free_noti_node);
+}
+
+
+static gboolean _preference_kdb_gio_cb(GIOChannel *src, GIOCondition cond, gpointer data)
+{
+ int fd, r, res;
+ struct inotify_event ie;
+ GList *l_notilist = NULL;
+
+ fd = g_io_channel_unix_get_fd(src);
+ r = read(fd, &ie, sizeof(ie));
+
+ while (r > 0) {
+ if (ie.mask & INOTY_EVENT_MASK) {
+
+ INFO("read event from GIOChannel. wd : %d", ie.wd);
+
+ pthread_mutex_lock(&_kdb_g_ns_mutex);
+ l_notilist = _preference_copy_noti_list(g_notilist);
+ pthread_mutex_unlock(&_kdb_g_ns_mutex);
+
+ if (l_notilist) {
+
+ struct noti_node *t = NULL;
+ GList *noti_list = NULL;
+
+ noti_list = g_list_first(l_notilist);
+
+ while (noti_list) {
+ t = noti_list->data;
+
+ keynode_t* keynode = _preference_keynode_new();
+ if (keynode == NULL) {
+ ERR("key malloc fail");
+ break;
+ }
+
+ if ( (t) && (t->wd == ie.wd) && (t->keyname) ) {
+ if ((ie.mask & IN_DELETE_SELF))
+ {
+ res = _preference_kdb_del_notify(t->keyname);
+ if (res != PREFERENCE_ERROR_NONE)
+ ERR("_preference_kdb_del_notify() failed(%d)", res);
+ }
+ else
+ {
+ res = _preference_keynode_set_keyname(keynode, t->keyname);
+ if (res != PREFERENCE_ERROR_NONE) {
+ ERR("_preference_keynode_set_keyname() failed(%d)", res);
+ goto out_func;
+ }
+
+ res = _preference_get_key(keynode);
+ if (res != PREFERENCE_ERROR_NONE)
+ ERR("_preference_get_key() failed(%d)", res);
+
+ INFO("key(%s) is changed. cb(%p) called", t->keyname, t->cb);
+ t->cb(t->keyname, t->cb_data);
+ }
+ }
+ else if ( (t) && (t->keyname == NULL) ) { /* for debugging */
+ ERR("preference keyname is null.");
+ }
+out_func:
+ _preference_keynode_free(keynode);
+
+ noti_list = g_list_next(noti_list);
+ }
+
+ _preference_free_noti_list(l_notilist);
+ }
+ }
+
+ if (ie.len > 0)
+ (void) lseek(fd, ie.len, SEEK_CUR);
+
+ r = read(fd, &ie, sizeof(ie));
+ }
+ return TRUE;
+}
+
+static int _preference_kdb_noti_init(void)
+{
+ GIOChannel *gio;
+ int ret = 0;
+
+ pthread_mutex_lock(&_kdb_inoti_fd_mutex);
+
+ if (0 < _kdb_inoti_fd) {
+ ERR("Error: invalid _kdb_inoti_fd");
+ pthread_mutex_unlock(&_kdb_inoti_fd_mutex);
+ return PREFERENCE_ERROR_IO_ERROR;
+ }
+ _kdb_inoti_fd = inotify_init();
+ if (_kdb_inoti_fd == -1) {
+ char err_buf[100] = { 0, };
+ strerror_r(errno, err_buf, sizeof(err_buf));
+ ERR("inotify init: %s", err_buf);
+ pthread_mutex_unlock(&_kdb_inoti_fd_mutex);
+ return PREFERENCE_ERROR_IO_ERROR;
+ }
+
+ ret = fcntl(_kdb_inoti_fd, F_SETFD, FD_CLOEXEC);
+ if (ret < 0) {
+ char err_buf[100] = { 0, };
+ strerror_r(errno, err_buf, sizeof(err_buf));
+ ERR("inotify init: %s", err_buf);
+ pthread_mutex_unlock(&_kdb_inoti_fd_mutex);
+ return PREFERENCE_ERROR_IO_ERROR;
+ }
+
+ ret = fcntl(_kdb_inoti_fd, F_SETFL, O_NONBLOCK);
+ if (ret < 0) {
+ char err_buf[100] = { 0, };
+ strerror_r(errno, err_buf, sizeof(err_buf));
+ ERR("inotify init: %s", err_buf);
+ pthread_mutex_unlock(&_kdb_inoti_fd_mutex);
+ return PREFERENCE_ERROR_IO_ERROR;
+ }
+
+ pthread_mutex_unlock(&_kdb_inoti_fd_mutex);
+
+ gio = g_io_channel_unix_new(_kdb_inoti_fd);
+ retvm_if(gio == NULL, -1, "Error: create a new GIOChannel");
+
+ g_io_channel_set_flags(gio, G_IO_FLAG_NONBLOCK, NULL);
+
+ _kdb_handler = g_io_create_watch(gio, G_IO_IN);
+ g_source_set_callback(_kdb_handler, (GSourceFunc) _preference_kdb_gio_cb, NULL, NULL);
+ g_source_attach(_kdb_handler, NULL);
+ g_io_channel_unref(gio);
+ g_source_unref(_kdb_handler);
+
+ return PREFERENCE_ERROR_NONE;
+}
+
+int _preference_kdb_add_notify(const char *keyname, preference_changed_cb cb, void *data)
+{
+ char path[PREFERENCE_KEY_PATH_LEN];
+ int wd;
+ struct noti_node t, *n, *node;
+ char err_buf[ERR_LEN] = { 0, };
+ int ret = 0;
+ GList *list = NULL;
+ int func_ret = PREFERENCE_ERROR_NONE;
+
+ retvm_if((keyname == NULL || cb == NULL), PREFERENCE_ERROR_INVALID_PARAMETER,
+ "_preference_kdb_add_notify : Invalid argument - keyname(%s) cb(%p)",
+ keyname, cb);
+
+ if (_kdb_inoti_fd <= 0)
+ if (_preference_kdb_noti_init())
+ return PREFERENCE_ERROR_IO_ERROR;
+
+ ret = _preference_get_key_path((char*)keyname, path);
+ retvm_if(ret != PREFERENCE_ERROR_NONE, PREFERENCE_ERROR_INVALID_PARAMETER, "Invalid argument: key is not valid");
+
+ if (0 != access(path, F_OK)) {
+ if (errno == ENOENT) {
+ ERR("_preference_kdb_add_notify : Key(%s) does not exist", keyname);
+ return PREFERENCE_ERROR_IO_ERROR;
+ }
+ }
+
+ wd = inotify_add_watch(_kdb_inoti_fd, path, INOTY_EVENT_MASK);
+ if (wd == -1) {
+ strerror_r(errno, err_buf, sizeof(err_buf));
+ ERR("_preference_kdb_add_notify : add noti(%s)", err_buf);
+ return PREFERENCE_ERROR_IO_ERROR;
+ }
+
+ t.wd = wd;
+
+ pthread_mutex_lock(&_kdb_g_ns_mutex);
+
+ list = g_list_find_custom(g_notilist, &t, (GCompareFunc)_preference_inoti_comp_with_wd);
+ if (list) {
+ WARN("_preference_kdb_add_notify : key(%s) change callback(%p)", keyname, cb);
+
+ node = list->data;
+ node->wd = wd;
+ node->cb_data = data;
+ node->cb = cb;
+
+ goto out_func;
+ }
+
+ n = calloc(1, sizeof(noti_node_s));
+ if (n == NULL) {
+ strerror_r(errno, err_buf, sizeof(err_buf));
+ ERR("_preference_kdb_add_notify : add noti(%s)", err_buf);
+ func_ret = PREFERENCE_ERROR_IO_ERROR;
+ goto out_func;
+ }
+
+ n->keyname = strndup(keyname, PREFERENCE_KEY_PATH_LEN);
+ if (n->keyname == NULL)
+ {
+ ERR("The memory is insufficient, errno: %d (%s)", errno, strerror(errno));
+ free(n);
+ goto out_func;
+ }
+ n->wd = wd;
+ n->cb_data = data;
+ n->cb = cb;
+
+ g_notilist = g_list_append(g_notilist, n);
+ if (!g_notilist) {
+ ERR("g_list_append fail");
+ }
+
+ INFO("cb(%p) is added for %s. tot cb cnt : %d\n", cb, n->keyname, g_list_length(g_notilist));
+
+out_func:
+ pthread_mutex_unlock(&_kdb_g_ns_mutex);
+
+ return func_ret;
+}
+
+int _preference_kdb_del_notify(const char *keyname)
+{
+ int wd = 0;
+ int r = 0;
+ struct noti_node *n = NULL;
+ struct noti_node t;
+ char path[PREFERENCE_KEY_PATH_LEN] = { 0, };
+ char err_buf[ERR_LEN] = { 0, };
+ int del = 0;
+ int ret = 0;
+ int func_ret = PREFERENCE_ERROR_NONE;
+ GList *noti_list;
+
+ retvm_if(keyname == NULL, PREFERENCE_ERROR_INVALID_PARAMETER, "Invalid argument: keyname(%s)", keyname);
+ retvm_if(_kdb_inoti_fd == 0, PREFERENCE_ERROR_NONE, "Invalid operation: not exist anything for inotify");
+
+ ret = _preference_get_key_path((char*)keyname, path);
+ retvm_if(ret != PREFERENCE_ERROR_NONE, PREFERENCE_ERROR_INVALID_PARAMETER, "Invalid argument: key is not valid");
+
+ /* get wd */
+ wd = inotify_add_watch(_kdb_inoti_fd, path, INOTY_EVENT_MASK);
+ if (wd == -1) {
+ strerror_r(errno, err_buf, sizeof(err_buf));
+ ERR("Error: inotify_add_watch() [%s]: %s", path, err_buf);
+ return PREFERENCE_ERROR_IO_ERROR;
+ }
+
+ pthread_mutex_lock(&_kdb_g_ns_mutex);
+
+ t.wd = wd;
+
+ noti_list = g_list_find_custom(g_notilist, &t, (GCompareFunc)_preference_inoti_comp_with_wd);
+ if(noti_list) {
+ del++;
+
+ n = noti_list->data;
+ g_notilist = g_list_remove(g_notilist, n);
+ g_free(n->keyname);
+ g_free(n);
+
+ r = inotify_rm_watch(_kdb_inoti_fd, wd);
+ if(r == -1) {
+ strerror_r(errno, err_buf, sizeof(err_buf));
+ ERR("Error: inotify_rm_watch [%s]: %s", keyname, err_buf);
+ func_ret = PREFERENCE_ERROR_IO_ERROR;
+ }
+
+ INFO("key(%s) cb is removed. remained noti list total length(%d)",
+ keyname, g_list_length(g_notilist));
+ }
+
+ if(g_list_length(g_notilist) == 0) {
+ close(_kdb_inoti_fd);
+ _kdb_inoti_fd = 0;
+
+ g_source_destroy(_kdb_handler);
+ _kdb_handler = NULL;
+
+ g_list_free(g_notilist);
+ g_notilist = NULL;
+
+ INFO("all noti list is freed");
+ }
+
+ pthread_mutex_unlock(&_kdb_g_ns_mutex);
+
+ if(del == 0) {
+ errno = ENOENT;
+ func_ret = PREFERENCE_ERROR_IO_ERROR;
+ }
+
+ return func_ret;
+}