SET(CMAKE_C_FLAGS_DEBUG "-O0 -g")
ADD_LIBRARY (${PROJECT_NAME} SHARED
+ src/data-control-noti.c
src/data-control-map.c
src/data-control-sql.c
src/data-control-sql-cursor.c
src/data-control-provider.c
src/data-control-internal.c
src/data_control_internal.c
+ src/data_control_noti.c
src/data_control_map.c
src/data_control_sql.c
src/data_control_sql_cursor.c
SET(VERSION ${FULLVER})
CONFIGURE_FILE(${PROJECT_NAME}.pc.in ${CMAKE_SOURCE_DIR}/${PROJECT_NAME}.pc @ONLY)
+CONFIGURE_FILE(${PROJECT_NAME}.conf.in ${PROJECT_NAME}.conf @ONLY)
+
+INSTALL(FILES ${CMAKE_SOURCE_DIR}/${PROJECT_NAME}.conf DESTINATION ${SYSCONF_INSTALL_DIR}/dbus-1/session.d/)
INSTALL(FILES ${CMAKE_SOURCE_DIR}/${PROJECT_NAME}.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig)
CONFIGURE_FILE(capi-${PROJECT_NAME}.pc.in ${CMAKE_SOURCE_DIR}/capi-${PROJECT_NAME}.pc @ONLY)
INSTALL(FILES ${CMAKE_SOURCE_DIR}/capi-${PROJECT_NAME}.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig)
--- /dev/null
+<?xml version="1.0"?>
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+ <policy user="user">
+ </policy>
+ <policy context="default">
+ <check receive_type="signal" receive_interface="org.tizen.data_control_service" privilege="http://tizen.org/privilege/datasharing" />
+ <check send_type="signal" send_interface="org.tizen.data_control_service" privilege="http://tizen.org/privilege/datasharing" />
+ </policy>
+</busconfig>
* - data_control_sql_get_int64_data()
* - data_control_sql_get_double_data()
* - data_control_sql_get_text_data()
+ * Note: All callbacks are called in the main loop context, unless stated otherwise.
*/
/**
* The service application providing its own database file must register the provider callback using data_control_provider_sql_register_cb().
* The service application providing its own registry file or key-value pairs data set must register the provider callback using the data_control_provider_map_register_cb().
* The service application sends SQL-type or Map-type data control result to the other application, by using methods such as data_control_provider_send_select_result(),data_control_provider_send_insert_result(), data_control_provider_send_update_result(), or data_control_provider_send_delete_result().
+ * Note: All callbacks are called in the main loop context, unless stated otherwise.
*/
--- /dev/null
+/*
+ * Copyright (c) 2013 - 2016 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 _APPFW_DATA_CONTROL_DATA_CHANGE_H_
+#define _APPFW_DATA_CONTROL_DATA_CHANGE_H_
+
+#include <data_control_types.h>
+#include <data_control_noti.h>
+#include <data-control-types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Adds data changed callback which called when provider's data is changed.
+ * @param [in] provider Target provider handle
+ * @param [in] callback The callback function to be called when consumer receive data changed notification
+ * @param [in] user_data The user data to be passed to the callback function
+ * @param [in] result_callback The callback function to be called when consumer receive add data changed callback process result
+ * @param [in] result_cb_user_data The user data to be passed to the result_callback function
+ * @param [out] callback_id Added callback ID
+ * @return 0 on success, otherwise a negative error value.
+ * @retval #DATACONTROL_ERROR_NONE Successful
+ * @retval #DATACONTROL_ERROR_IO_ERROR I/O error
+ * @retval #DATACONTROL_ERROR_OUT_OF_MEMORY Out of memory
+ */
+EXPORT_API int datacontrol_add_data_change_cb(datacontrol_h provider,
+ data_control_data_change_cb callback,
+ void *user_data,
+ data_control_add_callback_result_cb result_callback,
+ void *result_cb_user_data,
+ int *callback_id);
+
+/**
+ * @brief Removes the data changed callback function.
+ * @param [in] provider Target provider handle
+ * @param [in] callback_id Target callback ID
+ * @return 0 on success, otherwise a negative error value.
+ * @retval #DATACONTROL_ERROR_NONE Successful
+ * @retval #DATACONTROL_ERROR_IO_ERROR I/O error
+ * @retval #DATACONTROL_ERROR_OUT_OF_MEMORY Out of memory
+ */
+EXPORT_API int datacontrol_remove_data_change_cb(datacontrol_h provider, int callback_id);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _APPFW_DATA_CONTROL_DATA_CHANGE_H__ */
#define _APPFW_DATA_CONTROL_PROVIDER_H_
#include <data-control-types.h>
+#include <data_control_types.h>
+#include <data_control_provider.h>
#include <data-control-sql-cursor.h>
#ifdef __cplusplus
extern "C" {
#endif
-
/**
* @brief Called when the insert request is received from an application using SQL-friendly interface based data control.
*
*/
EXPORT_API int datacontrol_provider_send_map_get_value_result(int request_id, char **value_list, int value_count);
+/**
+ * @brief Send data changed notification to consumer apps which are successfully add data changed callback.
+ * @param[in] provider Target provider handle
+ * @param[in] type Changed data type
+ * @param[in] data Customized data, intend to contains information about changed data
+ *
+ * @return @c 0 on success,
+ * otherwise a negative error value
+ *
+ * @retval #DATACONTROL_ERROR_NONE Successful
+ * @retval #DATACONTROL_ERROR_IO_ERROR I/O error
+ */
+EXPORT_API int datacontrol_provider_send_data_change_noti(
+ datacontrol_h provider,
+ datacontrol_data_change_type_e type,
+ bundle *data);
+
+/**
+ * @brief Add consumer filter for add data changed callback process.
+ * @param[in] callback Consumer filter callback filtering consumers which try to add data changed callback
+ * @param[in] user_data The user data to be passed to the list_cb function
+ * @param[out] callback_id Added callback ID
+ *
+ * @return @c 0 on success,
+ * otherwise a negative error value
+ *
+ * @retval #DATACONTROL_ERROR_NONE Successful
+ * @retval #DATACONTROL_ERROR_IO_ERROR I/O error
+ */
+EXPORT_API int datacontrol_provider_add_data_change_consumer_filter_cb(
+ data_control_provider_data_change_consumer_filter_cb callback,
+ void *user_data,
+ int *callback_id);
+
+/**
+ * @brief Remove consumer filter for add data changed callback process.
+ * @param[in] callback_id Target callback ID
+ *
+ * @return @c 0 on success,
+ * otherwise a negative error value
+ *
+ * @retval #DATACONTROL_ERROR_NONE Successful
+ * @retval #DATACONTROL_ERROR_IO_ERROR I/O error
+ */
+EXPORT_API int datacontrol_provider_remove_data_change_consumer_filter_cb(int callback_id);
+
+/**
+ * @brief Get consumer list which successfully add data changed callback.
+ * @param[in] provider The provider handle
+ * @param[in] list_cb Callback for each consumer info
+ * @param[in] user_data The user data to be passed to the list_cb function
+ *
+ * @return @c 0 on success,
+ * otherwise a negative error value
+ *
+ * @retval #DATACONTROL_ERROR_NONE Successful
+ * @retval #DATACONTROL_ERROR_IO_ERROR I/O error
+ */
+EXPORT_API int datacontrol_provider_foreach_data_change_consumer(
+ datacontrol_h provider,
+ void *list_cb,
+ void *user_data);
+
#ifdef __cplusplus
}
#endif
DATACONTROL_ERROR_MAX_EXCEEDED = -EMSGSIZE /**< Too long argument */
} datacontrol_error_e;
+/**
+ * @brief Enumerations of different type of data control requests.
+ */
+typedef enum
+{
+ DATACONTROL_TYPE_ERROR = -1,
+ DATACONTROL_TYPE_UNDEFINED,
+ DATACONTROL_TYPE_SQL_SELECT,
+ DATACONTROL_TYPE_SQL_INSERT,
+ DATACONTROL_TYPE_SQL_UPDATE,
+ DATACONTROL_TYPE_SQL_DELETE,
+ DATACONTROL_TYPE_MAP_GET,
+ DATACONTROL_TYPE_MAP_SET,
+ DATACONTROL_TYPE_MAP_ADD,
+ DATACONTROL_TYPE_MAP_REMOVE,
+ DATACONTROL_TYPE_ADD_DATA_CHANGED_CB,
+ DATACONTROL_TYPE_REMOVE_DATA_CHANGED_CB,
+ DATACONTROL_TYPE_MAX = 255
+} datacontrol_request_type;
+
+
+/**
+ * @brief Enumerations of the various datacontrol noti type.
+ */
+typedef enum {
+ DATACONTROL_DATA_CHANGE_SQL_UPDATE,
+ DATACONTROL_DATA_CHANGE_SQL_INSERT,
+ DATACONTROL_DATA_CHANGE_SQL_DELETE,
+ DATACONTROL_DATA_CHANGE_MAP_SET,
+ DATACONTROL_DATA_CHANGE_MAP_ADD,
+ DATACONTROL_DATA_CHANGE_MAP_REMOVE,
+ DATACONTROL_DATA_CHANGE_CALLBACK_ADD_RESULT,
+ DATACONTROL_DATA_CHANGE_CALLBACK_REMOVE_RESULT
+} datacontrol_data_change_type_e;
+
+typedef struct datacontrol_s *datacontrol_h;
+
#endif /* _APPFW_DATA_CONTROL_TYPES_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2013 - 2016 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_DATA_CONTROL_NOTI_H__
+#define __TIZEN_APPFW_DATA_CONTROL_NOTI_H__
+
+#include <data_control_types.h>
+#include <bundle.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @file data_control_noti.h
+ * @brief This is the header file for data change notifications of the Data Control module. \n
+ * All callbacks are called in the main loop context, unless stated otherwise.
+ */
+
+/**
+ * @addtogroup CAPI_DATA_CONTROL_CONSUMER_MODULE
+ * @{
+ */
+
+
+/**
+ * @brief Called when received data changed notification from provider application.
+ * @since_tizen 3.0
+ *
+ * @param[in] provider The provider handle. @a provider is valid only inside this function. \n
+ * @a provider should not be freed.
+ * @param[in] type Changed data type.
+ * @param[in] data Data from provider, intended to contain information about changed data \n
+ * @a data is valid only inside this function. \n
+ * To use outside the callback, make a copy. @a data should not be freed.
+ * @param[in] user_data The user data passed from the add function.
+ *
+ * @pre The callback must be registered using data_control_add_data_change_cb(). \n
+ * data_control_provider_send_data_change_noti() must be called to invoke this callback.
+ *
+ * @see data_control_add_data_change_cb()
+ * @see data_control_provider_send_data_change_noti()
+ */
+typedef void (*data_control_data_change_cb) (
+ data_control_h provider,
+ data_control_data_change_type_e type,
+ bundle *data,
+ void *user_data);
+
+/**
+ * @brief Called when the consumer receives the result of the data change callback adding operation.
+ * @details The following error codes can be delivered. \n
+ * #DATA_CONTROL_ERROR_NONE, \n
+ * #DATA_CONTROL_ERROR_OUT_OF_MEMORY, \n
+ * #DATA_CONTROL_ERROR_IO_ERROR, \n
+ * #DATA_CONTROL_ERROR_PERMISSION_DENIED, \n
+ * #DATA_CONTROL_ERROR_MAX_EXCEEDED
+ *
+ * @since_tizen 3.0
+ *
+ * @remarks DATA_CONTROL_ERROR_PERMISSION_DENIED will be returned when the provider denies to add the callback.
+ *
+ * @param[in] provider The provider handle. @a provider is valid only inside this function. \n
+ * @a provider should not be freed.
+ * @param[in] result Add data changed callback result.
+ * @param[in] callback_id Added callback ID.
+ * @param[in] user_data The user data passed from the add function.
+ *
+ * @pre The callback must be registered using data_control_add_data_change_cb().
+ *
+ * @see data_control_add_data_change_cb()
+ */
+typedef void (*data_control_add_callback_result_cb) (
+ data_control_h provider,
+ data_control_error_e result,
+ int callback_id,
+ void *user_data);
+
+/**
+ * @brief Asynchronously adds a data changed callback which is called whenever the provider's data is changed.
+ * @details The function attempts to add the callback asynchronously and calls result_callback to inform about the result of the attempt. \n
+ * If the function is successful, result_callback will be called. \n
+ * The provider will call any filters which were added for the callback addition process.
+ * @since_tizen 3.0
+ * @privlevel public
+ * @privilege %http://tizen.org/privilege/datasharing \n
+ * %http://tizen.org/privilege/appmanager.launch
+ *
+ * @remarks If you want to use this function, you must add privileges.
+ *
+ * @param [in] provider Target provider handle
+ * @param [in] callback The callback function to be called when consumer receive data changed notification
+ * @param [in] user_data The user data to be passed to the callback function
+ * @param [in] result_callback The callback function to be called when consumer receive add data changed callback process result
+ * @param [in] result_cb_user_data The user data to be passed to the result_callback function
+ * @param [out] callback_id Added callback ID, it can be used to remove the callback
+ *
+ * @return @c 0 on success,
+ * otherwise a negative error value
+ *
+ * @retval #DATA_CONTROL_ERROR_NONE Successful
+ * @retval #DATA_CONTROL_ERROR_IO_ERROR I/O error
+ * @retval #DATA_CONTROL_ERROR_OUT_OF_MEMORY Out of memory
+ * @retval #DATA_CONTROL_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #DATA_CONTROL_ERROR_PERMISSION_DENIED Permission denied
+ *
+ * @see data_control_data_change_cb()
+ * @see data_control_add_callback_result_cb()
+ * @see data_control_provider_add_data_change_consumer_filter_cb()
+ *
+ */
+int data_control_add_data_change_cb(
+ data_control_h provider,
+ data_control_data_change_cb callback,
+ void *user_data,
+ data_control_add_callback_result_cb result_callback,
+ void *result_cb_user_data,
+ int *callback_id);
+
+/**
+ * @brief Removes data changed callback function.
+ * @since_tizen 3.0
+ * @privlevel public
+ * @privilege %http://tizen.org/privilege/datasharing \n
+ * %http://tizen.org/privilege/appmanager.launch
+ *
+ * @remarks If you want to use this function, you must add privileges.
+ *
+ * @param [in] provider Target provider handle
+ * @param [in] callback_id Target callback ID
+ *
+ * @return @c 0 on success,
+ * otherwise a negative error value
+ *
+ * @retval #DATA_CONTROL_ERROR_NONE Successful
+ * @retval #DATA_CONTROL_ERROR_IO_ERROR I/O error
+ * @retval #DATA_CONTROL_ERROR_OUT_OF_MEMORY Out of memory
+ * @retval #DATA_CONTROL_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #DATA_CONTROL_ERROR_PERMISSION_DENIED Permission denied
+ */
+int data_control_remove_data_change_cb(data_control_h provider, int callback_id);
+
+
+/**
+* @}
+*/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __TIZEN_APPFW_DATA_CONTROL_NOTI_H__ */
#endif
/**
- * @file data_control_provider.h
- * @brief This is the header file for the data control provider.
+ * @file data_control_provider.h
+ * @brief This is the header file for the data control provider.\n
+ * All callbacks are called in the main loop context, unless stated otherwise.
*/
/**
* @{
*/
+
+/**
+ * @brief Called for each application which successfully added a data change callback.
+ * @since_tizen 3.0
+ *
+ * @param[in] provider The provider handle. @a provider is valid only inside this function. \n
+ * @a provider should not be freed.
+ * @param[in] consumer_appid The id of the consumer application. @a consumer_appid is valid only inside this function. \n
+ * To use outside the callback, make a copy. @a consumer_appid should not be freed.
+ * @param[in] user_data The user data.
+ * @return @c true to continue with the next iteration of the loop,
+ * otherwise @c false to break out of the loop
+ *
+ * @pre data_control_provider_foreach_data_change_consumer() must be called to invoke this callback.
+ * @see data_control_provider_foreach_data_change_consumer()
+ */
+typedef bool (*data_control_provider_data_change_consumer_cb)(
+ data_control_h provider,
+ char *consumer_appid,
+ void *user_data);
+
+/**
+ * @brief Called when a consumer requests a data change callback addition.
+ * @since_tizen 3.0
+ * @details The callback decides - through the return value - whether a consumer application should be allowed to add a data change callback. \n
+ * If it returns true, it means the application should be allowed to do so, if it returns false, it means it should be denied.
+ *
+ * @param[in] provider The provider handle. @a provider is valid only inside this function. \n
+ * @a provider should not be freed.
+ * @param[in] consumer_appid The id of the consumer application which requested to add the callback. @a consumer_appid is valid only inside this function. \n
+ * To use outside the callback, make a copy. @a consumer_appid should not be freed.
+ * @param[in] user_data The user data.
+ * @return @c A boolean value indicating whether the consumer application should be allowed to add data change callbacks.
+ *
+ * @pre The callback must be registered using data_control_provider_add_data_change_consumer_filter_cb(). \n
+ * data_control_add_data_change_cb() must be called to invoke this callback.
+ *
+ * @see data_control_provider_add_data_change_consumer_filter_cb()
+ * @see data_control_provider_remove_data_change_consumer_filter_cb()
+ */
+typedef bool (*data_control_provider_data_change_consumer_filter_cb)(
+ data_control_h provider,
+ char *consumer_appid,
+ void *user_data);
+
/**
* @brief Called when the insert request is received from an application using SQL-friendly interface based data control.
* @since_tizen 2.3
*/
bool data_control_provider_match_data_id(data_control_h provider, const char *data_id);
+/**
+ * @brief Sends a data change notification to consumer applications which have successfully added a data change callback.
+ * @details If the function is successful, data_control_data_change_cb() callback will be called under certain conditions.
+ * @since_tizen 3.0
+ * @privlevel public
+ * @privilege %http://tizen.org/privilege/datasharing
+ *
+ * @param[in] provider Target provider handle
+ * @param[in] type Changed data type
+ * @param[in] data Customized information about changed data
+ *
+ * @return @c 0 on success,
+ * otherwise a negative error value
+ *
+ * @retval #DATA_CONTROL_ERROR_NONE Successful
+ * @retval #DATA_CONTROL_ERROR_IO_ERROR I/O error
+ * @retval #DATA_CONTROL_ERROR_INVALID_PARAMETER Invalid parameter
+ * @retval #DATA_CONTROL_ERROR_PERMISSION_DENIED Permission denied
+ *
+ * @pre The consumer should call data_control_add_data_change_cb() to receive data change notifications,
+ * @see data_control_data_change_cb()
+ * @see data_control_add_data_change_cb()
+ */
+int data_control_provider_send_data_change_noti(
+ data_control_h provider,
+ data_control_data_change_type_e type,
+ bundle *data);
+
+/**
+ * @brief Adds a consumer filter for the data changed callback addition process.
+ * @since_tizen 3.0
+ * @remarks If the provider does not add any filters by calling this function, all requests to add a data change callback will be granted. \n
+ * If filters are added, and at least one filter returns false for a consumer application, the application will not be able to add data change callbacks.
+ *
+ * @param[in] callback Consumer filter callback, filtering consumers which try to add data changed callback
+ * @param[in] user_data The user data to be passed to the list_cb function
+ * @param[out] callback_id Added callback ID
+ *
+ * @return @c 0 on success,
+ * otherwise a negative error value
+ *
+ * @retval #DATA_CONTROL_ERROR_NONE Successful
+ * @retval #DATA_CONTROL_ERROR_IO_ERROR I/O error
+ * @retval #DATA_CONTROL_ERROR_INVALID_PARAMETER Invalid parameter
+ * @see data_control_provider_data_change_consumer_filter_cb()
+ */
+int data_control_provider_add_data_change_consumer_filter_cb(
+ data_control_provider_data_change_consumer_filter_cb callback,
+ void *user_data,
+ int *callback_id);
+
+/**
+ * @brief Removes a consumer filter for the data changed callback addition process.
+ * @since_tizen 3.0
+ *
+ * @param[in] callback_id Target callback ID
+ *
+ * @return @c 0 on success,
+ * otherwise a negative error value
+ *
+ * @retval #DATA_CONTROL_ERROR_NONE Successful
+ * @retval #DATA_CONTROL_ERROR_IO_ERROR I/O error
+ * @retval #DATA_CONTROL_ERROR_INVALID_PARAMETER Invalid parameter
+ */
+int data_control_provider_remove_data_change_consumer_filter_cb(int callback_id);
+
+/**
+ * @brief Retrieves ids of all applications which receive data change notifications from a given provider.
+ * @since_tizen 3.0
+ * @details This function calls data_control_provider_data_change_consumer_cb() once for each provider's notification target consumer id. \n
+ * If the data_control_provider_data_change_consumer_cb() callback function returns @c false, then iteration will be finished.
+ *
+ * @param[in] provider Target provider handle
+ * @param[in] list_cb The iteration callback function
+ * @param[in] user_data The user data to be passed to the list_cb function
+ *
+ * @return @c 0 on success,
+ * otherwise a negative error value
+ *
+ * @retval #DATA_CONTROL_ERROR_NONE Successful
+ * @retval #DATA_CONTROL_ERROR_IO_ERROR I/O error
+ * @retval #DATA_CONTROL_ERROR_INVALID_PARAMETER Invalid parameter
+ * @post This function invokes data_control_provider_data_change_consumer_cb().
+ * @see data_control_provider_data_change_consumer_cb()
+ */
+int data_control_provider_foreach_data_change_consumer(
+ data_control_h provider,
+ data_control_provider_data_change_consumer_cb list_cb,
+ void *user_data);
+
/**
* @}
*/
DATA_CONTROL_ERROR_MAX_EXCEEDED = TIZEN_ERROR_DATA_CONTROL | 0x01 /**< Too long argument */
} data_control_error_e;
+/**
+ * @brief Enumeration for the various data changed notification types.
+ * @since_tizen 3.0
+ */
+typedef enum {
+ DATA_CONTROL_DATA_CHANGE_SQL_UPDATE, /**< Update notification */
+ DATA_CONTROL_DATA_CHANGE_SQL_INSERT, /**< Insert notification */
+ DATA_CONTROL_DATA_CHANGE_SQL_DELETE, /**< Delete notification */
+ DATA_CONTROL_DATA_CHANGE_MAP_SET, /**< Set notification */
+ DATA_CONTROL_DATA_CHANGE_MAP_ADD, /**< Add notification */
+ DATA_CONTROL_DATA_CHANGE_MAP_REMOVE /**< Remove notification */
+} data_control_data_change_type_e;
+
/**
* @}
*/
%files
%{_libdir}/lib%{name}.so.*
%{_libdir}/libcapi-data-control.so.*
+%config %{_sysconfdir}/dbus-1/session.d/data-control.conf
+
%manifest %{name}.manifest
/usr/share/license/%{name}
* limitations under the License.
*/
-#include <dlog.h>
+#include <sqlite3.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <glib.h>
#include <gio/gio.h>
#include <pthread.h>
+#include <sys/socket.h>
+
+#include <dlog.h>
#include <appsvc/appsvc.h>
#include <aul/aul.h>
#include "data-control-sql-cursor.h"
#include "data-control-internal.h"
+#include "data-control-types.h"
#define MAX_COLUMN_SIZE 512
#define MAX_STATEMENT_SIZE 1024
#define ERR_BUFFER_SIZE 1024
#define BUFSIZE 512
+#define DATA_CONTROL_DBUS_PATH_PREFIX "/org/tizen/data_control_service_"
+#define DATA_CONTROL_OBJECT_PATH "/org/tizen/data_control_service"
+#define DATA_CONTROL_INTERFACE_NAME "org.tizen.data_control_service"
+#define DATA_CONTROL_DB_NAME_PREFIX "._data_control_list_"
+#define DATA_CONTROL_DB_NAME "DATA_CONTROL_DATA_CHANGE_TABLE"
+
+static GDBusConnection *_gdbus_conn = NULL;
+
+datacontrol_data_change_type_e _get_internal_noti_type(data_control_data_change_type_e type)
+{
+ datacontrol_data_change_type_e ret_type = DATACONTROL_DATA_CHANGE_SQL_UPDATE;
+ switch (type) {
+ case DATA_CONTROL_DATA_CHANGE_SQL_UPDATE:
+ ret_type = DATACONTROL_DATA_CHANGE_SQL_UPDATE;
+ break;
+ case DATA_CONTROL_DATA_CHANGE_SQL_INSERT:
+ ret_type = DATACONTROL_DATA_CHANGE_SQL_INSERT;
+ break;
+ case DATA_CONTROL_DATA_CHANGE_SQL_DELETE:
+ ret_type = DATACONTROL_DATA_CHANGE_SQL_DELETE;
+ break;
+ case DATA_CONTROL_DATA_CHANGE_MAP_SET:
+ ret_type = DATACONTROL_DATA_CHANGE_MAP_SET;
+ break;
+ case DATA_CONTROL_DATA_CHANGE_MAP_ADD:
+ ret_type = DATACONTROL_DATA_CHANGE_MAP_ADD;
+ break;
+ case DATA_CONTROL_DATA_CHANGE_MAP_REMOVE:
+ ret_type = DATACONTROL_DATA_CHANGE_MAP_REMOVE;
+ break;
+ default:
+ LOGE("Invalid noti type");
+ break;
+ }
+ return ret_type;
+}
+
+data_control_data_change_type_e _get_public_noti_type(datacontrol_data_change_type_e type)
+{
+ data_control_data_change_type_e ret_type = DATA_CONTROL_DATA_CHANGE_SQL_UPDATE;
+ switch(type) {
+ case DATACONTROL_DATA_CHANGE_SQL_UPDATE:
+ ret_type = DATA_CONTROL_DATA_CHANGE_SQL_UPDATE;
+ break;
+ case DATACONTROL_DATA_CHANGE_SQL_INSERT:
+ ret_type = DATA_CONTROL_DATA_CHANGE_SQL_INSERT;
+ break;
+ case DATACONTROL_DATA_CHANGE_SQL_DELETE:
+ ret_type = DATA_CONTROL_DATA_CHANGE_SQL_DELETE;
+ break;
+ case DATACONTROL_DATA_CHANGE_MAP_SET:
+ ret_type = DATA_CONTROL_DATA_CHANGE_MAP_SET;
+ break;
+ case DATACONTROL_DATA_CHANGE_MAP_ADD:
+ ret_type = DATA_CONTROL_DATA_CHANGE_MAP_ADD;
+ break;
+ case DATACONTROL_DATA_CHANGE_MAP_REMOVE:
+ ret_type = DATA_CONTROL_DATA_CHANGE_MAP_REMOVE;
+ break;
+ default:
+ LOGE("Invalid noti type");
+ break;
+ }
+ return ret_type;
+}
int _consumer_request_compare_cb(gconstpointer a, gconstpointer b)
{
return 1;
}
+int _create_datacontrol_h(datacontrol_h *provider)
+{
+ struct datacontrol_s *request;
+
+ if (provider == NULL)
+ return DATACONTROL_ERROR_INVALID_PARAMETER;
+
+ request = malloc(sizeof(struct datacontrol_s));
+ if (request == NULL)
+ return DATACONTROL_ERROR_OUT_OF_MEMORY;
+
+ request->provider_id = NULL;
+ request->data_id = NULL;
+
+ *provider = request;
+
+ return 0;
+}
+
+int _destroy_datacontrol_h(datacontrol_h provider)
+{
+ if (provider == NULL)
+ return DATACONTROL_ERROR_INVALID_PARAMETER;
+
+ if (provider->provider_id != NULL)
+ free(provider->provider_id);
+
+ if (provider->data_id != NULL)
+ free(provider->data_id);
+
+ free(provider);
+ return 0;
+}
+
+int _set_provider_id(datacontrol_h provider, const char *provider_id)
+{
+ if (provider == NULL || provider_id == NULL)
+ return DATACONTROL_ERROR_INVALID_PARAMETER;
+
+ if (provider->provider_id != NULL)
+ free(provider->provider_id);
+
+ provider->provider_id = strdup(provider_id);
+ if (provider->provider_id == NULL)
+ return DATACONTROL_ERROR_OUT_OF_MEMORY;
+
+ return 0;
+}
+
+int _set_data_id(datacontrol_h provider, const char *data_id)
+{
+ if (provider == NULL || data_id == NULL)
+ return DATACONTROL_ERROR_INVALID_PARAMETER;
+
+ if (provider->data_id != NULL)
+ free(provider->data_id);
+
+ provider->data_id = strdup(data_id);
+ if (provider->data_id == NULL)
+ return DATACONTROL_ERROR_OUT_OF_MEMORY;
+
+ return 0;
+}
+
int _write_socket(int fd, void *buffer, unsigned int nbytes,
unsigned int *bytes_write)
{
return id;
}
+int _datacontrol_get_data_changed_callback_id(void)
+{
+ static int id = 0;
+ g_atomic_int_inc(&id);
+
+ return id;
+}
+
+int _datacontrol_get_data_changed_filter_callback_id(void)
+{
+ static int id = 0;
+ g_atomic_int_inc(&id);
+
+ return id;
+}
+
void _socket_info_free(gpointer socket)
{
datacontrol_socket_info *socket_info = (datacontrol_socket_info *)socket;
}
-datacontrol_socket_info *_get_socket_info(const char *caller_id,
- const char *callee_id, const char *type, GIOFunc cb, void *data)
+datacontrol_socket_info *_add_watch_on_socket_info(const char *caller_id, const char *callee_id, const char *type,
+ GIOFunc cb, void *data)
{
char err_buf[ERR_BUFFER_SIZE];
int socketpair = 0;
return DATACONTROL_ERROR_NONE;
}
+char *_get_encoded_db_path()
+{
+ char db_path[PATH_MAX];
+ char *dup_db_path = NULL;
+ char *encoded_appid = NULL;
+ char provider_appid[255];
+ if (aul_app_get_appid_bypid(getpid(), provider_appid, sizeof(provider_appid)) != 0) {
+ LOGE("Failed to get appid by pid");
+ return NULL;
+ }
+
+ encoded_appid = g_compute_checksum_for_string(G_CHECKSUM_MD5, provider_appid, -1);
+ snprintf(db_path, sizeof(db_path), "/run/user/%d/%s%s.db",
+ getuid(), DATA_CONTROL_DB_NAME_PREFIX, encoded_appid);
+ dup_db_path = strdup(db_path);
+ if (dup_db_path == NULL)
+ LOGE("fail to dup db path. out of memory.");
+
+ free(encoded_appid);
+ LOGI("dup db path : %s ", dup_db_path);
+ return dup_db_path;
+}
+
+char *_get_encoded_path(datacontrol_h provider, char *consumer_appid)
+{
+ int prefix_len = strlen(DATA_CONTROL_DBUS_PATH_PREFIX);
+ char *encoded_path;
+ char *full_path;
+ int path_len = strlen(provider->provider_id) + strlen(provider->data_id) + strlen(consumer_appid) + 3;
+ int full_path_len = path_len + prefix_len;
+ char *path = (char *)calloc(path_len, sizeof(char));
+ if (path == NULL) {
+ LOGE("path calloc failed");
+ return 0;
+ }
+
+ snprintf(path, path_len, "%s_%s_%s", provider->provider_id, provider->data_id, consumer_appid);
+ encoded_path = g_compute_checksum_for_string(G_CHECKSUM_MD5, path, -1);
+
+ full_path = (char *)calloc(full_path_len, sizeof(char));
+ snprintf(full_path, full_path_len, "%s%s", DATA_CONTROL_DBUS_PATH_PREFIX, encoded_path);
+
+ free(path);
+ free(encoded_path);
+ LOGI("full path : %s ", full_path);
+ return full_path;
+}
+
+int _dbus_init()
+{
+ int ret = DATACONTROL_ERROR_NONE;
+ GError *error = NULL;
+
+ if (_gdbus_conn == NULL) {
+ _gdbus_conn = g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, &error);
+
+ if (_gdbus_conn == NULL) {
+ if (error != NULL) {
+ LOGE("Failed to get dbus [%s]", error->message);
+ g_error_free(error);
+ }
+ return DATACONTROL_ERROR_IO_ERROR;
+ }
+ ret = DATACONTROL_ERROR_NONE;
+ }
+ return ret;
+}
+
+GDBusConnection *_get_dbus_connection()
+{
+ int result = _dbus_init();
+ if (result != DATACONTROL_ERROR_NONE) {
+ LOGE("Can't init dbus %d", result);
+ return NULL;
+ }
+ return _gdbus_conn;
+}
+
+int _dbus_signal_init(int *monitor_id, char *path, GDBusSignalCallback callback)
+{
+ int id;
+ id = g_dbus_connection_signal_subscribe(
+ _get_dbus_connection(),
+ NULL,
+ DATA_CONTROL_INTERFACE_NAME, /* interface */
+ NULL, /* member */
+ path, /* path */
+ NULL, /* arg0 */
+ G_DBUS_SIGNAL_FLAGS_NONE,
+ callback,
+ NULL,
+ NULL);
+
+ LOGI("subscribe id : %d", id);
+ if (id == 0) {
+ return DATACONTROL_ERROR_IO_ERROR;
+ LOGE("Failed to _register_noti_dbus_interface");
+ } else {
+ *monitor_id = id;
+ }
+
+ return DATACONTROL_ERROR_NONE;
+}
* @brief This is the header file for private keys of the data-control.
*/
#include <gio/gio.h>
+#include "data_control_types.h"
#ifndef _APPFW_DATA_CONTROL_INTERNAL_H_
#define _APPFW_DATA_CONTROL_INTERNAL_H_
#define OSP_K_DATACONTROL_REQUEST_TYPE "__OSP_DATACONTROL_REQUEST_TYPE__"
#define OSP_K_DATACONTROL_PROTOCOL_VERSION "__OSP_DATACONTROL_PROTOCOL_VERSION__"
#define OSP_K_CALLER_TYPE "__OSP_CALLER_TYPE__"
+#define OSP_K_DATACONTROL_UNIQUE_NAME "__OSP_DATACONTROL_UNIQUE_NAME__"
+#define OSP_K_DATA_CHANGED_CALLBACK_ID "__OSP_DATA_CHANGED_CALLBACK_ID__"
#define DATACONTROL_SELECT_STATEMENT "DATACONTROL_SELECT_STATEMENT"
/**
* @brief Enumerations of different type of data control requests.
*/
-typedef enum {
- DATACONTROL_TYPE_ERROR = -1,
- DATACONTROL_TYPE_UNDEFINED,
- DATACONTROL_TYPE_SQL_SELECT,
- DATACONTROL_TYPE_SQL_INSERT,
- DATACONTROL_TYPE_SQL_UPDATE,
- DATACONTROL_TYPE_SQL_DELETE,
- DATACONTROL_TYPE_MAP_GET,
- DATACONTROL_TYPE_MAP_SET,
- DATACONTROL_TYPE_MAP_ADD,
- DATACONTROL_TYPE_MAP_REMOVE,
- DATACONTROL_TYPE_MAX = 255
-} datacontrol_request_type;
typedef struct datacontrol_pkt {
int len;
datacontrol_request_type type;
} datacontrol_consumer_request_info;
+typedef struct datacontrol_consumer {
+ int monitor_id;
+ char *appid;
+ char *object_path;
+ char *unique_id;
+} datacontrol_consumer_info;
+
+struct datacontrol_s {
+ char *provider_id;
+ char *data_id;
+};
+
int _consumer_request_compare_cb(gconstpointer a, gconstpointer b);
int _datacontrol_sql_set_cursor(const char *path);
char *_datacontrol_create_select_statement(char *data_id,
const char *where, const char *order, int page_number,
int count_per_page);
int _datacontrol_create_request_id(void);
-int _datacontrol_send_async(int sockfd, bundle *kb,
- datacontrol_request_type type, void *data);
-int _read_socket(int fd, char *buffer, unsigned int nbytes,
- unsigned int *bytes_read);
-int _write_socket(int fd, void *buffer, unsigned int nbytes,
- unsigned int *bytes_write);
-gboolean _datacontrol_recv_message(GIOChannel *channel, GIOCondition cond,
- gpointer data);
-int _get_gdbus_shared_connection(GDBusConnection **connection,
- char *provider_id);
+int _datacontrol_get_data_changed_callback_id(void);
+int _datacontrol_get_data_changed_filter_callback_id(void);
+
+int _datacontrol_send_async(int sockfd, bundle *kb, datacontrol_request_type type, void *data);
+int _read_socket(int fd, char *buffer, unsigned int nbytes, unsigned int *bytes_read);
+int _write_socket(int fd, void *buffer, unsigned int nbytes, unsigned int *bytes_write);
+
+gboolean _datacontrol_recv_message(GIOChannel *channel, GIOCondition cond, gpointer data);
+int _get_gdbus_shared_connection(GDBusConnection **connection, char *provider_id);
void _socket_info_free(gpointer socket);
-datacontrol_socket_info *_get_socket_info(const char *caller_id,
- const char *callee_id, const char *type, GIOFunc cb,
- void *data);
+datacontrol_socket_info *_add_watch_on_socket_info(const char *caller_id, const char *callee_id, const char *type, GIOFunc cb, void *data);
int _request_appsvc_run(const char *caller_id, const char *callee_id);
+GDBusConnection *_get_dbus_connection();
+int _dbus_signal_init(int *monitor_id, char *path, GDBusSignalCallback callback);
+char *_get_encoded_path(datacontrol_h provider, char *consumer_appid);
+char *_get_encoded_db_path();
+
+int _create_datacontrol_h(datacontrol_h *provider);
+int _destroy_datacontrol_h(datacontrol_h provider);
+int _set_provider_id(datacontrol_h provider, const char *provider_id);
+int _set_data_id(datacontrol_h provider, const char *data_id);
+datacontrol_data_change_type_e _get_internal_noti_type(data_control_data_change_type_e type);
+data_control_data_change_type_e _get_public_noti_type(datacontrol_data_change_type_e type);
+
#endif /* _APPFW_DATA_CONTROL_INTERNAL_H_ */
#include "data-control-map.h"
#include "data-control-internal.h"
-struct datacontrol_s {
- char *provider_id;
- char *data_id;
-};
-
typedef struct {
char *provider_id;
char *app_id;
if (ret != DATACONTROL_ERROR_NONE)
return ret;
- socket_info = _get_socket_info(caller_app_id, app_id, "consumer", __recv_map_message, data);
+ socket_info = _add_watch_on_socket_info(caller_app_id, app_id, "consumer", __recv_map_message, data);
if (socket_info == NULL)
return DATACONTROL_ERROR_IO_ERROR;
--- /dev/null
+/*
+ * Copyright (c) 2016 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 <errno.h>
+#include <search.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <glib.h>
+#include <pthread.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <gio/gio.h>
+#include <sys/socket.h>
+
+#include <dlog.h>
+#include <appsvc/appsvc.h>
+#include <aul/aul.h>
+#include <bundle.h>
+#include <bundle_internal.h>
+#include <pkgmgr-info.h>
+
+#include "data-control-noti.h"
+#include "data-control-internal.h"
+
+typedef struct {
+ GList *cb_list;
+ char *provider_appid;
+ char *provider_id;
+ char *data_id;
+ int monitor_id;
+} provider_info_s;
+
+typedef struct {
+ void *user_data;
+ int callback_id;
+ data_control_data_change_cb changed_cb;
+} changed_cb_info_s;
+
+typedef struct {
+ void *user_data;
+ int callback_id;
+ char *provider_id;
+ char *data_id;
+ int timeout_id;
+ data_control_add_callback_result_cb callback;
+} add_callback_result_cb_info_s;
+
+static GList *__add_callback_result_cb_list = NULL;
+static GList *__changed_provider_list = NULL;
+
+static int __callback_result_info_compare_cb(gconstpointer a, gconstpointer b)
+{
+ add_callback_result_cb_info_s *key1 =
+ (add_callback_result_cb_info_s *)a;
+ add_callback_result_cb_info_s *key2 =
+ (add_callback_result_cb_info_s *)b;
+
+ return !(key1->callback_id == key2->callback_id);
+}
+
+static int __provider_info_compare_cb(gconstpointer a, gconstpointer b)
+{
+ provider_info_s *key1 = (provider_info_s *)a;
+ provider_info_s *key2 = (provider_info_s *)b;
+ return (strcmp(key1->provider_id, key2->provider_id) || strcmp(key1->data_id, key2->data_id));
+}
+
+static int __changed_cb_info_compare_cb(gconstpointer a, gconstpointer b)
+{
+ changed_cb_info_s *key1 = (changed_cb_info_s *)a;
+ changed_cb_info_s *key2 = (changed_cb_info_s *)b;
+
+ return !(key1->callback_id == key2->callback_id);
+}
+
+static void __free_result_cb_info(
+ add_callback_result_cb_info_s *result_cb_info)
+{
+ if (result_cb_info) {
+ if (result_cb_info->provider_id)
+ free(result_cb_info->provider_id);
+ if (result_cb_info->data_id)
+ free(result_cb_info->data_id);
+ if (result_cb_info->timeout_id > 0)
+ g_source_remove(result_cb_info->timeout_id);
+ free(result_cb_info);
+ }
+}
+
+static void __free_provider_info(
+ provider_info_s *provider_info)
+{
+ if (provider_info) {
+ if (provider_info->provider_appid)
+ free(provider_info->provider_appid);
+ if (provider_info->provider_id)
+ free(provider_info->provider_id);
+ if (provider_info->data_id)
+ free(provider_info->data_id);
+ if (provider_info->monitor_id > 0)
+ g_dbus_connection_signal_unsubscribe(_get_dbus_connection(), provider_info->monitor_id);
+ g_list_free(provider_info->cb_list);
+ free(provider_info);
+ }
+}
+
+static void __noti_process(GVariant *parameters)
+{
+ char *provider_id = NULL;
+ char *data_id = NULL;
+ bundle_raw *raw = NULL;
+ bundle *noti_data = NULL;
+ int len = 0;
+ datacontrol_h provider = NULL;
+ GList *find_list;
+ changed_cb_info_s *cb_info = NULL;
+ provider_info_s find_info;
+ provider_info_s *provider_info;
+ GList *callback_list = NULL;
+ datacontrol_data_change_type_e type;
+
+ g_variant_get(parameters, "(i&s&s&si)", &type, &provider_id, &data_id, &raw, &len);
+ LOGI("__noti_process : %d %s %s %d", type, provider_id, data_id, len);
+
+ if (provider_id == NULL || data_id == NULL)
+ return;
+
+ find_info.provider_id = provider_id;
+ find_info.data_id = data_id;
+
+ find_list = g_list_find_custom(__changed_provider_list, &find_info,
+ (GCompareFunc)__provider_info_compare_cb);
+ if (find_list != NULL) {
+ _create_datacontrol_h(&provider);
+ _set_provider_id(provider, provider_id);
+ _set_data_id(provider, data_id);
+ noti_data = bundle_decode(raw, len);
+ provider_info = (provider_info_s *)find_list->data;
+ callback_list = g_list_first(provider_info->cb_list);
+ for (; callback_list != NULL; callback_list = callback_list->next) {
+ cb_info = callback_list->data;
+ cb_info->changed_cb((data_control_h)provider, _get_public_noti_type(type), noti_data, cb_info->user_data);
+ LOGI("callback called: %s, %s", provider_info->provider_id, provider_info->data_id);
+ }
+ bundle_free(noti_data);
+ _destroy_datacontrol_h(provider);
+
+ } else {
+ LOGE("data_control_data_change_cb is null");
+ }
+ LOGI("__noti_process done");
+}
+
+static void __call_result_callback(int callback_id, int callback_result)
+{
+ add_callback_result_cb_info_s *result_cb_info;
+ add_callback_result_cb_info_s find_info;
+ datacontrol_h provider;
+ GList *find_list;
+ find_info.callback_id = callback_id;
+ find_list = g_list_find_custom(__add_callback_result_cb_list, &find_info,
+ (GCompareFunc)__callback_result_info_compare_cb);
+
+ if (find_list != NULL) {
+ result_cb_info = (add_callback_result_cb_info_s *)find_list->data;
+ _create_datacontrol_h(&provider);
+ _set_provider_id(provider, result_cb_info->provider_id);
+ _set_data_id(provider, result_cb_info->data_id);
+ result_cb_info->callback(
+ (data_control_h)provider,
+ callback_result,
+ callback_id,
+ result_cb_info->user_data);
+ __add_callback_result_cb_list = g_list_remove_link(__add_callback_result_cb_list, find_list);
+ __free_result_cb_info((add_callback_result_cb_info_s *)find_list->data);
+ _destroy_datacontrol_h(provider);
+ } else {
+ LOGE("add_callback_result_cb_info_s is null");
+ }
+}
+
+static gboolean __add_callback_result_timeout_handler(gpointer user_data)
+{
+ LOGE("add data changed callback time out !!!");
+ add_callback_result_cb_info_s *result_cb_info =
+ (add_callback_result_cb_info_s *)user_data;
+ __call_result_callback(result_cb_info->callback_id, DATA_CONTROL_ERROR_IO_ERROR);
+ return FALSE;
+}
+
+static void __noti_add_remove_data_changed_cb_result_process(
+ GVariant *parameters)
+{
+ int callback_id;
+ data_control_error_e callback_result;
+ datacontrol_data_change_type_e type;
+
+ g_variant_get(parameters, "(iii)", &type, &callback_id, &callback_result);
+ LOGI("__noti_add_remove_data_changed_cb_result_process: %d %d %d", type, callback_id, callback_result);
+
+ if (type == DATACONTROL_DATA_CHANGE_CALLBACK_ADD_RESULT)
+ __call_result_callback(callback_id, callback_result);
+}
+
+static void __handle_noti(GDBusConnection *connection,
+ const gchar *sender_name,
+ const gchar *object_path,
+ const gchar *interface_name,
+ const gchar *signal_name,
+ GVariant *parameters,
+ gpointer user_data)
+{
+ LOGI("signal_name: %s", signal_name);
+
+ if (g_strcmp0(signal_name, "noti_data_changed") == 0)
+ __noti_process(parameters);
+ else if (g_strcmp0(signal_name, "noti_add_remove_result") == 0)
+ __noti_add_remove_data_changed_cb_result_process(parameters);
+}
+
+static int __noti_request_appsvc_run(const char *callee_id,
+ char *provider_id, char *data_id, const char *unique_id, int callback_id, int request_type)
+{
+ int pid = -1;
+ char callback_id_str[32] = {0,};
+ char request_type_str[MAX_LEN_DATACONTROL_REQ_TYPE] = {0,};
+ bundle *arg_list = bundle_create();
+ if (!arg_list) {
+ LOGE("unable to create bundle: %d", errno);
+ return DATACONTROL_ERROR_OUT_OF_MEMORY;
+ }
+
+ snprintf(callback_id_str, 32, "%d", callback_id);
+ snprintf(request_type_str, MAX_LEN_DATACONTROL_REQ_TYPE, "%d", (int)(request_type));
+
+ appsvc_set_operation(arg_list, APPSVC_OPERATION_DEFAULT);
+ appsvc_set_appid(arg_list, callee_id);
+
+ bundle_add_str(arg_list, OSP_K_DATACONTROL_REQUEST_TYPE, request_type_str);
+ bundle_add_str(arg_list, OSP_K_DATACONTROL_UNIQUE_NAME, unique_id);
+ bundle_add_str(arg_list, OSP_K_DATACONTROL_PROVIDER, provider_id);
+ bundle_add_str(arg_list, OSP_K_DATACONTROL_DATA, data_id);
+ bundle_add_str(arg_list, OSP_K_DATA_CHANGED_CALLBACK_ID, callback_id_str);
+
+ bundle_add_str(arg_list, OSP_K_CALLER_TYPE, OSP_V_CALLER_TYPE_OSP);
+ bundle_add_str(arg_list, OSP_K_LAUNCH_TYPE, OSP_V_LAUNCH_TYPE_DATACONTROL);
+ bundle_add_str(arg_list, AUL_K_CALLEE_APPID, callee_id);
+ bundle_add_str(arg_list, AUL_K_NO_CANCEL, "1");
+ LOGI("callee_id %s", callee_id);
+ LOGI("provider_id %s, data_id %s", provider_id, data_id);
+
+ /* For DataControl CAPI */
+ bundle_add_str(arg_list, AUL_K_DATA_CONTROL_TYPE, "CORE");
+
+ pid = appsvc_run_service(arg_list, 0, NULL, NULL);
+ if (pid >= 0) {
+ LOGI("Launch the provider app successfully: %d", pid);
+ bundle_free(arg_list);
+ } else if (pid == APPSVC_RET_EINVAL) {
+ LOGE("not able to launch service: %d", pid);
+ bundle_free(arg_list);
+ return DATACONTROL_ERROR_INVALID_PARAMETER;
+ } else {
+ LOGE("unable to launch service: %d", pid);
+ return DATACONTROL_ERROR_IO_ERROR;
+ }
+ return DATACONTROL_ERROR_NONE;
+}
+
+int datacontrol_add_data_change_cb(datacontrol_h provider,
+ data_control_data_change_cb callback,
+ void *user_data,
+ data_control_add_callback_result_cb result_callback,
+ void *result_cb_user_data,
+ int *callback_id)
+{
+ int ret = DATACONTROL_ERROR_NONE;
+ changed_cb_info_s *cb_info = NULL;
+ provider_info_s *provider_info = NULL;
+ provider_info_s find_provider_info;
+ int monitor_id = 0;
+ GList *find_list;
+ char *path = NULL;
+ const char *unique_id = NULL;
+ char caller_app_id[255];
+ pid_t pid;
+ add_callback_result_cb_info_s *result_cb_info = NULL;
+ char *provider_app_id = NULL;
+ char *access = NULL;
+
+ LOGI("provider_id : %s, data_id : %s", provider->provider_id, provider->data_id);
+ ret = pkgmgrinfo_appinfo_usr_get_datacontrol_info(provider->provider_id, "Sql", getuid(), &provider_app_id, &access);
+ if (ret != PMINFO_R_OK) {
+ LOGE("unable to get sql data control information, retry with map: %d", ret);
+ ret = pkgmgrinfo_appinfo_usr_get_datacontrol_info(provider->provider_id, "Map", getuid(), &provider_app_id, &access);
+ if (ret != PMINFO_R_OK) {
+ LOGE("unable to get map data control information: %d", ret);
+ return DATACONTROL_ERROR_IO_ERROR;
+ }
+ }
+
+ *callback_id = _datacontrol_get_data_changed_callback_id();
+ unique_id = g_dbus_connection_get_unique_name(_get_dbus_connection());
+ LOGI("unique_id : %s, callback_id %d", unique_id, *callback_id);
+
+ find_provider_info.provider_id = provider->provider_id;
+ find_provider_info.data_id = provider->data_id;
+ find_list = g_list_find_custom(__changed_provider_list, &find_provider_info,
+ (GCompareFunc)__provider_info_compare_cb);
+ if (find_list == NULL) {
+
+ pid = getpid();
+ if (aul_app_get_appid_bypid(pid, caller_app_id, sizeof(caller_app_id)) != 0) {
+ LOGE("Failed to get appid by pid(%d).", pid);
+ ret = DATACONTROL_ERROR_INVALID_PARAMETER;
+ goto err;
+ }
+ path = _get_encoded_path(provider, caller_app_id);
+ if (path == NULL) {
+ LOGE("cannot get encoded path. out of memory.");
+ ret = DATACONTROL_ERROR_OUT_OF_MEMORY;
+ goto err;
+ }
+
+ ret = _dbus_signal_init(&monitor_id, path, __handle_noti);
+ if (ret != DATACONTROL_ERROR_NONE) {
+ LOGE("fail to init dbus signal.");
+ ret = DATACONTROL_ERROR_IO_ERROR;
+ goto err;
+ }
+
+ provider_info = (provider_info_s *)calloc(1, sizeof(provider_info_s));
+ if (provider_info == NULL) {
+ LOGE("provider_info_s alloc fail out of memory.");
+ ret = DATACONTROL_ERROR_OUT_OF_MEMORY;
+ goto err;
+ }
+ provider_info->provider_appid = strdup(provider_app_id);
+ provider_info->provider_id = strdup(provider->provider_id);
+ provider_info->data_id = strdup(provider->data_id);
+ provider_info->monitor_id = monitor_id;
+ if (provider_info->provider_id == NULL || provider_info->data_id == NULL) {
+ LOGE("data_id alloc fail out of memory.");
+ ret = DATACONTROL_ERROR_OUT_OF_MEMORY;
+ __free_provider_info(provider_info);
+ goto err;
+ }
+ __changed_provider_list = g_list_append(__changed_provider_list, provider_info);
+ } else {
+ provider_info = (provider_info_s *)find_list->data;
+ }
+
+ ret = __noti_request_appsvc_run(
+ provider_app_id,
+ provider_info->provider_id,
+ provider_info->data_id,
+ unique_id,
+ *callback_id,
+ DATACONTROL_TYPE_ADD_DATA_CHANGED_CB);
+ if (ret != DATACONTROL_ERROR_NONE) {
+ LOGE("__noti_request_appsvc_run error !!!");
+ goto err;
+ }
+
+ cb_info = (changed_cb_info_s *)calloc(1,
+ sizeof(changed_cb_info_s));
+ if (cb_info == NULL) {
+ LOGE("changed_cb_info_s alloc fail out of memory.");
+ ret = DATACONTROL_ERROR_OUT_OF_MEMORY;
+ goto err;
+ }
+
+ cb_info->changed_cb = callback;
+ cb_info->user_data = user_data;
+ cb_info->callback_id = *callback_id;
+ provider_info->cb_list = g_list_append(
+ provider_info->cb_list, cb_info);
+
+ result_cb_info =
+ (add_callback_result_cb_info_s *)calloc(1, sizeof(add_callback_result_cb_info_s));
+ result_cb_info->callback_id = *callback_id;
+ result_cb_info->callback = result_callback;
+ result_cb_info->user_data = result_cb_user_data;
+ result_cb_info->provider_id = strdup(provider_info->provider_id);
+ result_cb_info->data_id = strdup(provider_info->data_id);
+ result_cb_info->timeout_id = g_timeout_add(5000, __add_callback_result_timeout_handler, result_cb_info);
+ __add_callback_result_cb_list = g_list_append(__add_callback_result_cb_list, result_cb_info);
+ LOGI("datacontrol_add_data_change_cb done");
+
+ return ret;
+err:
+ if (access)
+ free(access);
+ if (provider_app_id)
+ free(provider_app_id);
+ if (path)
+ free(path);
+ if (monitor_id > 0)
+ g_dbus_connection_signal_unsubscribe(_get_dbus_connection(), monitor_id);
+
+ return ret;
+}
+
+int datacontrol_remove_data_change_cb(datacontrol_h provider, int callback_id)
+{
+ changed_cb_info_s *removed_cb_info = NULL;
+ changed_cb_info_s cb_info;
+ provider_info_s info;
+ provider_info_s *target_provider_info;
+ add_callback_result_cb_info_s result_cb_info;
+ GList *provider_list = NULL;
+ GList *callback_list = NULL;
+ GList *result_cb_list = NULL;
+ const char *unique_id = NULL;
+ int ret = DATACONTROL_ERROR_NONE;
+
+ info.provider_id = provider->provider_id;
+ info.data_id = provider->data_id;
+
+ provider_list = g_list_find_custom(__changed_provider_list, &info,
+ (GCompareFunc)__provider_info_compare_cb);
+ if (provider_list == NULL) {
+ LOGE("Cannot find provider info");
+ return DATACONTROL_ERROR_INVALID_PARAMETER;
+ }
+
+ cb_info.callback_id = callback_id;
+ target_provider_info = (provider_info_s *)provider_list->data;
+ callback_list = g_list_find_custom(
+ target_provider_info->cb_list,
+ &cb_info,
+ (GCompareFunc)__changed_cb_info_compare_cb);
+ if (callback_list == NULL) {
+ LOGE("Cannot find changed callback info");
+ return DATACONTROL_ERROR_INVALID_PARAMETER;
+ }
+
+ removed_cb_info = (changed_cb_info_s *)callback_list->data;
+ target_provider_info->cb_list
+ = g_list_remove(target_provider_info->cb_list, removed_cb_info);
+
+ if (g_list_length(target_provider_info->cb_list) == 0) {
+ unique_id = g_dbus_connection_get_unique_name(_get_dbus_connection());
+ LOGI("unique_id : %s", unique_id);
+ ret = __noti_request_appsvc_run(
+ target_provider_info->provider_appid,
+ target_provider_info->provider_id,
+ target_provider_info->data_id,
+ unique_id,
+ callback_id,
+ DATACONTROL_TYPE_REMOVE_DATA_CHANGED_CB);
+ if (ret != DATACONTROL_ERROR_NONE) {
+ LOGE("__sql_request_provider fail %d", ret);
+ return ret;
+ }
+
+ __changed_provider_list = g_list_remove_link(__changed_provider_list, provider_list);
+ __free_provider_info((provider_info_s *)provider_list->data);
+ }
+
+ result_cb_info.callback_id = callback_id;
+ result_cb_list = g_list_find_custom(__add_callback_result_cb_list, &result_cb_info,
+ (GCompareFunc)__callback_result_info_compare_cb);
+ if (result_cb_list) {
+ __add_callback_result_cb_list = g_list_remove_link(__add_callback_result_cb_list, result_cb_list);
+ __free_result_cb_info((add_callback_result_cb_info_s *)result_cb_list->data);
+ }
+ return DATACONTROL_ERROR_NONE;
+}
* limitations under the License.
*/
-#include <dlog.h>
#include <errno.h>
#include <search.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/socket.h>
+#include <dlog.h>
#include <appsvc/appsvc.h>
#include <aul/aul.h>
#include <bundle.h>
#include "data-control-provider.h"
#include "data-control-internal.h"
-#define ROW_ID_SIZE 32
-#define RESULT_PATH_MAX 512
+#define QUERY_MAXLEN 4096
+#define ROW_ID_SIZE 32
+#define RESULT_PATH_MAX 512
#define RESULT_PAGE_NUMBER "RESULT_PAGE_NUMBER"
#define MAX_COUNT_PER_PAGE "MAX_COUNT_PER_PAGE"
#define PACKET_INDEX_MAP_PAGE_NO 2
#define PACKET_INDEX_MAP_COUNT_PER_PAGE 3
+#define DATA_CONTROL_BUS_NAME "org.tizen.data_control_service"
+#define DATA_CONTROL_OBJECT_PATH "/org/tizen/data_control_service"
+#define DATA_CONTROL_INTERFACE_NAME "org.tizen.data_control_service"
+#define DATA_CONTROL_DATA_CHANGE_DATA_CHANGED "noti_data_changed"
+#define DATA_CONTROL_DATA_CHANGE_ADD_REMOVE_RESULT "noti_add_remove_result"
+
static GHashTable *__request_table = NULL;
static GHashTable *__socket_pair_hash = NULL;
+static sqlite3 *__provider_db = NULL;
-/* static pthread_mutex_t provider_lock = PTHREAD_MUTEX_INITIALIZER; */
-
-struct datacontrol_s {
- char *provider_id;
- char *data_id;
-};
+void *provider_sql_user_data;
+void *provider_map_user_data;
+/* static pthread_mutex_t provider_lock = PTHREAD_MUTEX_INITIALIZER; */
+typedef int (*provider_handler_cb) (bundle *b, int request_id, void *data);
-typedef int (*provider_handler_cb)(bundle *b, int request_id, void *data);
+typedef struct {
+ void *user_data;
+ int callback_id;
+ data_control_provider_data_change_consumer_filter_cb callback;
+} changed_noti_consumer_filter_info_s;
static datacontrol_provider_sql_cb *provider_sql_cb = NULL;
static datacontrol_provider_map_cb *provider_map_cb = NULL;
-static void *provider_map_user_data = NULL;
-static void *provider_sql_user_data = NULL;
+
+static GList *__noti_consumer_app_list = NULL;
+static GList *__noti_consumer_filter_info_list = NULL;
+static int __create_consumer_list_db();
+static int __delete_consumer_list_db_info(char *object_path);
+
+static int __data_changed_filter_cb_info_compare_cb(gconstpointer a, gconstpointer b)
+{
+ changed_noti_consumer_filter_info_s *key1 = (changed_noti_consumer_filter_info_s *)a;
+ changed_noti_consumer_filter_info_s *key2 = (changed_noti_consumer_filter_info_s *)b;
+
+ return !(key1->callback_id == key2->callback_id);
+}
+
+static int __noti_consumer_app_list_compare_cb(gconstpointer a, gconstpointer b)
+{
+ datacontrol_consumer_info *info_a = (datacontrol_consumer_info *)a;
+ datacontrol_consumer_info *info_b = (datacontrol_consumer_info *)b;
+
+ return strcmp(info_a->unique_id, info_b->unique_id);
+}
+
+static void __free_consumer_info(const gchar *name)
+{
+ datacontrol_consumer_info find_key;
+ datacontrol_consumer_info *info;
+ GList *find_list = NULL;
+ int result;
+
+ find_key.unique_id = (char *)name;
+ find_list = g_list_find_custom(__noti_consumer_app_list, &find_key,
+ (GCompareFunc)__noti_consumer_app_list_compare_cb);
+ if (find_list == NULL) {
+ LOGI("__free_consumer_info %s not exist", name);
+ return;
+ }
+
+ info = (datacontrol_consumer_info *)find_list->data;
+ result = __delete_consumer_list_db_info(info->object_path);
+ if (result != DATACONTROL_ERROR_NONE)
+ LOGE("__delete_consumer_list_db_info fail %d", result);
+
+ if (info->appid)
+ free(info->appid);
+ if (info->object_path)
+ free(info->object_path);
+ if (info->unique_id)
+ free(info->unique_id);
+ g_bus_unwatch_name(info->monitor_id);
+
+ __noti_consumer_app_list = g_list_remove(__noti_consumer_app_list, find_list->data);
+ LOGI("__free_consumer_info done");
+}
static void __free_data(gpointer data)
{
static void __initialize_provider(void)
{
+ int result;
__request_table = g_hash_table_new_full(g_int_hash, g_int_equal, __free_data, __free_data);
__socket_pair_hash = g_hash_table_new_full(g_str_hash, g_str_equal, free, _socket_info_free);
+ result = __create_consumer_list_db();
+ if (result != DATACONTROL_ERROR_NONE)
+ LOGE("fail to create consumer list db");
}
static int __provider_new_request_id(void)
break;
}
+ case DATACONTROL_TYPE_ADD_DATA_CHANGED_CB:
+ {
+ const char *list[2];
+ char result_str[2] = {0,};
+ bool result = *(bool *)data;
+ snprintf(result_str, 2, "%d", result);
+
+ list[PACKET_INDEX_REQUEST_RESULT] = result_str; /* request result */
+ list[PACKET_INDEX_ERROR_MSG] = DATACONTROL_EMPTY;
+
+ bundle_add_str_array(res, OSP_K_ARG, list, 2);
+ break;
+ }
case DATACONTROL_TYPE_UNDEFINED: /* DATACONTROL_TYPE_MAP_SET || ADD || REMOVE */
{
const char *list[2];
return ret;
}
+static int __insert_consumer_list_db_info(char *app_id, char *provider_id, char *data_id, char *unique_id, char *object_path)
+{
+ int r;
+ int result = DATACONTROL_ERROR_NONE;
+ char query[QUERY_MAXLEN];
+ sqlite3_stmt *stmt = NULL;
+ sqlite3_snprintf(QUERY_MAXLEN, query,
+ "INSERT OR REPLACE INTO data_control_consumer_path_list(app_id, provider_id, data_id, unique_id, object_path)" \
+ "VALUES (?,?,?,?,?)");
+ LOGI("consumer list db insert sql : %s", query);
+ r = sqlite3_prepare(__provider_db, query, sizeof(query), &stmt, NULL);
+ if (r != SQLITE_OK) {
+ LOGE("sqlite3_prepare error(%d , %d, %s)", r, sqlite3_extended_errcode(__provider_db), sqlite3_errmsg(__provider_db));
+ result = DATACONTROL_ERROR_IO_ERROR;
+ goto out;
+ }
+
+ r = sqlite3_bind_text(stmt, 1, app_id, strlen(app_id), SQLITE_STATIC);
+ if (r != SQLITE_OK) {
+ LOGE("app_id bind error(%d) \n", r);
+ result = DATACONTROL_ERROR_IO_ERROR;
+ goto out;
+ }
+
+ r = sqlite3_bind_text(stmt, 2, provider_id, strlen(provider_id), SQLITE_STATIC);
+ if (r != SQLITE_OK) {
+ LOGE("provider_id bind error(%d) \n", r);
+ result = DATACONTROL_ERROR_IO_ERROR;
+ goto out;
+ }
+
+ r = sqlite3_bind_text(stmt, 3, data_id, strlen(data_id), SQLITE_STATIC);
+ if (r != SQLITE_OK) {
+ LOGE("data_id bind error(%d) \n", r);
+ result = DATACONTROL_ERROR_IO_ERROR;
+ goto out;
+ }
+
+ r = sqlite3_bind_text(stmt, 4, unique_id, strlen(unique_id), SQLITE_STATIC);
+ if (r != SQLITE_OK) {
+ LOGE("unique_id bind error(%d) \n", r);
+ result = DATACONTROL_ERROR_IO_ERROR;
+ goto out;
+ }
+
+ r = sqlite3_bind_text(stmt, 5, object_path, strlen(object_path), SQLITE_STATIC);
+ if (r != SQLITE_OK) {
+ LOGE("object_path bind error(%d) \n", r);
+ result = DATACONTROL_ERROR_IO_ERROR;
+ goto out;
+ }
+
+ r = sqlite3_step(stmt);
+ if (r != SQLITE_DONE) {
+ LOGE("step error(%d) \n", r);
+ LOGE("sqlite3_step error(%d, %s)",
+ sqlite3_extended_errcode(__provider_db),
+ sqlite3_errmsg(__provider_db));
+ result = DATACONTROL_ERROR_IO_ERROR;
+ goto out;
+ }
+
+out :
+ if (stmt)
+ sqlite3_finalize(stmt);
+
+ return result;
+}
+
+static int __delete_consumer_list_db_info(char *object_path)
+{
+ int r;
+ char query[QUERY_MAXLEN];
+ int result = DATACONTROL_ERROR_NONE;
+ sqlite3_stmt *stmt = NULL;
+ sqlite3_snprintf(QUERY_MAXLEN, query,
+ "DELETE FROM data_control_consumer_path_list WHERE object_path = ?");
+ LOGI("consumer list db DELETE : %s, object_path : %s", query, object_path);
+ r = sqlite3_prepare(__provider_db, query, sizeof(query), &stmt, NULL);
+ if (r != SQLITE_OK) {
+ LOGE("sqlite3_prepare error(%d , %d, %s)", r,
+ sqlite3_extended_errcode(__provider_db), sqlite3_errmsg(__provider_db));
+ result = DATACONTROL_ERROR_IO_ERROR;
+ goto out;
+ }
+
+ r = sqlite3_bind_text(stmt, 1, object_path, strlen(object_path), SQLITE_STATIC);
+ if (r != SQLITE_OK) {
+ LOGE("caller bind error(%d) \n", r);
+ result = DATACONTROL_ERROR_IO_ERROR;
+ goto out;
+ }
+
+ r = sqlite3_step(stmt);
+ if (r != SQLITE_DONE) {
+ LOGE("step error(%d) \n", r);
+ result = DATACONTROL_ERROR_IO_ERROR;
+ goto out;
+ }
+
+out :
+ if (stmt)
+ sqlite3_finalize(stmt);
+
+ LOGI("__delete_consumer_list_db_info done");
+ return result;
+}
+
+static void __on_name_appeared(GDBusConnection *connection,
+ const gchar *name,
+ const gchar *name_owner,
+ gpointer user_data)
+{
+ LOGI("name appeared : %s", name);
+}
+
+static void __on_name_vanished(GDBusConnection *connection,
+ const gchar *name,
+ gpointer user_data)
+{
+ LOGI("name vanished : %s", name);
+ __free_consumer_info(name);
+}
+
+static int __init_changed_noti_consumer_list()
+{
+ char *app_id = NULL;
+ char *unique_id = NULL;
+ char *object_path = NULL;
+ int ret = DATACONTROL_ERROR_NONE;
+ sqlite3_stmt *stmt = NULL;
+ char query[QUERY_MAXLEN];
+ datacontrol_consumer_info *consumer_info = NULL;
+
+ sqlite3_snprintf(QUERY_MAXLEN, query,
+ "SELECT app_id, object_path, unique_id " \
+ "FROM data_control_consumer_path_list");
+
+ LOGI("__init_changed_noti_consumer_list query : %s", query);
+ ret = sqlite3_prepare_v2(__provider_db, query, -1, &stmt, NULL);
+ if (ret != SQLITE_OK) {
+ LOGE("prepare stmt fail");
+ return DATACONTROL_ERROR_IO_ERROR;
+ }
+
+ while (SQLITE_ROW == sqlite3_step(stmt)) {
+ app_id = (char *)sqlite3_column_text(stmt, 0);
+ if (!app_id) {
+ LOGE("Failed to get package name\n");
+ continue;
+ }
+
+ object_path = (char *)sqlite3_column_text(stmt, 1);
+ if (!object_path) {
+ LOGE("Failed to get object_path\n");
+ continue;
+ }
+
+ unique_id = (char *)sqlite3_column_text(stmt, 2);
+ if (!unique_id) {
+ LOGE("Failed to get unique_id\n");
+ continue;
+ }
+ LOGI("sql : app_id : %s, object_path : %s, unique_id : %s",
+ app_id, object_path, unique_id);
+
+ consumer_info = (datacontrol_consumer_info *)
+ calloc(1, sizeof(datacontrol_consumer_info));
+ consumer_info->appid = strdup(app_id);
+ consumer_info->object_path = strdup(object_path);
+ consumer_info->unique_id = strdup(unique_id);
+
+ consumer_info->monitor_id = g_bus_watch_name_on_connection(
+ _get_dbus_connection(),
+ consumer_info->unique_id,
+ G_BUS_NAME_WATCHER_FLAGS_NONE,
+ __on_name_appeared,
+ __on_name_vanished,
+ consumer_info,
+ NULL);
+
+ LOGI("noti consumer_app_list append %s", consumer_info->object_path);
+ __noti_consumer_app_list =
+ g_list_append(__noti_consumer_app_list, consumer_info);
+ }
+ sqlite3_reset(stmt);
+ sqlite3_finalize(stmt);
+
+ return ret;
+}
+
+static int __create_consumer_list_db()
+{
+ char *db_path = NULL;
+ int ret = SQLITE_OK;
+ int open_flags = (SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE);
+ char *table_command =
+ "CREATE TABLE IF NOT EXISTS data_control_consumer_path_list" \
+ "(app_id TEXT NOT NULL, provider_id TEXT NOT NULL, data_id TEXT NOT NULL, " \
+ "unique_id TEXT NOT NULL, object_path TEXT NOT NULL, " \
+ "PRIMARY KEY(object_path))";
+
+ if (__provider_db == NULL) {
+ db_path = _get_encoded_db_path();
+ if (db_path == NULL)
+ return DATACONTROL_ERROR_IO_ERROR;
+ LOGI("data-control provider db path : %s", db_path);
+
+ ret = sqlite3_open_v2(db_path, &__provider_db, open_flags, NULL);
+ free(db_path);
+ if (ret != SQLITE_OK) {
+ LOGE("database creation failed with error: %d", ret);
+ return DATACONTROL_ERROR_IO_ERROR;
+ }
+ ret = sqlite3_exec(__provider_db, table_command, NULL, NULL, NULL);
+ if (ret != SQLITE_OK) {
+ LOGE("database table creation failed with error: %d", ret);
+ return DATACONTROL_ERROR_IO_ERROR;
+ }
+ ret = __init_changed_noti_consumer_list();
+ if (ret != DATACONTROL_ERROR_NONE) {
+ LOGE("__init_changed_noti_consumer_list fail %d", ret);
+ return ret;
+ }
+ }
+ return DATACONTROL_ERROR_NONE;
+}
+
+static int __set_consumer_app_list(
+ char *caller,
+ char *object_path,
+ char *unique_id)
+{
+ datacontrol_consumer_info find_key;
+ datacontrol_consumer_info *consumer_info;
+ GList *find_list = NULL;
+ int ret = DATACONTROL_ERROR_NONE;
+ LOGI("set consumer_app_list caller : %s, path : %s, unique_id : %s",
+ caller, object_path, unique_id);
+
+ find_key.unique_id = unique_id;
+ find_list = g_list_find_custom(__noti_consumer_app_list,
+ &find_key,
+ (GCompareFunc)__noti_consumer_app_list_compare_cb);
+
+ if (!find_list) {
+ consumer_info = (datacontrol_consumer_info *)
+ calloc(1, sizeof(datacontrol_consumer_info));
+ consumer_info->appid = strdup(caller);
+ consumer_info->object_path = strdup(object_path);
+ consumer_info->unique_id = strdup(unique_id);
+
+ consumer_info->monitor_id = g_bus_watch_name_on_connection(
+ _get_dbus_connection(),
+ consumer_info->unique_id,
+ G_BUS_NAME_WATCHER_FLAGS_NONE,
+ __on_name_appeared,
+ __on_name_vanished,
+ consumer_info,
+ NULL);
+ if (consumer_info->monitor_id == 0) {
+ LOGE("g_bus_watch_name_on_connection fail");
+
+ free(consumer_info->appid);
+ free(consumer_info->object_path);
+ free(consumer_info->unique_id);
+ free(consumer_info);
+
+ return DATACONTROL_ERROR_IO_ERROR;
+ }
+ LOGI("new noti consumer_app_list append %s", consumer_info->object_path);
+ __noti_consumer_app_list = g_list_append(__noti_consumer_app_list, consumer_info);
+ }
+ return ret;
+}
int __provider_process(bundle *b, int fd)
{
}
} else {
- LOGE("Invalid requeste type");
+ LOGE("Invalid request type");
return DATACONTROL_ERROR_INVALID_PARAMETER;
}
return FALSE;
}
+static int __send_add_callback_result(
+ datacontrol_data_change_type_e result_type,
+ char *unique_id,
+ char *path,
+ int callback_id,
+ int callback_result)
+{
+ GError *err = NULL;
+ int result = DATACONTROL_ERROR_NONE;
+ gboolean signal_result = TRUE;
+ LOGI("add callback_result type : %d, callback_id : %d, result : %d",
+ result_type, callback_id, callback_result);
+
+ signal_result = g_dbus_connection_emit_signal(
+ _get_dbus_connection(),
+ unique_id,
+ path,
+ DATA_CONTROL_INTERFACE_NAME,
+ DATA_CONTROL_DATA_CHANGE_ADD_REMOVE_RESULT,
+ g_variant_new("(iii)",
+ result_type,
+ callback_id,
+ callback_result), &err);
+ if (signal_result == FALSE) {
+ LOGE("g_dbus_connection_emit_signal() is failed");
+ if (err != NULL) {
+ LOGE("g_dbus_connection_emit_signal() err : %s",
+ err->message);
+ g_error_free(err);
+ }
+ return DATACONTROL_ERROR_IO_ERROR;
+ }
+
+ LOGI("Send __send_add_callback_result done %d", result);
+ return result;
+}
+
+
+static int __get_sender_pid(const char *sender_name)
+{
+ GDBusMessage *msg = NULL;
+ GDBusMessage *reply = NULL;
+ GError *err = NULL;
+ GVariant *body;
+ int pid = 0;
+
+ msg = g_dbus_message_new_method_call("org.freedesktop.DBus", "/org/freedesktop/DBus",
+ "org.freedesktop.DBus", "GetConnectionUnixProcessID");
+ if (!msg) {
+ LOGE("Can't allocate new method call");
+ goto out;
+ }
+
+ g_dbus_message_set_body(msg, g_variant_new("(s)", sender_name));
+ reply = g_dbus_connection_send_message_with_reply_sync(_get_dbus_connection(), msg,
+ G_DBUS_SEND_MESSAGE_FLAGS_NONE, -1, NULL, NULL, &err);
+
+ if (!reply) {
+ if (err != NULL) {
+ LOGE("Failed to get pid [%s]", err->message);
+ g_error_free(err);
+ }
+ goto out;
+ }
+
+ body = g_dbus_message_get_body(reply);
+ g_variant_get(body, "(u)", &pid);
+
+out:
+ if (msg)
+ g_object_unref(msg);
+ if (reply)
+ g_object_unref(reply);
+
+ return pid;
+}
+
+static int __provider_noti_process(bundle *b, datacontrol_request_type type)
+{
+ datacontrol_h provider = NULL;
+ bool noti_allow = true;
+ char *path = NULL;
+ int result = DATACONTROL_ERROR_NONE;
+ char *unique_id = NULL;
+ datacontrol_data_change_type_e result_type = DATACONTROL_DATA_CHANGE_CALLBACK_ADD_RESULT;
+ char *callback_id_str = NULL;
+ int callback_id = -1;
+ GList *filter_iter;
+ changed_noti_consumer_filter_info_s *filter_info;
+ char caller_app_id[255];
+ const char *pid_str;
+ int pid;
+ int sender_pid;
+
+ pid_str = bundle_get_val(b, AUL_K_CALLER_PID);
+ if (pid_str == NULL) {
+ LOGE("fail to get caller pid");
+ result = DATACONTROL_ERROR_IO_ERROR;
+ goto out;
+ }
+ pid = atoi(pid_str);
+ if (pid <= 1) {
+ LOGE("invalid caller pid %s", pid_str);
+ result = DATACONTROL_ERROR_IO_ERROR;
+ goto out;
+ }
+ if (aul_app_get_appid_bypid(pid, caller_app_id, sizeof(caller_app_id)) != 0) {
+ LOGE("Failed to get appid by pid");
+ return DATACONTROL_ERROR_IO_ERROR;
+ }
+
+ unique_id = (char *)bundle_get_val(b, OSP_K_DATACONTROL_UNIQUE_NAME);
+ LOGI("unique_id : %s", unique_id);
+ sender_pid = __get_sender_pid(unique_id);
+ if (sender_pid != pid) {
+ LOGE("invalid unique id. sender does not have unique_id %s", unique_id);
+ return DATACONTROL_ERROR_PERMISSION_DENIED;
+ }
+
+ result = __create_consumer_list_db();
+ if (result != DATACONTROL_ERROR_NONE) {
+ LOGE("fail to create consumer list db");
+ return result;
+ }
+
+ provider = malloc(sizeof(struct datacontrol_s));
+ if (provider == NULL) {
+ LOGE("Out of memory. fail to alloc provider.");
+ return DATACONTROL_ERROR_OUT_OF_MEMORY;
+ }
+ provider->provider_id = (char *)bundle_get_val(b, OSP_K_DATACONTROL_PROVIDER);
+ provider->data_id = (char *)bundle_get_val(b, OSP_K_DATACONTROL_DATA);
+ LOGI("Noti Provider ID: %s, data ID: %s, request type: %d", provider->provider_id, provider->data_id, type);
+ path = _get_encoded_path(provider, caller_app_id);
+ if (path == NULL) {
+ LOGE("can not get encoded path out of memory");
+ free(provider);
+ return DATACONTROL_ERROR_OUT_OF_MEMORY;
+ }
+
+ callback_id_str = (char *)bundle_get_val(b, OSP_K_DATA_CHANGED_CALLBACK_ID);
+ if (callback_id_str == NULL) {
+ LOGE("callback_id is NULL");
+ result = DATACONTROL_ERROR_IO_ERROR;
+ goto out;
+ }
+ callback_id = atoi(callback_id_str);
+
+ switch (type) {
+ case DATACONTROL_TYPE_ADD_DATA_CHANGED_CB:
+ {
+ LOGI("DATACONTROL_TYPE_ADD_DATA_CHANGED_CB called");
+ result_type = DATACONTROL_DATA_CHANGE_CALLBACK_ADD_RESULT;
+ filter_iter = g_list_first(__noti_consumer_filter_info_list);
+ for (; filter_iter != NULL; filter_iter = filter_iter->next) {
+ filter_info = (changed_noti_consumer_filter_info_s *)filter_iter->data;
+ noti_allow = filter_info->callback((data_control_h)provider, caller_app_id, filter_info->user_data);
+ if (!noti_allow)
+ break;
+ }
+ LOGI("provider_sql_consumer_filter_cb result %d", noti_allow);
+
+ if (noti_allow) {
+ result = __insert_consumer_list_db_info(
+ caller_app_id,
+ provider->provider_id,
+ provider->data_id,
+ unique_id,
+ path);
+ if (result != DATACONTROL_ERROR_NONE) {
+ LOGE("fail to set consumer list db info %d", result);
+ result = DATACONTROL_ERROR_PERMISSION_DENIED;
+ break;
+ }
+
+ result = __set_consumer_app_list(
+ caller_app_id,
+ path,
+ unique_id);
+ if (result != DATACONTROL_ERROR_NONE)
+ LOGE("fail to __set_consumer_app_list");
+
+ } else {
+ result = DATACONTROL_ERROR_PERMISSION_DENIED;
+ break;
+ }
+ break;
+ }
+ case DATACONTROL_TYPE_REMOVE_DATA_CHANGED_CB:
+ {
+ LOGI("DATACONTROL_NOTI_REMOVE_DATA_CHANGED_CB called");
+ result_type = DATACONTROL_DATA_CHANGE_CALLBACK_REMOVE_RESULT;
+ if (__noti_consumer_app_list) {
+ __free_consumer_info(unique_id);
+ LOGI("unregister %s from __noti_consumer_app_list", unique_id);
+ } else {
+ LOGI("empty __consumer_app_list");
+ }
+ result = __delete_consumer_list_db_info(path);
+ if (result != DATACONTROL_ERROR_NONE) {
+ LOGE("__delete_consumer_list_db_info fail %d", result);
+ result = DATACONTROL_ERROR_IO_ERROR;
+ goto out;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+out:
+ __send_add_callback_result(
+ result_type, unique_id, path, callback_id, result);
+
+ if (provider)
+ free(provider);
+
+ return result;
+}
+
int __datacontrol_handler_cb(bundle *b, int request_id, void *data)
{
datacontrol_socket_info *socket_info;
- char *caller = (char *)bundle_get_val(b, AUL_K_CALLER_APPID);
- char *callee = (char *)bundle_get_val(b, AUL_K_CALLEE_APPID);
+ int ret = DATACONTROL_ERROR_NONE;
- LOGI("datacontrol_handler_cb");
- socket_info = g_hash_table_lookup(__socket_pair_hash, caller);
+ const char *request_type = bundle_get_val(b, OSP_K_DATACONTROL_REQUEST_TYPE);
+ if (request_type == NULL) {
+ char *caller = (char *)bundle_get_val(b, AUL_K_CALLER_APPID);
+ char *callee = (char *)bundle_get_val(b, AUL_K_CALLEE_APPID);
- if (socket_info != NULL)
- g_hash_table_remove(__socket_pair_hash, caller);
+ socket_info = g_hash_table_lookup(__socket_pair_hash, caller);
- socket_info = _get_socket_info(caller, callee, "provider", __provider_recv_message, caller);
- if (socket_info == NULL)
- return DATACONTROL_ERROR_IO_ERROR;
+ if (socket_info != NULL)
+ g_hash_table_remove(__socket_pair_hash, caller);
- g_hash_table_insert(__socket_pair_hash, strdup(caller), socket_info);
+ socket_info = _add_watch_on_socket_info(caller, callee, "provider", __provider_recv_message, caller);
+ if (socket_info == NULL)
+ return DATACONTROL_ERROR_IO_ERROR;
- return DATACONTROL_ERROR_NONE;
+ g_hash_table_insert(__socket_pair_hash, strdup(caller), socket_info);
+ } else {
+ /* Get the request type */
+ datacontrol_request_type type = atoi(request_type);
+ if (type == DATACONTROL_TYPE_ADD_DATA_CHANGED_CB ||
+ type == DATACONTROL_TYPE_REMOVE_DATA_CHANGED_CB) {
+ __provider_noti_process(b, type);
+ } else {
+ LOGE("Invalid data control request");
+ return DATACONTROL_ERROR_INVALID_PARAMETER;
+ }
+ }
+
+ return ret;
}
int datacontrol_provider_sql_register_cb(datacontrol_provider_sql_cb *callback, void *user_data)
g_hash_table_remove(__request_table, &request_id);
return ret;
+
}
int datacontrol_provider_send_update_result(int request_id)
return ret;
}
+static int __send_signal_to_consumer(datacontrol_h provider,
+ char *unique_id,
+ char *path,
+ datacontrol_data_change_type_e type,
+ bundle *data)
+{
+ int result = DATACONTROL_ERROR_NONE;
+ int len = 0;
+ bundle_raw *raw = NULL;
+ GError *err = NULL;
+ gboolean signal_result = TRUE;
+
+ if (data) {
+ if (bundle_encode(data, &raw, &len) != BUNDLE_ERROR_NONE) {
+ LOGE("bundle_encode fail");
+ result = DATACONTROL_ERROR_IO_ERROR;
+ goto out;
+ }
+ }
+
+ LOGI("emit signal to object path %s", path);
+ signal_result = g_dbus_connection_emit_signal(
+ _get_dbus_connection(),
+ unique_id,
+ path,
+ DATA_CONTROL_INTERFACE_NAME,
+ DATA_CONTROL_DATA_CHANGE_DATA_CHANGED,
+ g_variant_new("(isssi)",
+ type,
+ provider->provider_id,
+ provider->data_id,
+ ((raw) ? (char *)raw : ""),
+ len), &err);
+
+ if (signal_result == FALSE) {
+ LOGE("g_dbus_connection_emit_signal() is failed");
+ if (err != NULL) {
+ LOGE("g_dbus_connection_emit_signal() err : %s",
+ err->message);
+ g_error_free(err);
+ }
+ return DATACONTROL_ERROR_IO_ERROR;
+ }
+
+out:
+ if (raw)
+ free(raw);
+
+ return result;
+}
+
+int datacontrol_provider_send_data_change_noti(
+ datacontrol_h provider,
+ datacontrol_data_change_type_e type,
+ bundle *data)
+{
+ int result = DATACONTROL_ERROR_NONE;
+ GList *consumer_iter = NULL;
+ datacontrol_consumer_info *consumer_info = NULL;
+
+ LOGE("datacontrol_provider_send_data_change_noti %d, %d", g_list_length(__noti_consumer_app_list), type);
+ consumer_iter = g_list_first(__noti_consumer_app_list);
+ for (; consumer_iter != NULL; consumer_iter = consumer_iter->next) {
+ consumer_info = (datacontrol_consumer_info *)consumer_iter->data;
+ result = __send_signal_to_consumer(
+ provider,
+ consumer_info->unique_id,
+ consumer_info->object_path,
+ type,
+ data);
+ if (result != DATACONTROL_ERROR_NONE) {
+ LOGE("__send_signal_to_consumer fail : %d", result);
+ break;
+ }
+ }
+ return result;
+}
+
+int datacontrol_provider_add_data_change_consumer_filter_cb(
+ data_control_provider_data_change_consumer_filter_cb callback,
+ void *user_data,
+ int *callback_id)
+{
+ changed_noti_consumer_filter_info_s *filter_info = (changed_noti_consumer_filter_info_s *)calloc(1,
+ sizeof(changed_noti_consumer_filter_info_s));
+
+ *callback_id = _datacontrol_get_data_changed_filter_callback_id();
+
+ filter_info->callback_id = *callback_id;
+ filter_info->callback = callback;
+ filter_info->user_data = user_data;
+ __noti_consumer_filter_info_list = g_list_append(__noti_consumer_filter_info_list, filter_info);
+
+ return DATACONTROL_ERROR_NONE;
+}
+
+int datacontrol_provider_remove_data_change_consumer_filter_cb(int callback_id)
+{
+ GList *find_list;
+ changed_noti_consumer_filter_info_s filter_info;
+ filter_info.callback_id = callback_id;
+ find_list = g_list_find_custom(__noti_consumer_filter_info_list, &filter_info,
+ (GCompareFunc)__data_changed_filter_cb_info_compare_cb);
+ if (find_list != NULL) {
+ __noti_consumer_filter_info_list = g_list_remove(__noti_consumer_filter_info_list, find_list->data);
+ } else {
+ LOGE("invalid callback_id : %d", callback_id);
+ return DATACONTROL_ERROR_INVALID_PARAMETER;
+ }
+
+ return DATACONTROL_ERROR_NONE;
+}
+
+int datacontrol_provider_foreach_data_change_consumer(
+ datacontrol_h provider,
+ void *list_cb,
+ void *user_data)
+{
+ char *app_id = NULL;
+ int ret = DATACONTROL_ERROR_NONE;
+ sqlite3_stmt *stmt = NULL;
+ char query[QUERY_MAXLEN];
+ bool callback_result;
+ data_control_provider_data_change_consumer_cb consumer_list_cb;
+ consumer_list_cb = (data_control_provider_data_change_consumer_cb)list_cb;
+
+ sqlite3_snprintf(QUERY_MAXLEN, query,
+ "SELECT app_id " \
+ "FROM data_control_consumer_path_list WHERE provider_id = ? AND data_id = ?");
+ LOGI("get_changed_noti_consumer_list query : %s", query);
+
+ ret = sqlite3_prepare_v2(__provider_db, query, -1, &stmt, NULL);
+ if (ret != SQLITE_OK) {
+ LOGE("prepare stmt fail");
+ ret = DATACONTROL_ERROR_IO_ERROR;
+ goto out;
+ }
+
+ ret = sqlite3_bind_text(stmt, 1, provider->provider_id, -1, SQLITE_TRANSIENT);
+ if (ret != SQLITE_OK) {
+ LOGE("bind provider id fail: %s", sqlite3_errmsg(__provider_db));
+ ret = DATACONTROL_ERROR_IO_ERROR;
+ goto out;
+ }
+
+ ret = sqlite3_bind_text(stmt, 2, provider->data_id, -1, SQLITE_TRANSIENT);
+ if (ret != SQLITE_OK) {
+ LOGE("bind data id fail: %s", sqlite3_errmsg(__provider_db));
+ ret = DATACONTROL_ERROR_IO_ERROR;
+ goto out;
+ }
+
+ while (SQLITE_ROW == sqlite3_step(stmt)) {
+ app_id = (char *)sqlite3_column_text(stmt, 0);
+ if (!app_id) {
+ LOGE("Failed to get package name\n");
+ continue;
+ }
+ callback_result = consumer_list_cb((data_control_h)provider, app_id, user_data);
+ LOGI("app_id : %s, result : %d ", app_id, callback_result);
+ if (!callback_result)
+ break;
+ }
+out:
+ sqlite3_reset(stmt);
+ sqlite3_clear_bindings(stmt);
+ sqlite3_finalize(stmt);
+
+ return ret;
+}
+
#define REQUEST_PATH_MAX 512
#define MAX_REQUEST_ARGUMENT_SIZE 1048576 /* 1MB */
-struct datacontrol_s {
- char *provider_id;
- char *data_id;
-};
-
typedef struct {
char *provider_id;
char *app_id;
static int __sql_request_provider(datacontrol_h provider, datacontrol_request_type type, bundle *request_data, bundle *extra_kb, int request_id)
{
char *app_id = NULL;
- void *data = NULL;
+ void *response_cb_data = NULL;
int ret = DATACONTROL_ERROR_NONE;
sql_response_cb_s *sql_dc_temp;
void *sql_dc_returned = NULL;
request_info->type = type;
sql_dc->request_info_list = g_list_append(sql_dc->request_info_list, request_info);
- data = sql_dc;
+ response_cb_data = sql_dc;
LOGI("SQL datacontrol appid: %s", sql_dc->app_id);
}
return ret;
}
- socket_info = _get_socket_info(caller_app_id, app_id, "consumer", __consumer_recv_sql_message, data);
+ socket_info = _add_watch_on_socket_info(caller_app_id, app_id, "consumer", __consumer_recv_sql_message, response_cb_data);
if (socket_info == NULL) {
LOGE("_get_socket_info error !!!");
return DATACONTROL_ERROR_IO_ERROR;
--- /dev/null
+#include <dlog.h>
+#include <errno.h>
+#include <glib.h>
+#include <stdbool.h>
+#include <stdlib.h>
+
+#include "data_control_internal.h"
+#include "data_control_log.h"
+#include "data_control_noti.h"
+#include "data-control-noti.h"
+
+EXPORT_API int data_control_add_data_change_cb(
+ data_control_h provider,
+ data_control_data_change_cb callback,
+ void *user_data,
+ data_control_add_callback_result_cb result_callback,
+ void *result_cb_user_data,
+ int *callback_id)
+{
+ int retval = datacontrol_check_privilege(PRIVILEGE_CONSUMER);
+ if (retval != DATA_CONTROL_ERROR_NONE)
+ return retval;
+
+ if (callback == NULL || provider == NULL || callback_id == NULL)
+ return DATA_CONTROL_ERROR_INVALID_PARAMETER;
+
+ return datacontrol_add_data_change_cb(
+ (datacontrol_h)provider,
+ callback,
+ user_data,
+ result_callback,
+ result_cb_user_data,
+ callback_id);
+}
+
+EXPORT_API int data_control_remove_data_change_cb(data_control_h provider, int callback_id)
+{
+ if (callback_id < 1 || provider == NULL)
+ return DATA_CONTROL_ERROR_INVALID_PARAMETER;
+
+ int retval = datacontrol_check_privilege(PRIVILEGE_CONSUMER);
+ if (retval != DATA_CONTROL_ERROR_NONE)
+ return retval;
+
+ return datacontrol_remove_data_change_cb((datacontrol_h)provider, callback_id);
+}
#include <dlog.h>
#include <bundle.h>
#include <data-control-provider.h>
+#include <data-control-types.h>
#include "data_control_provider.h"
#include "data_control_sql.h"
#include "data_control_log.h"
#include "data_control_internal.h"
+#include "data-control-internal.h"
#define INSERT_STMT_CONST_LEN 25
#define DELETE_STMT_CONST_LEN 12
char *data_id;
};
-struct datacontrol_s {
- char *provider_id;
- char *data_id;
-};
-
typedef struct {
int no_of_elements;
int length;
return datacontrol_provider_send_map_get_value_result(request_id, value_list, value_count);
}
+EXPORT_API int data_control_provider_add_data_change_consumer_filter_cb(
+ data_control_provider_data_change_consumer_filter_cb callback,
+ void *user_data,
+ int *callback_id)
+{
+ if (!callback)
+ return DATA_CONTROL_ERROR_INVALID_PARAMETER;
+ if (callback_id == NULL)
+ return DATA_CONTROL_ERROR_INVALID_PARAMETER;
+
+ return datacontrol_provider_add_data_change_consumer_filter_cb(callback, user_data, callback_id);
+}
+
+EXPORT_API int data_control_provider_remove_data_change_consumer_filter_cb(int callback_id)
+{
+ if (callback_id < 1)
+ return DATA_CONTROL_ERROR_INVALID_PARAMETER;
+
+ return datacontrol_provider_remove_data_change_consumer_filter_cb(callback_id);
+}
+
+EXPORT_API int data_control_provider_send_data_change_noti(
+ data_control_h provider,
+ data_control_data_change_type_e type,
+ bundle *data)
+{
+ if (!provider)
+ return DATA_CONTROL_ERROR_INVALID_PARAMETER;
+ if (provider->provider_id == NULL || provider->data_id == NULL)
+ return DATA_CONTROL_ERROR_INVALID_PARAMETER;
+
+ if (type < DATA_CONTROL_DATA_CHANGE_SQL_UPDATE || type > DATA_CONTROL_DATA_CHANGE_MAP_REMOVE)
+ return DATA_CONTROL_ERROR_INVALID_PARAMETER;
+
+ return datacontrol_provider_send_data_change_noti(
+ (datacontrol_h)provider,
+ _get_internal_noti_type(type),
+ data);
+}
+
+EXPORT_API int data_control_provider_foreach_data_change_consumer(
+ data_control_h provider,
+ data_control_provider_data_change_consumer_cb list_cb,
+ void *user_data)
+{
+ if (!provider || !list_cb)
+ return DATA_CONTROL_ERROR_INVALID_PARAMETER;
+ if (provider->provider_id == NULL || provider->data_id == NULL)
+ return DATA_CONTROL_ERROR_INVALID_PARAMETER;
+
+ return datacontrol_provider_foreach_data_change_consumer(
+ (datacontrol_h)provider, list_cb, user_data);
+}