Add new api for subscribing db update callback 34/119534/1 accepted/tizen/3.0/common/20170320.130656 accepted/tizen/3.0/ivi/20170320.012605 accepted/tizen/3.0/mobile/20170320.012253 accepted/tizen/3.0/tv/20170320.012347 accepted/tizen/3.0/wearable/20170320.012459 submit/tizen_3.0/20170317.074355
authorInkyun Kil <inkyun.kil@samsung.com>
Tue, 10 Jan 2017 07:42:22 +0000 (16:42 +0900)
committerJiwoong Im <jiwoong.im@samsung.com>
Fri, 17 Mar 2017 07:35:05 +0000 (00:35 -0700)
- To inform task-manager of updated rua list.

Change-Id: Ia7e8cb28221e21bd383a5171dd97dc2bf9b78737
Signed-off-by: Inkyun Kil <inkyun.kil@samsung.com>
(cherry picked from commit 4259894fde81136eca754812738695dc6871fe74)

CMakeLists.txt
include/rua.h
include/rua_dbus.h [new file with mode: 0644]
packaging/librua.spec
src/rua.c
src/rua_dbus.c [new file with mode: 0644]
src/rua_internal.c
test/rua-test.c

index 80f24be..cca4366 100644 (file)
@@ -10,7 +10,7 @@ AUX_SOURCE_DIRECTORY(src SRCS)
 INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include)
 
 INCLUDE(FindPkgConfig)
-pkg_check_modules(pkgs REQUIRED sqlite3 db-util libtzplatform-config bundle aul)
+pkg_check_modules(pkgs REQUIRED sqlite3 db-util libtzplatform-config bundle aul gio-2.0)
 
 FOREACH(flag ${pkgs_CFLAGS})
        SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}")
@@ -39,6 +39,7 @@ INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/include/rua_internal.h DESTINATION inc
 INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/include/rua_stat.h DESTINATION include/${PROJECT_NAME})
 INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/include/rua_stat_internal.h DESTINATION include/${PROJECT_NAME})
 INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/include/rua_util.h DESTINATION include/${PROJECT_NAME})
+INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/include/rua_dbus.h DESTINATION include/${PROJECT_NAME})
 
 
 ADD_SUBDIRECTORY(test)
index 3bf79d1..ad38464 100755 (executable)
@@ -78,6 +78,19 @@ struct rua_rec {
 };
 
 /**
+ * @brief Called when the history is updated.
+ * @param[in] table db table pointer
+ * @param[in] nrows the number of record
+ * @param[in] ncols the number of field
+ * @param[in] user_data  The user data passed from rua_register_update_cb()
+ */
+typedef void (*rua_history_update_cb) (
+               char **table,
+               int nrows,
+               int ncols,
+               void *user_data);
+
+/**
  * @brief      Add history
  * @param[in]  pkg_name package name to delete history
  * @return     0 on success, otherwise a nagative error value
@@ -170,6 +183,30 @@ API int rua_delete_history_with_instance_id(const char *app_id,
                const char *instance_id);
 
 /**
+ * @brief      Registers a callback function to be invoked when the history is updated
+ * @param[in]  callback The callback function to be registered
+ * @param[in]  user_data The user data passed to rua_register_update_cb()
+ * @param[out] callback_id Added callback id
+ * @return     0 on success, otherwise a nagative error value
+ * @retval     0 if on successful
+ * @retval     -1 if it is already registered or on failed
+ */
+API int rua_register_update_cb(rua_history_update_cb callback,
+               void *user_data, int *callback_id);
+API int rua_register_update_cb_for_uid(rua_history_update_cb callback,
+               void *user_data, int *callback_id, uid_t uid);
+
+/**
+ * @brief      Unregisters a callback function
+ * @param[in]  callback_id Target callback id
+ * @return     0 on success, otherwise a nagative error value
+ * @retval     0 if on successful
+ * @retval     -1 if the callback was not registered or on failed
+ */
+API int rua_unregister_update_cb(int callback_id);
+API int rua_unregister_update_cb_for_uid(int callback_id, uid_t uid);
+
+/**
  * @brief      Initialize rua
  * @return     0 on success, otherwise a nagative error value
  * @retval     0 on successful
diff --git a/include/rua_dbus.h b/include/rua_dbus.h
new file mode 100644 (file)
index 0000000..f0110ae
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2017 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.
+ */
+
+/**
+ * @file        rua_dbus.h
+ * @brief       RUA DBUS API declaration header file.
+ * @version     0.1
+ * @history     0.1: RUA DBUS API Declarations, structure declaration
+ */
+
+#ifndef __RUA_DBUS_H__
+#define __RUA_DBUS_H__
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <dlog.h>
+
+#ifndef API
+#define API __attribute__ ((visibility("default")))
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif
+
+#define LOG_TAG "RUA"
+
+typedef enum
+{
+       ADD,
+       DELETE
+} update_type;
+
+int rua_dbus_send_update_signal(update_type type);
+int rua_dbus_signal_subscribe(rua_history_update_cb callback,
+               void *user_data, int *callback_id);
+int rua_dbus_signal_unsubscribe(int callback_id);
+
+#ifdef __cplusplus
+}
+#endif
+#endif                         /*__RUA_DBUS_H__*/
index 57078f3..e76f9e1 100644 (file)
@@ -13,6 +13,7 @@ BuildRequires:  pkgconfig(aul)
 BuildRequires:  pkgconfig(db-util)
 BuildRequires:  pkgconfig(sqlite3)
 BuildRequires:  pkgconfig(libtzplatform-config)
+BuildRequires:  pkgconfig(gio-2.0)
 
 %define upgrade_script_path /usr/share/upgrade/scripts
 
index f8bab6a..6381870 100644 (file)
--- a/src/rua.c
+++ b/src/rua.c
@@ -24,6 +24,7 @@
 #include "rua_internal.h"
 #include "rua.h"
 #include "db-schema.h"
+#include "rua_dbus.h"
 
 int rua_add_history_for_uid(char *pkg_name, char *app_path, char *arg, uid_t uid)
 {
@@ -172,6 +173,45 @@ int rua_history_load_db_for_uid(char ***table, int *nrows, int *ncols, uid_t uid
        return r;
 }
 
+int rua_register_update_cb(rua_history_update_cb callback, void *user_data, int *callback_id)
+{
+       return rua_register_update_cb_for_uid(callback, user_data, callback_id, getuid());
+}
+
+int rua_register_update_cb_for_uid(rua_history_update_cb callback, void *user_data, int *callback_id, uid_t uid)
+{
+       int r;
+
+       if (callback == NULL)
+               return -1;
+
+       r = _rua_util_check_uid(uid);
+       if (r == -1)
+               return r;
+
+       r = rua_dbus_signal_subscribe(callback, user_data, callback_id);
+
+       return r;
+}
+
+int rua_unregister_update_cb(int callback_id)
+{
+       return rua_unregister_update_cb_for_uid(callback_id, getuid());
+}
+
+int rua_unregister_update_cb_for_uid(int callback_id, uid_t uid)
+{
+       int r;
+
+       r = _rua_util_check_uid(uid);
+       if (r == -1)
+               return r;
+
+       r = rua_dbus_signal_unsubscribe(callback_id);
+
+       return r;
+}
+
 int rua_history_unload_db(char ***table)
 {
        if (*table) {
diff --git a/src/rua_dbus.c b/src/rua_dbus.c
new file mode 100644 (file)
index 0000000..132bb4a
--- /dev/null
@@ -0,0 +1,224 @@
+/*
+ * Copyright (c) 2017 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 <string.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include "rua.h"
+#include "rua_dbus.h"
+
+#define RUA_INTERFACE "org.tizen.rua"
+#define RUA_PATH "/org/tizen/rua"
+#define RUA_SIGNAL_DATA_UPDATE "dataupdate"
+
+struct cb_data
+{
+       int callback_id;
+       rua_history_update_cb callback;
+       void *user_data;
+};
+
+static GDBusConnection *conn;
+static guint s_id;
+
+static gint atomic_callback_id;
+static GHashTable* callback_hash_table;
+
+static void __foreach_callback(gpointer key, gpointer value,
+               gpointer user_data);
+static void __signal_handler(GDBusConnection *connection,
+                                       const gchar *sender_name,
+                                       const gchar *object_path,
+                                       const gchar *interface_name,
+                                       const gchar *signal_name,
+                                       GVariant *parameters,
+                                       gpointer user_data);
+static void __rua_dbus_init(void);
+static void __rua_dbus_exit(void);
+
+int rua_dbus_send_update_signal(update_type type)
+{
+       __rua_dbus_init();
+       GError *err = NULL;
+
+       if (g_dbus_connection_emit_signal(conn,
+                               NULL,
+                               RUA_PATH,
+                               RUA_INTERFACE,
+                               RUA_SIGNAL_DATA_UPDATE,
+                               g_variant_new("(i)", type),
+                               &err) == FALSE) {
+               LOGE("g_dbus_connection_emit_signal() is failed. %s",
+                               err->message);
+               __rua_dbus_exit();
+               return -1;
+       }
+
+       if (g_dbus_connection_flush_sync(conn, NULL, &err) == FALSE) {
+               LOGE("g_dbus_connection_flush_sync() is failed. %s",
+                               err->message);
+               __rua_dbus_exit();
+               return -1;
+       }
+
+       g_clear_error(&err);
+
+       __rua_dbus_exit();
+
+       return 0;
+}
+
+int rua_dbus_signal_subscribe(rua_history_update_cb callback,
+               void *user_data, int *callback_id)
+{
+       struct cb_data *cd = NULL;
+
+       if (s_id == 0) {
+               __rua_dbus_init();
+
+               s_id = g_dbus_connection_signal_subscribe(conn,
+                               NULL,
+                               RUA_INTERFACE,
+                               RUA_SIGNAL_DATA_UPDATE,
+                               RUA_PATH,
+                               NULL,
+                               G_DBUS_SIGNAL_FLAGS_NONE,
+                               __signal_handler,
+                               NULL,
+                               NULL);
+
+               if (s_id == 0) {
+                       LOGE("g_dbus_connection_signal_subscribe() is failed.");
+                       __rua_dbus_exit();
+                       return -1;
+               }
+
+               if (!callback_hash_table) {
+                       callback_hash_table = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+                                       NULL, free);
+                       if (!callback_hash_table) {
+                               LOGE("out of memory : g_hash_table_new() is failed");
+                               __rua_dbus_exit();
+                               return -1;
+                       }
+               }
+       }
+
+       cd = (struct cb_data *)malloc(sizeof(struct cb_data));
+       if (!cd) {
+               LOGE("out of memory : malloc() is failed");
+               return -1;
+       }
+
+       g_atomic_int_inc(&atomic_callback_id);
+
+       cd->callback = callback;
+       cd->user_data = user_data;
+       cd->callback_id = atomic_callback_id;
+
+       g_hash_table_insert(callback_hash_table,
+                       GINT_TO_POINTER(cd->callback_id), (gpointer)cd);
+
+       *callback_id = cd->callback_id;
+
+       return 0;
+}
+
+int rua_dbus_signal_unsubscribe(int callback_id)
+{
+       gboolean result = FALSE;
+       guint table_size = 0;
+
+       result = g_hash_table_remove(callback_hash_table, GINT_TO_POINTER(callback_id));
+       if (!result) {
+               LOGE("g_hash_table_remove failed(%d is wrong)", callback_id);
+               return -1;
+       }
+
+       table_size = g_hash_table_size(callback_hash_table);
+       if (s_id && table_size == 0) {
+               g_dbus_connection_signal_unsubscribe(conn, s_id);
+               g_hash_table_destroy(callback_hash_table);
+               __rua_dbus_exit();
+               callback_hash_table = NULL;
+               s_id = 0;
+       }
+
+       return 0;
+}
+
+static void __foreach_callback(gpointer key, gpointer value,
+               gpointer user_data)
+{
+       char **table = NULL;
+       int nrows = 0;
+       int ncols = 0;
+       int r = 0;
+
+       struct cb_data *cd = (struct cb_data *)value;
+
+       r = rua_history_load_db(&table, &nrows, &ncols);
+       if (r == -1)
+               return;
+
+       if (cd->callback) {
+               cd->callback(table, nrows, ncols, cd->user_data);
+       }
+}
+
+static void __signal_handler(GDBusConnection *connection,
+                                       const gchar *sender_name,
+                                       const gchar *object_path,
+                                       const gchar *interface_name,
+                                       const gchar *signal_name,
+                                       GVariant *parameters,
+                                       gpointer user_data)
+{
+       int update_type;
+
+       LOGI("__signal_handler");
+       if (g_strcmp0(signal_name, RUA_SIGNAL_DATA_UPDATE))
+               return;
+
+       g_variant_get(parameters, "(i)", &update_type);
+
+       g_hash_table_foreach(callback_hash_table, __foreach_callback, NULL);
+}
+
+static void __rua_dbus_init(void)
+{
+       if (!conn) {
+               GError *err = NULL;
+               conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &err);
+               if (!conn) {
+                       LOGE("g_bus_get_sync() is failed. %s", err->message);
+                       g_error_free(err);
+                       return;
+               }
+       }
+}
+
+static void __rua_dbus_exit(void)
+{
+       if (conn) {
+               g_object_unref(conn);
+               conn = NULL;
+       }
+}
index 7995262..bc17818 100644 (file)
@@ -20,6 +20,7 @@
 #include "rua_internal.h"
 #include "db-schema.h"
 #include "rua_util.h"
+#include "rua_dbus.h"
 
 static int __exec(sqlite3 *db, char *query)
 {
@@ -120,6 +121,13 @@ int rua_usr_db_delete_history(bundle *b, uid_t uid)
                result = -1;
        }
 
+       r = rua_dbus_send_update_signal(DELETE);
+       if (r == -1) {
+               LOGE("[RUA SEND SIGNAL ERROR] \n");
+               db_util_close(db);
+               return -1;
+       }
+
        if (db != NULL)
                db_util_close(db);
 
@@ -183,6 +191,13 @@ int rua_usr_db_add_history(struct rua_rec *rec, uid_t uid)
                return -1;
        }
 
+       r = rua_dbus_send_update_signal(ADD);
+       if (r == -1) {
+               LOGE("[RUA SEND SIGNAL ERROR] \n");
+               db_util_close(db);
+               return -1;
+       }
+
        db_util_close(db);
        return r;
 }
index 2d85442..1dd6bf3 100644 (file)
@@ -22,6 +22,7 @@
 #include <unistd.h>
 #include <sys/types.h>
 #include <glib.h>
+#include <pthread.h>
 
 /* For multi-user support */
 #include <tzplatform_config.h>
 #include "rua_stat.h"
 #include "rua_stat_internal.h"
 
+static GMainLoop *mainloop = NULL;
+static pthread_t g_thread;
+
+static int callback_id_table[10] = { 0, };
+
+static gboolean run_test(int selected_number);
+static void __print_menu()
+{
+       int test_num = 0;
+       int run_next = 1;
+
+       while(run_next) {
+               printf("==========================================\n");
+               printf("    Basic test menu \n");
+               printf("==========================================\n");
+               printf(" 0.  EXIT\n");
+               printf(" 1.  Add rua history to DEFAULT USER(5001)\n");
+               printf(" 2.  Delete history with pkgname\n");
+               printf(" 3.  Load RUA history\n");
+               printf(" 4.  Update RUA stat\n");
+               printf(" 5.  Get RUA stat tags\n");
+               printf(" 6.  Register update callback (MAX 10)\n");
+               printf(" 7.  Unregister update callback\n");
+               printf("------------------------------------------\n");
+               int ret = scanf("%d", &test_num);
+               if (ret < 0) {
+                       printf("scanf fail %d", ret);
+                       break;
+               }
+
+               run_next = run_test(test_num);
+       }
+
+}
+
+static void __update_cb(char **table, int rows, int cols)
+{
+       struct rua_rec record;
+
+       printf("update_cb");
+       int row;
+       for (row = 0; row < rows; ++row) {
+               rua_history_get_rec(&record, table, rows, cols, row);
+               printf("pkgname : %s, time : %d \n", record.pkg_name, (int)record.launch_time);
+       }
+
+}
+
 static int __add_history()
 {
        int ret;
@@ -95,13 +144,62 @@ static int __get_stat_tags()
        return ret;
 }
 
-static gboolean run_test(int selected_number)
+static int __register_callback()
+{
+       int ret;
+
+       int i = 0;
+       for (i = 0; i < 10; i++) {
+               if (callback_id_table[i] == 0) {
+                       int output = 0;
+                       ret = rua_register_update_cb(__update_cb, NULL, &output);
+                       if (ret == -1) {
+                               printf("__register_callback error : %d \n", ret);
+                               return -1;
+                       }
+
+                       printf("callback id : %d \n", output);
+                       callback_id_table[i] = output;
+
+                       return 0;
+               }
+       }
+
+       return -1;
+}
+
+static int __unregister_callback()
 {
-       gboolean go_to_loop = TRUE;
+       int ret;
+
+       int i = 0;
+       int id = 0;
+       printf("Enter callback_id (The number from register_callback) : ");
+       scanf("%d", &id);
+
+       for (i = 0; i < 10; i++) {
+               if (callback_id_table[i] == id) {
+                       ret = rua_unregister_update_cb(callback_id_table[i]);
+                       if (ret == -1)
+                               printf("__unregister_callback : %d \n", ret);
 
+                       callback_id_table[i] = 0;
+                       return 0;
+               }
+       }
+
+       return -1;
+}
+
+
+static gboolean run_test(int selected_number)
+{
+       int r = 0;
+       gboolean loop = TRUE;
        switch (selected_number) {
        case 0:
-               go_to_loop = FALSE;
+               g_main_loop_quit(mainloop);
+               loop = FALSE;
                break;
 
        case 1:
@@ -124,38 +222,44 @@ static gboolean run_test(int selected_number)
                __get_stat_tags();
                break;
 
+       case 6:
+               r = __register_callback();
+               if (r == -1)
+                       printf("table is full");
+               break;
+
+       case 7:
+               r = __unregister_callback();
+               if (r == -1)
+                       printf("id is wrong");
+
+               break;
+
        default:
                break;
        }
 
-       return go_to_loop;
 
+
+       return loop;
 }
 
 int main()
 {
        int ret = 0;
-       int test_num;
-       gboolean run_next = TRUE;
 
-       while(run_next) {
-               printf("==========================================\n");
-               printf("    Basic test menu \n");
-               printf("==========================================\n");
-               printf(" 0.  EXIT\n");
-               printf(" 1.  Add rua history to DEFAULT USER(5001)\n");
-               printf(" 2.  Delete history with pkgname\n");
-               printf(" 3.  Load RUA history\n");
-               printf(" 4.  Update RUA stat\n");
-               printf(" 5.  Get RUA stat tags\n");
-               printf("------------------------------------------\n");
-               ret = scanf("%d", &test_num);
-               if (ret < 0) {
-                       printf("scanf fail %d", ret);
-                       break;
-               }
+       mainloop = g_main_loop_new(NULL, FALSE);
 
-               run_next = run_test(test_num);
+       int result = pthread_create(&g_thread, NULL, __print_menu, (void *)NULL);
+       if (result < 0)
+       {
+               printf("pthread_create failed in initialize\n");
+               return 0;
        }
+
+       g_main_loop_run(mainloop);
+       g_main_loop_unref(mainloop);
+       mainloop = NULL;
+
        return ret;
 }