Update hello protocol
[platform/core/uifw/tts.git] / client / tts.c
index 08e8d9a..b7176fc 100644 (file)
@@ -20,6 +20,8 @@
 #include <sys/wait.h>
 #include <system_info.h>
 #include <vconf.h>
+#include <package-manager.h>
+#include <pthread.h>
 
 #include "tts.h"
 #include "tts_client.h"
@@ -39,6 +41,7 @@ static int g_max_text_size = -1;
 
 static Ecore_Timer* g_check_state_timer = NULL;
 
+static Ecore_Timer* g_hello_timer;
 
 /* for repetition */
 static char* g_language = NULL;
@@ -47,10 +50,18 @@ static int g_voice_type = -1;
 
 static int g_speed = -1;
 
+/* for checking engine update */
+static pkgmgr_client* g_pkgmgr = NULL;
+static char* g_engine_name = NULL;
+static int g_engine_update_status = 0;
+static pthread_mutex_t g_pkgmgr_mutex = PTHREAD_MUTEX_INITIALIZER;
+
 
 /* Function definition */
 static Eina_Bool __tts_notify_state_changed(void *data);
 static Eina_Bool __tts_notify_error(void *data);
+int __tts_cb_error(int uid, tts_error_e reason, int utt_id, char* err_msg);
+
 
 const char* tts_tag()
 {
@@ -233,6 +244,84 @@ void _tts_config_engine_changed_cb(const char* engine_id, const char* setting, c
 }
 //LCOV_EXCL_STOP
 
+void __tts_unset_all_callbacks(tts_h tts)
+{
+       SLOG(LOG_INFO, TAG_TTSC, "@@@ unset all callbacks");
+
+       tts_unset_state_changed_cb(tts);
+       tts_unset_utterance_started_cb(tts);
+       tts_unset_utterance_completed_cb(tts);
+       tts_unset_error_cb(tts);
+       tts_unset_default_voice_changed_cb(tts);
+       tts_unset_engine_changed_cb(tts);
+
+       SLOG(LOG_DEBUG, TAG_TTSC, "@@@");
+}
+
+static int __pkgmgr_status_cb(uid_t target_uid, int req_id, const char *type, const char *pkgname, const char *key, const char *val, const void *pmsg, void *data)
+{
+       SLOG(LOG_INFO, TAG_TTSC, "[INFO] pkgmgr status cb is invoked. pkgname(%s), type(%s), key(%s), val(%s)", pkgname, type, key, val);
+
+       if (0 != strncmp(g_engine_name, pkgname, strlen(g_engine_name))) {
+               SLOG(LOG_DEBUG, TAG_TTSC, "[WARN] this is not tts engine");
+               return 0;
+       } else {
+               if (key && 0 == strncmp(key, "start", strlen(key))) {
+                       if (val && (0 == strncmp(val, "update", strlen(val) || 0 == strncmp(val, "uninstall", strlen(val))))) {
+                               SLOG(LOG_INFO, TAG_TTSC, "[INFO] start to install.");
+                               g_engine_update_status = 1;
+                       }
+               } else if (key && 0 == strncmp(key, "end", strlen(key))) {
+                       SLOG(LOG_INFO, TAG_TTSC, "[INFO] finish to install");
+                       g_engine_update_status = 0;
+               }
+       }
+
+       return 0;
+}
+
+static void __create_pkgmgr_thread(void* data, Ecore_Thread* thread)
+{
+       SLOG(LOG_ERROR, TAG_TTSC, "[DEBUG] create pkgmgr thread");
+
+       pthread_mutex_lock(&g_pkgmgr_mutex);
+
+       while (!g_pkgmgr) {
+               g_pkgmgr = pkgmgr_client_new(PC_LISTENING);
+               if (NULL == g_pkgmgr) {
+                       SLOG(LOG_ERROR, TAG_TTSC, "[ERROR] Fail to create pkgmgr handle");
+               } else {
+                       if (pkgmgr_client_listen_status(g_pkgmgr, __pkgmgr_status_cb, NULL) < 0) {
+                               SLOG(LOG_ERROR, TAG_TTSC, "[ERROR] Fail to listen pkgmgr status. remove and recreate client");
+                               pkgmgr_client_free(g_pkgmgr);
+                               g_pkgmgr = NULL;
+                               continue;
+                       } else {
+                               SLOG(LOG_ERROR, TAG_TTSC, "[DEBUG] Succeed to register pkgmgr cb");
+                       }
+               }
+               usleep(10000);
+       }
+
+       pthread_mutex_unlock(&g_pkgmgr_mutex);
+
+       return ;
+}
+
+static void __finish_pkgmgr_thread(void* data, Ecore_Thread* thread)
+{
+       SLOG(LOG_ERROR, TAG_TTSC, "[DEBUG] Finish pkgmgr thread");
+}
+
+static void __pkgmgr_thread(void* data)
+{
+       SLOG(LOG_ERROR, TAG_TTSC, "[DEBUG] call pkgmgr_thread");
+
+       ecore_thread_run(__create_pkgmgr_thread, __finish_pkgmgr_thread, NULL, NULL);
+
+       return ;
+}
+
 int tts_create(tts_h* tts)
 {
        if (0 != __tts_get_feature_enabled()) {
@@ -273,13 +362,24 @@ int tts_create(tts_h* tts)
                return __tts_convert_config_error_code(ret);
        }
 
-       ret = tts_config_mgr_set_callback(client->uid, _tts_config_engine_changed_cb, __tts_config_voice_changed_cb, NULL, NULL, client->tts);
+       ret = tts_config_mgr_set_callback(client->uid, _tts_config_engine_changed_cb, __tts_config_voice_changed_cb, NULL, NULL, NULL, client->tts);
        if (0 != ret) {
                SLOG(LOG_ERROR, TAG_TTSC, "[ERROR] Fail to set config changed : %d", ret);
                tts_client_destroy(*tts);
                return __tts_convert_config_error_code(ret);
        }
 
+       ecore_main_loop_thread_safe_call_async(__pkgmgr_thread, NULL);
+
+       SLOG(LOG_INFO, TAG_TTSC, "[INFO] call ecore thread for creating pkgmgr thread");
+
+       g_engine_name = vconf_get_str(TTS_ENGINE_DB_DEFAULT);
+       if (NULL == g_engine_name) {
+               SLOG(LOG_ERROR, TAG_TTSC, "[ERROR] Fail to get engine name");
+       } else {
+               SLOG(LOG_ERROR, TAG_TTSC, "[INFO] Engine name(%s)", g_engine_name);
+       }
+
        SLOG(LOG_DEBUG, TAG_TTSC, "@@@");
        SLOG(LOG_DEBUG, TAG_TTSC, " ");
 
@@ -313,6 +413,14 @@ int tts_destroy(tts_h tts)
                return TTS_ERROR_OPERATION_FAILED;
        }
 
+       pthread_mutex_lock(&g_pkgmgr_mutex);
+       if (g_pkgmgr) {
+               pkgmgr_client_remove_listen_status(g_pkgmgr);
+               pkgmgr_client_free(g_pkgmgr);
+               g_pkgmgr = NULL;
+       }
+       pthread_mutex_unlock(&g_pkgmgr_mutex);
+
        tts_config_mgr_finalize(client->uid);
 
        int ret = -1;
@@ -364,6 +472,9 @@ int tts_destroy(tts_h tts)
                        ecore_timer_del(client->conn_timer);
                        client->conn_timer = NULL;
                }
+               /* Unset registered callbacks */
+               __tts_unset_all_callbacks(tts);
+
                /* Free resources */
                tts_client_destroy(tts);
                break;
@@ -627,8 +738,16 @@ static Eina_Bool __tts_connect_daemon(void *data)
                return EINA_FALSE;
        }
 
+       /* check whether engine is updating or not */
+       if (g_engine_update_status) {
+               SLOG(LOG_ERROR, TAG_TTSC, "[DEBUG] cannot prepare due to engine update");
+               __tts_cb_error(-1, TTS_ERROR_SERVICE_RESET, -1, "Daemon Reset");
+
+               return EINA_FALSE;
+       }
+
        /* Send hello */
-       if (0 != tts_dbus_request_hello(client->uid)) {
+       if (0 != tts_dbus_request_hello_sync(client->uid)) {
                return EINA_TRUE;
        }
 
@@ -704,6 +823,97 @@ static Eina_Bool __tts_connect_daemon(void *data)
        return EINA_FALSE;
 }
 
+int __tts_cb_hello(int uid, int ret, int credential_needed)
+{
+       tts_client_s* client = tts_client_get_by_uid(uid);
+       if (NULL == client) {
+               SLOG(LOG_ERROR, TAG_TTSC, "Fail to get TTS client or ignore this uid(%d)", uid);
+               return TTS_ERROR_OPERATION_FAILED;
+       }
+
+       if (g_hello_timer) {
+               ecore_timer_del(g_hello_timer);
+               g_hello_timer = NULL;
+       }
+
+       if (TTS_STATE_READY == client->current_state) {
+               SLOG(LOG_INFO, TAG_TTSC, "[INFO] tts client is already READY");
+               return TTS_ERROR_NONE;
+       }
+
+       if (TTS_ERROR_ENGINE_NOT_FOUND == ret) {
+               SLOG(LOG_ERROR, TAG_TTSC, "[ERROR] Fail to initialize : %s", __tts_get_error_code(ret));
+
+               client->reason = TTS_ERROR_ENGINE_NOT_FOUND;
+               client->utt_id = -1;
+
+               ecore_timer_add(0, __tts_notify_error, (void*)client->tts);
+               return TTS_ERROR_OPERATION_FAILED;
+
+       } else if (TTS_ERROR_PERMISSION_DENIED == ret) {
+               SLOG(LOG_ERROR, TAG_TTSC, "[ERROR] Fail to initialize : %s", __tts_get_error_code(ret));
+
+               client->reason = TTS_ERROR_PERMISSION_DENIED;
+               client->utt_id = -1;
+
+               ecore_timer_add(0, __tts_notify_error, (void*)client->tts);
+               return TTS_ERROR_PERMISSION_DENIED;
+
+       } else if (TTS_ERROR_NONE != ret) {
+               SLOG(LOG_WARN, TAG_TTSC, "[WARNING] Fail to connection. Retry to connect : %s", __tts_get_error_code(ret));
+               return TTS_ERROR_OPERATION_FAILED;
+
+       } else {
+               /* success to connect tts-daemon */
+               client->credential_needed = credential_needed;
+               SLOG(LOG_ERROR, TAG_TTSC, "Supported options : credential(%s)", credential_needed ? "need" : "no need");
+       }
+
+       client->before_state = client->current_state;
+       client->current_state = TTS_STATE_READY;
+
+       ecore_timer_add(0.0, __tts_notify_state_changed, client->tts);
+
+       SLOG(LOG_DEBUG, TAG_TTSC, "@@@");
+       return TTS_ERROR_NONE;
+}
+
+static Eina_Bool __send_hello(void *data)
+{
+       tts_h tts = (tts_h)data;
+       tts_client_s* client = tts_client_get(tts);
+
+       /* check handle */
+       if (NULL == client) {
+               SLOG(LOG_ERROR, TAG_TTSC, "[ERROR] A handle is not valid");
+               g_hello_timer = NULL;
+               return EINA_FALSE;
+       }
+
+       /* check state */
+       if (client->current_state == TTS_STATE_READY) {
+               SLOG(LOG_ERROR, TAG_TTSC, "[INFO] TTS client has been already connected to tts service"); //LCOV_EXCL_LINE
+               SLOG(LOG_DEBUG, TAG_TTSC, "@@@");
+               g_hello_timer = NULL;
+               return EINA_FALSE;
+       }
+
+       /* Send hello */
+       int ret = tts_dbus_request_hello(client->uid);
+       if (0 != ret)  {
+               SLOG(LOG_ERROR, TAG_TTSC, "[ERROR] Fail to request hello !!"); //LCOV_EXCL_LINE
+       } else {
+               SLOG(LOG_ERROR, TAG_TTSC, "@@@ Send Hello");
+       }
+
+       if (!g_hello_timer) {
+               SLOG(LOG_ERROR, TAG_TTSC, "@@@ Call checking Hello timer callback");
+               g_hello_timer = ecore_timer_add(0.5, __send_hello, tts);
+               return EINA_FALSE;
+       }
+       return EINA_TRUE;
+}
+
 int tts_prepare(tts_h tts)
 {
        if (0 != __tts_get_feature_enabled()) {
@@ -728,13 +938,12 @@ int tts_prepare(tts_h tts)
                return TTS_ERROR_INVALID_STATE;
        }
 
-       ecore_thread_main_loop_begin();
-       if (client->conn_timer) {
-               ecore_timer_del(client->conn_timer);
-               client->conn_timer = NULL;
+       if (NULL == g_hello_timer) {
+               SLOG(LOG_ERROR, TAG_TTSC, "@@@ Call checking Hello timer callback");
+               ecore_thread_main_loop_begin();
+               ecore_timer_add(0.0, __send_hello, (void*)tts);
+               ecore_thread_main_loop_end();
        }
-       client->conn_timer = ecore_timer_add(0.02, __tts_connect_daemon, (void*)tts);
-       ecore_thread_main_loop_end();
 
        SLOG(LOG_DEBUG, TAG_TTSC, "@@@");
 
@@ -1212,7 +1421,7 @@ int tts_add_text(tts_h tts, const char* text, const char* language, int voice_ty
        g_voice_type = voice_type;
        g_speed = speed;
 
-       SLOG(LOG_DEBUG, TAG_TTSC, "[DEBUG] repeat: text(%s), language(%s), voice type(%d), speed(%d)", client->text_repeat, g_language, g_voice_type, g_speed);
+       SLOG(LOG_DEBUG, TAG_TTSC, "[DEBUG] repeat: text(%s), language(%s), voice type(%d), speed(%d)", client->text_repeat, (g_language) ? g_language : "NULL", g_voice_type, g_speed);
 
        /* change default language value */
        char* temp = NULL;
@@ -1984,6 +2193,44 @@ static Eina_Bool __tts_notify_error(void *data)
        return EINA_FALSE;
 }
 
+static void __start_reprepare_thread(void* data, Ecore_Thread* thread)
+{
+       SLOG(LOG_ERROR, TAG_TTSC, "[DEBUG] start reprepare thread. engine update status(%d)", g_engine_update_status);
+
+       tts_client_s* temp = (tts_client_s*)data;
+       if (NULL == temp) {
+               SLOG(LOG_ERROR, TAG_TTSC, "[ERROR] data is null");
+               return ;
+       }
+
+       int cnt = 0;
+       while (!g_engine_update_status && (cnt < 10)) {
+               SLOG(LOG_WARN, TAG_TTSC, "[WARNING] wait for starting update");
+               usleep(50000);
+               cnt++;
+       }
+
+       SLOG(LOG_ERROR, TAG_TTSC, "[DEBUG] update status(%d)", g_engine_update_status);
+
+       while (g_engine_update_status && (NULL != g_pkgmgr)) {
+//             SLOG(LOG_WARN, TAG_TTSC, "[WARNING] wait for finishing update");
+               usleep(200000);
+       }
+
+       SLOG(LOG_INFO, TAG_TTSC, "[INFO] finish updating. request to prepare");
+
+       if (0 != tts_prepare(temp->tts)) {
+               SLOG(LOG_ERROR, TAG_TTSC, "[ERROR] Fail to prepare");
+       }
+
+       return ;
+}
+
+static void __end_reprepare_thread(void* data, Ecore_Thread* thread)
+{
+       SLOG(LOG_INFO, TAG_TTSC, "[INFO] end reprepare thread");
+}
+
 int __tts_cb_error(int uid, tts_error_e reason, int utt_id, char* err_msg)
 {
        if (-1 == uid) {
@@ -2020,9 +2267,8 @@ int __tts_cb_error(int uid, tts_error_e reason, int utt_id, char* err_msg)
                                        SLOG(LOG_WARN, TAG_TTSC, "[WARNING] Service Reset");
 
                                        data->current_state = TTS_STATE_CREATED;
-                                       if (0 != tts_prepare(data->tts)) {
-                                               SLOG(LOG_ERROR, TAG_TTSC, "[ERROR] Fail to prepare");
-                                       }
+                                       data->reason = 0;
+                                       ecore_thread_run(__start_reprepare_thread, __end_reprepare_thread, NULL, data);
                                }
 
                                /* Next item */
@@ -2057,9 +2303,8 @@ int __tts_cb_error(int uid, tts_error_e reason, int utt_id, char* err_msg)
                        SLOG(LOG_WARN, TAG_TTSC, "[WARNING] Service Reset");
 
                        client->current_state = TTS_STATE_CREATED;
-                       if (0 != tts_prepare(client->tts)) {
-                               SLOG(LOG_ERROR, TAG_TTSC, "[ERROR] Fail to prepare");
-                       }
+                       client->reason = 0;
+                       ecore_thread_run(__start_reprepare_thread, __end_reprepare_thread, NULL, client);
                }
        }
 
@@ -2828,7 +3073,7 @@ int tts_repeat(tts_h tts, char** text_repeat, int* utt_id)
                        return ret;
                }
                *text_repeat = strdup(client->text_repeat);
-               SLOG(LOG_DEBUG, TAG_TTSC, "[DEBUG] text to repeat(%s), utt_id(%d)", *text_repeat, *utt_id);
+               SLOG(LOG_DEBUG, TAG_TTSC, "[DEBUG] text to repeat(%s), utt_id(%d)", (*text_repeat) ? *text_repeat : "NULL", *utt_id);
                if (NULL != tmp_text) {
                        free(tmp_text);
                        tmp_text = NULL;