4 * Copyright (c) 2000 - 2012 Samsung Electronics Co., Ltd. All rights reserved.
6 * Contact: Jungki Kwak <jungki.kwak@samsung.com>, Keunsoon Lee <keunsoon.lee@samsung.com>
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
20 * @file download-agent-client-mgr.c
21 * @brief client manager module for notifying download ststus information
22 * @author Keunsoon Lee(keunsoon.lee@samsung.com)
23 * @author Jungki Kwak(jungki.kwak@samsung.com)
28 #include "download-agent-client-mgr.h"
29 #include "download-agent-debug.h"
30 #include "download-agent-utils.h"
31 #include "download-agent-file.h"
33 #define IS_CLIENT_Q_HAVING_DATA(QUEUE) (QUEUE->having_data)
35 static client_app_mgr_t client_app_mgr;
37 static da_result_t __launch_client_thread(void);
38 static void *__thread_for_client_noti(void *data);
39 void __thread_clean_up_handler_for_client_thread(void *arg);
40 static void __pop_client_noti(client_noti_t **out_client_noti);
42 void __client_q_goto_sleep_without_lock(void);
43 void __client_q_wake_up_without_lock(void);
44 void destroy_client_noti(client_noti_t *client_noti);
46 da_result_t init_client_app_mgr()
48 DA_LOG_FUNC_START(ClientNoti);
50 if(client_app_mgr.is_init)
53 client_app_mgr.is_init = DA_TRUE;
54 client_app_mgr.client_app_info.is_using = DA_FALSE;
55 client_app_mgr.client_app_info.is_manual_download = DA_FALSE;
56 client_app_mgr.client_app_info.client_user_agent = DA_NULL;
57 client_app_mgr.is_thread_init = DA_FALSE;
62 da_bool_t is_client_app_mgr_init(void)
64 return client_app_mgr.is_init;
67 da_bool_t is_client_app_mgr_manual_download(void)
69 return client_app_mgr.is_manual_download;
72 da_result_t reg_client_app(
73 da_client_cb_t *da_client_callback,
74 da_download_managing_method download_method
77 da_result_t ret = DA_RESULT_OK;
78 client_queue_t *queue = DA_NULL;
79 client_noti_t *client_noti = DA_NULL;
81 DA_LOG_FUNC_START(ClientNoti);
83 if (client_app_mgr.client_app_info.is_using)
84 return DA_ERR_CLIENT_IS_ALREADY_REGISTERED;
86 client_app_mgr.client_app_info.is_using = DA_TRUE;
87 if (download_method == DA_DOWNLOAD_MANAGING_METHOD_MANUAL)
88 client_app_mgr.client_app_info.is_manual_download = DA_TRUE;
90 client_app_mgr.client_app_info.is_manual_download = DA_FALSE;
92 memset(&(client_app_mgr.client_app_info.client_callback),
93 0, sizeof(da_client_cb_t));
94 memcpy(&(client_app_mgr.client_app_info.client_callback),
95 da_client_callback, sizeof(da_client_cb_t));
97 _da_thread_mutex_init(&(client_app_mgr.mutex_client_mgr), DA_NULL);
99 /* If some noti is existed at queue, delete all */
101 __pop_client_noti(&client_noti);
102 destroy_client_noti(client_noti);
103 } while(client_noti != DA_NULL);
105 queue = &(client_app_mgr.client_queue);
106 DA_LOG_VERBOSE(ClientNoti, "client queue = %p", queue);
107 _da_thread_mutex_init(&(queue->mutex_client_queue), DA_NULL);
108 _da_thread_cond_init(&(queue->cond_client_queue), DA_NULL);
110 ret = __launch_client_thread();
115 da_result_t dereg_client_app(void)
117 client_noti_t *client_noti = DA_NULL;
119 DA_LOG_FUNC_START(ClientNoti);
121 client_noti = (client_noti_t *)calloc(1, sizeof(client_noti_t));
123 DA_LOG_ERR(ClientNoti, "calloc fail");
124 return DA_ERR_FAIL_TO_MEMALLOC;
127 client_noti->download_id = DA_INVALID_ID;
128 client_noti->noti_type = Q_CLIENT_NOTI_TYPE_TERMINATE;
129 client_noti->next = DA_NULL;
131 _da_thread_mutex_lock(&(client_app_mgr.mutex_client_mgr));
132 if (client_app_mgr.is_thread_init != DA_TRUE) {
133 DA_LOG_CRITICAL(ClientNoti, "try to cancel client mgr thread id[%lu]", client_app_mgr.thread_id);
134 if (pthread_cancel(client_app_mgr.thread_id) < 0) {
135 DA_LOG_ERR(ClientNoti, "cancel thread is failed!!!");
138 void *t_return = NULL;
139 DA_LOG_VERBOSE(ClientNoti, "pushing Q_CLIENT_NOTI_TYPE_TERMINATE");
140 push_client_noti(client_noti);
141 DA_LOG_CRITICAL(Thread, "===try to join client mgr thread id[%lu]===", client_app_mgr.thread_id);
142 if (pthread_join(client_app_mgr.thread_id, &t_return) < 0) {
143 DA_LOG_ERR(Thread, "join client thread is failed!!!");
145 DA_LOG_CRITICAL(Thread, "===thread join return[%d]===", (char*)t_return);
147 _da_thread_mutex_unlock(&(client_app_mgr.mutex_client_mgr));
149 /* ToDo: This clean up should be done at the end of client_thread. */
150 client_app_mgr.client_app_info.is_using= DA_FALSE;
151 client_app_mgr.client_app_info.is_manual_download = DA_FALSE;
152 if(client_app_mgr.client_app_info.client_user_agent) {
153 free(client_app_mgr.client_app_info.client_user_agent);
154 client_app_mgr.client_app_info.client_user_agent = DA_NULL;
156 _da_thread_mutex_lock(&(client_app_mgr.mutex_client_mgr));
157 client_app_mgr.is_thread_init = DA_FALSE;
158 _da_thread_mutex_unlock(&(client_app_mgr.mutex_client_mgr));
159 _da_thread_mutex_destroy(&(client_app_mgr.mutex_client_mgr));
163 da_result_t send_client_da_state (int download_id, da_state state, int err)
165 client_noti_t *client_noti = DA_NULL;
166 user_notify_info_t *send_state_info = DA_NULL;
167 da_state cur_da_state;
169 DA_LOG_FUNC_START(ClientNoti);
171 DA_LOG_VERBOSE(ClientNoti, "da_state[%s], download_id[%d]", print_dl_state(state), download_id);
173 if (!is_valid_dl_ID(download_id)) {
174 DA_LOG_ERR(ClientNoti, "Download ID is not valid");
175 /* Returning DA_RESULT_OK if download_id is not valid,
176 * because sending da_state should not effect to download flow. */
180 if (state_watcher_need_redirect_Q(download_id)) {
181 state_watcher_redirect_state(download_id, state, err);
185 cur_da_state = GET_DL_DA_STATE(download_id);
187 if ((DA_STATE_SUSPENDED != state) && (cur_da_state == state)) {
188 DA_LOG(ClientNoti, "inserting da_state is same with current da_state! Not inserting! inserting: %d, cur : %d", state, cur_da_state);
192 GET_DL_DA_STATE(download_id) = state;
193 DA_LOG_VERBOSE(ClientNoti, "change da_state to %d", state);
195 client_noti = (client_noti_t *)calloc(1, sizeof(client_noti_t));
197 DA_LOG_ERR(ClientNoti, "calloc fail");
198 return DA_ERR_FAIL_TO_MEMALLOC;
201 client_noti->download_id = download_id;
202 client_noti->user_data = GET_DL_USER_DATA(download_id);
203 client_noti->noti_type = Q_CLIENT_NOTI_TYPE_SEND_STATE;
204 client_noti->next = DA_NULL;
206 send_state_info = (user_notify_info_t *)&(client_noti->type.da_state_info);
207 send_state_info->da_dl_req_id = GET_DL_REQ_ID(download_id);
208 send_state_info->state = state;
209 send_state_info->err = err;
211 DA_LOG(ClientNoti, "pushing da_state=%d, download_id=%d, err=%d, dl_req_id=%d",
212 state, download_id, err, GET_DL_REQ_ID(download_id));
214 push_client_noti(client_noti);
219 da_result_t send_client_update_downloading_info (
222 unsigned long int total_received_size,
226 client_noti_t *client_noti = DA_NULL;
227 user_downloading_info_t *downloading_info = DA_NULL;
229 DA_LOG_FUNC_START(ClientNoti);
231 if (!is_valid_dl_ID(download_id)) {
232 DA_LOG_ERR(ClientNoti, "Download ID is not valid");
233 return DA_ERR_INVALID_DL_REQ_ID;
236 client_noti = (client_noti_t *)calloc(1, sizeof(client_noti_t));
238 DA_LOG_ERR(ClientNoti, "calloc fail");
239 return DA_ERR_FAIL_TO_MEMALLOC;
242 client_noti->download_id = download_id;
243 client_noti->user_data = GET_DL_USER_DATA(download_id);
244 client_noti->noti_type = Q_CLIENT_NOTI_TYPE_UPDATE_DOWNLOADING_INFO;
245 client_noti->next = DA_NULL;
247 downloading_info = (user_downloading_info_t *)&(client_noti->type.update_downloading_info);
248 downloading_info->da_dl_req_id = dl_req_id;
249 downloading_info->total_received_size = total_received_size;
251 /* These strings MUST be copied to detach __thread_for_client_noti from download_info */
253 downloading_info->saved_path = strdup(saved_path);
254 DA_LOG(ClientNoti, "pushing received_size=%lu, download_id=%d, dl_req_id=%d",
255 total_received_size, download_id, dl_req_id);
257 push_client_noti(client_noti);
262 da_result_t send_client_update_dl_info (
266 unsigned long int file_size,
267 char *tmp_saved_path,
268 char *http_response_header,
269 char *http_chunked_data
272 client_noti_t *client_noti = DA_NULL;
273 user_download_info_t *update_dl_info = DA_NULL;
275 DA_LOG_FUNC_START(ClientNoti);
277 if (!is_valid_dl_ID(download_id)) {
278 DA_LOG_ERR(ClientNoti, "Download ID is not valid");
279 return DA_ERR_INVALID_DL_REQ_ID;
282 client_noti = (client_noti_t *)calloc(1, sizeof(client_noti_t));
284 DA_LOG_ERR(ClientNoti, "calloc fail");
285 return DA_ERR_FAIL_TO_MEMALLOC;
288 client_noti->download_id = download_id;
289 client_noti->user_data = GET_DL_USER_DATA(download_id);
290 client_noti->noti_type = Q_CLIENT_NOTI_TYPE_UPDATE_DL_INFO;
291 client_noti->next = DA_NULL;
293 update_dl_info = (user_download_info_t *)&(client_noti->type.update_dl_info);
294 update_dl_info->da_dl_req_id = dl_req_id;
295 update_dl_info->file_size = file_size;
297 /* These strings MUST be copied to detach __thread_for_client_noti from download_info */
299 update_dl_info->file_type = strdup(file_type);
302 update_dl_info->tmp_saved_path = strdup(tmp_saved_path);
304 if (http_response_header) {
305 update_dl_info->http_response_header = strdup(http_response_header);
307 if (http_chunked_data) {
308 update_dl_info->http_chunked_data = calloc (1, file_size);
309 if (update_dl_info->http_chunked_data)
310 memcpy(update_dl_info->http_chunked_data, http_chunked_data,
313 DA_LOG(ClientNoti, "pushing file_size=%lu, download_id=%d, dl_req_id=%d",
314 file_size, download_id, dl_req_id);
316 push_client_noti(client_noti);
321 da_result_t __launch_client_thread(void)
323 pthread_t thread_id = DA_NULL;
325 DA_LOG_FUNC_START(Thread);
327 if (pthread_create(&thread_id,DA_NULL,__thread_for_client_noti,DA_NULL) < 0) {
328 DA_LOG_ERR(Thread, "making thread failed..");
329 return DA_ERR_FAIL_TO_CREATE_THREAD;
331 DA_LOG(Thread, "client mgr thread id[%d]", thread_id);
332 client_app_mgr.thread_id = thread_id;
336 void destroy_client_noti(client_noti_t *client_noti)
339 if (client_noti->noti_type == Q_CLIENT_NOTI_TYPE_UPDATE_DL_INFO) {
340 user_download_info_t *update_dl_info = DA_NULL;
341 update_dl_info = (user_download_info_t*)&(client_noti->type.update_dl_info);
342 if (update_dl_info->file_type) {
343 free(update_dl_info->file_type);
344 update_dl_info->file_type = DA_NULL;
346 if (update_dl_info->tmp_saved_path) {
347 free(update_dl_info->tmp_saved_path);
348 update_dl_info->tmp_saved_path = DA_NULL;
350 if (update_dl_info->http_response_header) {
351 free(update_dl_info->http_response_header);
352 update_dl_info->http_response_header = DA_NULL;
354 if (update_dl_info->http_chunked_data) {
355 free(update_dl_info->http_chunked_data);
356 update_dl_info->http_chunked_data = DA_NULL;
358 } else if (client_noti->noti_type ==
359 Q_CLIENT_NOTI_TYPE_UPDATE_DOWNLOADING_INFO) {
360 user_downloading_info_t *downloading_info = DA_NULL;
361 downloading_info = (user_downloading_info_t*)&(client_noti->type.update_downloading_info);
362 if (downloading_info->saved_path) {
363 free(downloading_info->saved_path);
364 downloading_info->saved_path = DA_NULL;
372 void push_client_noti(client_noti_t *client_noti)
374 client_queue_t *queue = DA_NULL;
375 client_noti_t *head = DA_NULL;
376 client_noti_t *pre = DA_NULL;
377 client_noti_t *cur = DA_NULL;
379 /* DA_LOG_FUNC_START(ClientNoti); */
381 if(!is_this_client_available()) {
382 DA_LOG_ERR(ClientNoti, "invalid client");
385 queue = &(client_app_mgr.client_queue);
386 _da_thread_mutex_lock (&(queue->mutex_client_queue));
388 head = queue->client_q_head;
390 queue->client_q_head = client_noti;
398 if (DA_TRUE == is_this_client_manual_download_type()) {
399 cur->next = client_noti;
400 } else if (client_noti->noti_type == Q_CLIENT_NOTI_TYPE_SEND_STATE) {
401 cur->next = client_noti;
403 if (cur->noti_type == Q_CLIENT_NOTI_TYPE_SEND_STATE) {
404 cur->next = client_noti;
405 } else if (cur->noti_type == Q_CLIENT_NOTI_TYPE_UPDATE_DOWNLOADING_INFO) {
406 /* For UI performance. If the update noti info is existed at queue,
407 replace it with new update noti info */
408 if (cur->download_id == client_noti->download_id) {
409 /* DA_LOG(ClientNoti, "exchange queue's tail and pushing item"); */
411 queue->client_q_head = client_noti;
413 pre->next = client_noti;
414 destroy_client_noti(cur);
416 cur->next = client_noti;
422 queue->having_data = DA_TRUE;
424 __client_q_wake_up_without_lock();
426 _da_thread_mutex_unlock (&(queue->mutex_client_queue));
429 void __pop_client_noti(client_noti_t **out_client_noti)
431 client_queue_t *queue = DA_NULL;
433 /* DA_LOG_FUNC_START(ClientNoti); */
435 queue = &(client_app_mgr.client_queue);
437 _da_thread_mutex_lock (&(queue->mutex_client_queue));
439 if (queue->client_q_head) {
440 *out_client_noti = queue->client_q_head;
441 queue->client_q_head = queue->client_q_head->next;
443 *out_client_noti = DA_NULL;
446 if (queue->client_q_head == DA_NULL) {
447 queue->having_data = DA_FALSE;
450 _da_thread_mutex_unlock (&(queue->mutex_client_queue));
453 void __client_q_goto_sleep_without_lock(void)
455 client_queue_t *queue = DA_NULL;
457 /* DA_LOG_FUNC_START(ClientNoti); */
459 queue = &(client_app_mgr.client_queue);
460 _da_thread_cond_wait(&(queue->cond_client_queue), &(queue->mutex_client_queue));
463 void __client_q_wake_up_without_lock(void)
465 client_queue_t *queue = DA_NULL;
467 /* DA_LOG_FUNC_START(ClientNoti); */
469 queue = &(client_app_mgr.client_queue);
470 _da_thread_cond_signal(&(queue->cond_client_queue));
473 void __thread_clean_up_handler_for_client_thread(void *arg)
475 DA_LOG_CRITICAL(Thread, "cleanup for thread id = %d", pthread_self());
478 static void *__thread_for_client_noti(void *data)
480 da_result_t ret = DA_RESULT_OK;
481 da_bool_t need_wait = DA_TRUE;
482 client_queue_t *queue = DA_NULL;
483 client_noti_t *client_noti = DA_NULL;
485 DA_LOG_FUNC_START(Thread);
487 _da_thread_mutex_lock(&(client_app_mgr.mutex_client_mgr));
488 client_app_mgr.is_thread_init = DA_TRUE;
489 _da_thread_mutex_unlock(&(client_app_mgr.mutex_client_mgr));
491 queue = &(client_app_mgr.client_queue);
492 DA_LOG(ClientNoti, "client queue = %p", queue);
494 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, DA_NULL);
495 pthread_cleanup_push(__thread_clean_up_handler_for_client_thread, (void *)DA_NULL);
498 _da_thread_mutex_lock(&(queue->mutex_client_queue));
499 if (DA_FALSE == IS_CLIENT_Q_HAVING_DATA(queue)) {
500 DA_LOG(Thread, "Sleep @ thread_for_client_noti!");
501 __client_q_goto_sleep_without_lock();
502 DA_LOG(Thread, "Woke up @ thread_for_client_noti");
504 _da_thread_mutex_unlock(&(queue->mutex_client_queue));
507 __pop_client_noti(&client_noti);
508 if (client_noti == DA_NULL) {
509 DA_LOG_ERR(ClientNoti, "There is no data on client queue!");
510 ret = DA_ERR_INVALID_STATE;
511 need_wait = DA_FALSE;
513 switch (client_noti->noti_type) {
514 case Q_CLIENT_NOTI_TYPE_UPDATE_DL_INFO:
516 user_download_info_t *update_dl_info = DA_NULL;;
517 update_dl_info = (user_download_info_t*)(&(client_noti->type.update_dl_info));
518 if (client_app_mgr.client_app_info.client_callback.update_dl_info_cb) {
519 client_app_mgr.client_app_info.client_callback.update_dl_info_cb(update_dl_info, client_noti->user_data);
520 DA_LOG(ClientNoti, "Update download info for download_id=%d, dl_req_id=%d, received size=%lu- DONE",
521 client_noti->download_id,
522 update_dl_info->da_dl_req_id,
523 update_dl_info->file_size
528 case Q_CLIENT_NOTI_TYPE_UPDATE_DOWNLOADING_INFO:
530 user_downloading_info_t *downloading_info = DA_NULL;;
531 downloading_info = (user_downloading_info_t*)(&(client_noti->type.update_downloading_info));
532 if (client_app_mgr.client_app_info.client_callback.update_progress_info_cb) {
533 client_app_mgr.client_app_info.client_callback.update_progress_info_cb(downloading_info, client_noti->user_data);
534 DA_LOG(ClientNoti, "Update downloading info for download_id=%d, dl_req_id=%d, received size=%lu - DONE",
535 client_noti->download_id,
536 downloading_info->da_dl_req_id,
537 downloading_info->total_received_size);
541 case Q_CLIENT_NOTI_TYPE_SEND_STATE:
543 user_notify_info_t *da_state_info = DA_NULL;
544 da_state_info = (user_notify_info_t *)(&(client_noti->type.da_state_info));
546 if (client_app_mgr.client_app_info.client_callback.user_noti_cb) {
547 DA_LOG(ClientNoti, "User Noti info for download_id=%d, dl_req_id=%d, da_state=%d, err=%d",
548 client_noti->download_id,
549 da_state_info->da_dl_req_id, da_state_info->state,
551 client_app_mgr.client_app_info.client_callback.user_noti_cb(da_state_info, client_noti->user_data);
552 DA_LOG(ClientNoti, "User Noti info for download_id=%d, dl_req_id=%d, da_state=%d, err=%d - DONE",
553 client_noti->download_id,
554 da_state_info->da_dl_req_id,
555 da_state_info->state, da_state_info->err);
559 case Q_CLIENT_NOTI_TYPE_TERMINATE:
560 DA_LOG_CRITICAL(ClientNoti, "Q_CLIENT_NOTI_TYPE_TERMINATE");
561 need_wait = DA_FALSE;
564 destroy_client_noti(client_noti);
567 if(DA_TRUE == need_wait) {
568 _da_thread_mutex_lock(&(queue->mutex_client_queue));
569 if (DA_FALSE == IS_CLIENT_Q_HAVING_DATA(queue)) {
570 _da_thread_mutex_unlock (&(queue->mutex_client_queue));
573 _da_thread_mutex_unlock (&(queue->mutex_client_queue));
579 } while (DA_TRUE == need_wait);
581 _da_thread_mutex_destroy(&(queue->mutex_client_queue));
582 _da_thread_cond_destroy(&(queue->cond_client_queue));
584 pthread_cleanup_pop(0);
585 DA_LOG_CRITICAL(Thread, "=====thread_for_client_noti- EXIT=====");
586 pthread_exit((void *)NULL);
590 da_bool_t is_this_client_available(void)
592 return client_app_mgr.client_app_info.is_using;
595 da_bool_t is_this_client_manual_download_type(void)
597 return client_app_mgr.client_app_info.is_manual_download;
600 da_result_t get_client_download_path(char **out_path)
603 DA_LOG_ERR(ClientNoti, "DA_ERR_INVALID_ARGUMENT");
604 return DA_ERR_INVALID_ARGUMENT;
607 /* change the directory policy. it doesn't clean the temp direcoty when deinitializing */
608 *out_path = strdup(DA_DEFAULT_TMP_FILE_DIR_PATH);
609 DA_LOG_VERBOSE(ClientNoti, "client download path = [%s]", *out_path);
614 char *get_client_user_agent_string(void)
616 if (!client_app_mgr.is_init)
619 return client_app_mgr.client_app_info.client_user_agent;