2 * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the License);
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an AS IS BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include "download-agent-client-mgr.h"
20 #include "download-agent-debug.h"
21 #include "download-agent-utils.h"
22 #include "download-agent-file.h"
24 #define IS_CLIENT_Q_HAVING_DATA(QUEUE) (QUEUE->having_data)
26 static client_app_mgr_t client_app_mgr;
28 static da_result_t __launch_client_thread(void);
29 static void *__thread_for_client_noti(void *data);
30 void __thread_clean_up_handler_for_client_thread(void *arg);
31 static void __pop_client_noti(client_noti_t **out_client_noti);
33 void __client_q_goto_sleep_without_lock(void);
34 void __client_q_wake_up_without_lock(void);
35 void destroy_client_noti(client_noti_t *client_noti);
37 da_result_t init_client_app_mgr()
39 DA_LOG_FUNC_START(ClientNoti);
41 if(client_app_mgr.is_init)
44 client_app_mgr.is_init = DA_TRUE;
45 client_app_mgr.client_app_info.client_user_agent = DA_NULL;
46 client_app_mgr.is_thread_init = DA_FALSE;
51 da_bool_t is_client_app_mgr_init(void)
53 return client_app_mgr.is_init;
56 da_result_t reg_client_app(
57 da_client_cb_t *da_client_callback)
59 da_result_t ret = DA_RESULT_OK;
60 client_queue_t *queue = DA_NULL;
61 client_noti_t *client_noti = DA_NULL;
63 DA_LOG_FUNC_START(ClientNoti);
65 memset(&(client_app_mgr.client_app_info.client_callback),
66 0, sizeof(da_client_cb_t));
67 memcpy(&(client_app_mgr.client_app_info.client_callback),
68 da_client_callback, sizeof(da_client_cb_t));
70 _da_thread_mutex_init(&(client_app_mgr.mutex_client_mgr), DA_NULL);
72 /* If some noti is existed at queue, delete all */
74 __pop_client_noti(&client_noti);
75 destroy_client_noti(client_noti);
76 } while(client_noti != DA_NULL);
78 queue = &(client_app_mgr.client_queue);
79 DA_LOG_VERBOSE(ClientNoti, "client queue = %p", queue);
80 _da_thread_mutex_init(&(queue->mutex_client_queue), DA_NULL);
81 _da_thread_cond_init(&(queue->cond_client_queue), DA_NULL);
83 ret = __launch_client_thread();
88 da_result_t dereg_client_app(void)
90 client_noti_t *client_noti = DA_NULL;
92 DA_LOG_FUNC_START(ClientNoti);
94 client_noti = (client_noti_t *)calloc(1, sizeof(client_noti_t));
96 DA_LOG_ERR(ClientNoti, "calloc fail");
97 return DA_ERR_FAIL_TO_MEMALLOC;
100 client_noti->slot_id = DA_INVALID_ID;
101 client_noti->noti_type = Q_CLIENT_NOTI_TYPE_TERMINATE;
102 client_noti->next = DA_NULL;
104 _da_thread_mutex_lock(&(client_app_mgr.mutex_client_mgr));
105 if (client_app_mgr.is_thread_init != DA_TRUE) {
106 DA_LOG_CRITICAL(ClientNoti, "try to cancel client mgr thread id[%lu]", client_app_mgr.thread_id);
107 if (pthread_cancel(client_app_mgr.thread_id) < 0) {
108 DA_LOG_ERR(ClientNoti, "cancel thread is failed!!!");
112 void *t_return = NULL;
113 DA_LOG_VERBOSE(ClientNoti, "pushing Q_CLIENT_NOTI_TYPE_TERMINATE");
114 push_client_noti(client_noti);
115 DA_LOG_CRITICAL(Thread, "===try to join client mgr thread id[%lu]===", client_app_mgr.thread_id);
116 if (pthread_join(client_app_mgr.thread_id, &t_return) < 0) {
117 DA_LOG_ERR(Thread, "join client thread is failed!!!");
119 DA_LOG_CRITICAL(Thread, "===thread join return[%d]===", (char*)t_return);
121 _da_thread_mutex_unlock(&(client_app_mgr.mutex_client_mgr));
123 /* ToDo: This clean up should be done at the end of client_thread. */
124 if(client_app_mgr.client_app_info.client_user_agent) {
125 free(client_app_mgr.client_app_info.client_user_agent);
126 client_app_mgr.client_app_info.client_user_agent = DA_NULL;
128 _da_thread_mutex_lock(&(client_app_mgr.mutex_client_mgr));
129 client_app_mgr.is_thread_init = DA_FALSE;
130 _da_thread_mutex_unlock(&(client_app_mgr.mutex_client_mgr));
131 _da_thread_mutex_destroy(&(client_app_mgr.mutex_client_mgr));
135 da_result_t send_client_paused_info(int slot_id)
137 client_noti_t *client_noti = DA_NULL;
138 user_paused_info_t *paused_info = DA_NULL;
139 download_state_t state = GET_DL_STATE_ON_ID(slot_id);
141 DA_LOG_FUNC_START(ClientNoti);
143 if (!GET_DL_ENABLE_PAUSE_UPDATE(slot_id)) {
144 DA_LOG(ClientNoti, "Do not call pause cb");
147 if (!is_valid_slot_id(slot_id)) {
148 DA_LOG_ERR(ClientNoti, "Download ID is not valid");
152 DA_LOG_VERBOSE(ClientNoti, "slot_id[%d]", slot_id);
153 if ((DOWNLOAD_STATE_PAUSED != state)) {
154 DA_LOG(ClientNoti, "The state is not paused. state:%d", state);
155 return DA_ERR_INVALID_STATE;
158 client_noti = (client_noti_t *)calloc(1, sizeof(client_noti_t));
160 DA_LOG_ERR(ClientNoti, "calloc fail");
161 return DA_ERR_FAIL_TO_MEMALLOC;
164 client_noti->slot_id = slot_id;
165 client_noti->user_data = GET_DL_USER_DATA(slot_id);
166 client_noti->noti_type = Q_CLIENT_NOTI_TYPE_PAUSED_INFO;
167 client_noti->next = DA_NULL;
169 paused_info = (user_paused_info_t *)&(client_noti->type.paused_info);
170 paused_info->download_id= GET_DL_ID(slot_id);
171 DA_LOG(ClientNoti, "pushing paused info. slot_id=%d, dl_id=%d",
172 slot_id, GET_DL_ID(slot_id));
174 push_client_noti(client_noti);
179 da_result_t send_client_update_progress_info (
182 unsigned long int received_size
185 client_noti_t *client_noti = DA_NULL;
186 user_progress_info_t *progress_info = DA_NULL;
188 //DA_LOG_FUNC_START(ClientNoti);
190 if (!is_valid_slot_id(slot_id)) {
191 DA_LOG_ERR(ClientNoti, "Download ID is not valid");
192 return DA_ERR_INVALID_DL_REQ_ID;
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->slot_id = slot_id;
202 client_noti->user_data = GET_DL_USER_DATA(slot_id);
203 client_noti->noti_type = Q_CLIENT_NOTI_TYPE_PROGRESS_INFO;
204 client_noti->next = DA_NULL;
206 progress_info = (user_progress_info_t *)&(client_noti->type.update_progress_info);
207 progress_info->download_id= dl_id;
208 progress_info->received_size = received_size;
210 DA_LOG_VERBOSE(ClientNoti, "pushing received_size=%lu, slot_id=%d, dl_id=%d",
211 received_size, slot_id, dl_id);
213 push_client_noti(client_noti);
218 da_result_t send_client_update_dl_info (
222 unsigned long int file_size,
223 char *tmp_saved_path,
224 char *pure_file_name,
228 client_noti_t *client_noti = DA_NULL;
229 user_download_info_t *update_dl_info = DA_NULL;
232 DA_LOG_FUNC_START(ClientNoti);
234 if (!is_valid_slot_id(slot_id)) {
235 DA_LOG_ERR(ClientNoti, "Download ID is not valid");
236 return DA_ERR_INVALID_DL_REQ_ID;
239 client_noti = (client_noti_t *)calloc(1, sizeof(client_noti_t));
241 DA_LOG_ERR(ClientNoti, "calloc fail");
242 return DA_ERR_FAIL_TO_MEMALLOC;
245 client_noti->slot_id = slot_id;
246 client_noti->user_data = GET_DL_USER_DATA(slot_id);
247 client_noti->noti_type = Q_CLIENT_NOTI_TYPE_STARTED_INFO;
248 client_noti->next = DA_NULL;
250 update_dl_info = (user_download_info_t *)&(client_noti->type.update_dl_info);
251 update_dl_info->download_id = dl_id;
252 update_dl_info->file_size = file_size;
253 if (pure_file_name && extension) {
254 len = strlen(pure_file_name) + strlen(extension) + 1;
255 update_dl_info->content_name = (char *)calloc(len + 1, sizeof(char));
256 if (!update_dl_info->content_name) {
258 return DA_ERR_FAIL_TO_MEMALLOC;
260 snprintf(update_dl_info->content_name, len + 1, "%s.%s",
261 pure_file_name, extension);
264 /* These strings MUST be copied to detach __thread_for_client_noti from download_info */
266 update_dl_info->file_type = strdup(file_type);
269 update_dl_info->tmp_saved_path = strdup(tmp_saved_path);
272 update_dl_info->etag = strdup(etag);
273 DA_LOG(ClientNoti, "pushing file_size=%lu, slot_id=%d, dl_id=%d",
274 file_size, slot_id, dl_id);
276 push_client_noti(client_noti);
281 da_result_t send_client_finished_info (
290 client_noti_t *client_noti = DA_NULL;
291 user_finished_info_t *finished_info = DA_NULL;
293 DA_LOG_FUNC_START(ClientNoti);
295 if (!is_valid_slot_id(slot_id)) {
296 DA_LOG_ERR(ClientNoti, "Download ID is not valid");
297 return DA_ERR_INVALID_DL_REQ_ID;
300 client_noti = (client_noti_t *)calloc(1, sizeof(client_noti_t));
302 DA_LOG_ERR(ClientNoti, "calloc fail");
303 return DA_ERR_FAIL_TO_MEMALLOC;
306 client_noti->slot_id = slot_id;
307 client_noti->user_data = GET_DL_USER_DATA(slot_id);
308 client_noti->noti_type = Q_CLIENT_NOTI_TYPE_FINISHED_INFO;
309 client_noti->next = DA_NULL;
311 finished_info = (user_finished_info_t *)&(client_noti->type.finished_info);
312 finished_info->download_id = dl_id;
313 finished_info->err = error;
314 finished_info->http_status = http_status;
317 finished_info->saved_path = strdup(saved_path);
318 DA_LOG(ClientNoti, "saved path=%s", saved_path);
321 finished_info->etag = strdup(etag);
322 DA_LOG(ClientNoti, "pushing finished info. etag[%s]", etag);
324 DA_LOG(ClientNoti, "user_data=%p", client_noti->user_data);
325 DA_LOG(ClientNoti, "http_status=%d", http_status);
326 DA_LOG(ClientNoti, "pushing slot_id=%d, dl_id=%d err=%d", slot_id, dl_id, error);
328 push_client_noti(client_noti);
333 da_result_t __launch_client_thread(void)
335 pthread_t thread_id = DA_NULL;
337 DA_LOG_FUNC_START(Thread);
339 if (pthread_create(&thread_id,DA_NULL,__thread_for_client_noti,DA_NULL) < 0) {
340 DA_LOG_ERR(Thread, "making thread failed..");
341 return DA_ERR_FAIL_TO_CREATE_THREAD;
343 DA_LOG(Thread, "client mgr thread id[%d]", thread_id);
344 client_app_mgr.thread_id = thread_id;
348 void destroy_client_noti(client_noti_t *client_noti)
351 if (client_noti->noti_type == Q_CLIENT_NOTI_TYPE_STARTED_INFO) {
352 user_download_info_t *update_dl_info = DA_NULL;
353 update_dl_info = (user_download_info_t*)&(client_noti->type.update_dl_info);
354 if (update_dl_info->file_type) {
355 free(update_dl_info->file_type);
356 update_dl_info->file_type = DA_NULL;
358 if (update_dl_info->tmp_saved_path) {
359 free(update_dl_info->tmp_saved_path);
360 update_dl_info->tmp_saved_path = DA_NULL;
362 if (update_dl_info->etag) {
363 free(update_dl_info->etag);
364 update_dl_info->etag = DA_NULL;
366 } else if (client_noti->noti_type ==
367 Q_CLIENT_NOTI_TYPE_FINISHED_INFO) {
368 user_finished_info_t *finished_info = DA_NULL;
369 finished_info = (user_finished_info_t*)
370 &(client_noti->type.finished_info);
371 if (finished_info->saved_path) {
372 free(finished_info->saved_path);
373 finished_info->saved_path = DA_NULL;
375 if (finished_info->etag) {
376 free(finished_info->etag);
377 finished_info->etag = DA_NULL;
385 void push_client_noti(client_noti_t *client_noti)
387 client_queue_t *queue = DA_NULL;
388 client_noti_t *head = DA_NULL;
389 client_noti_t *pre = DA_NULL;
390 client_noti_t *cur = DA_NULL;
392 /* DA_LOG_FUNC_START(ClientNoti); */
394 queue = &(client_app_mgr.client_queue);
395 _da_thread_mutex_lock (&(queue->mutex_client_queue));
397 head = queue->client_q_head;
399 queue->client_q_head = client_noti;
407 if (cur->noti_type == Q_CLIENT_NOTI_TYPE_PROGRESS_INFO) {
408 /* For UI performance. If the update noti info is existed at queue,
409 replace it with new update noti info */
410 if (cur->slot_id == client_noti->slot_id) {
411 /* DA_LOG(ClientNoti, "exchange queue's tail and pushing item"); */
413 queue->client_q_head = client_noti;
415 pre->next = client_noti;
416 destroy_client_noti(cur);
418 cur->next = client_noti;
421 cur->next = client_noti;
424 cur->next = client_noti;
428 queue->having_data = DA_TRUE;
430 __client_q_wake_up_without_lock();
431 if (queue->client_q_head->next) {
432 DA_LOG_VERBOSE(ClientNoti, "client noti[%p] next noti[%p]",
433 queue->client_q_head, queue->client_q_head->next);
435 DA_LOG_VERBOSE(ClientNoti, "client noti[%p] next noti is NULL",
436 queue->client_q_head);
439 _da_thread_mutex_unlock (&(queue->mutex_client_queue));
442 void __pop_client_noti(client_noti_t **out_client_noti)
444 client_queue_t *queue = DA_NULL;
446 /* DA_LOG_FUNC_START(ClientNoti); */
448 queue = &(client_app_mgr.client_queue);
450 _da_thread_mutex_lock (&(queue->mutex_client_queue));
452 if (queue->client_q_head) {
453 *out_client_noti = queue->client_q_head;
454 queue->client_q_head = queue->client_q_head->next;
455 if (queue->client_q_head) {
456 DA_LOG_VERBOSE(ClientNoti, "client noti[%p] next noti[%p]",
457 *out_client_noti, queue->client_q_head);
459 DA_LOG_VERBOSE(ClientNoti, "client noti[%p] next noti is NULL",
463 *out_client_noti = DA_NULL;
466 if (queue->client_q_head == DA_NULL) {
467 queue->having_data = DA_FALSE;
470 _da_thread_mutex_unlock (&(queue->mutex_client_queue));
473 void __client_q_goto_sleep_without_lock(void)
475 client_queue_t *queue = DA_NULL;
477 /* DA_LOG_FUNC_START(ClientNoti); */
479 queue = &(client_app_mgr.client_queue);
480 _da_thread_cond_wait(&(queue->cond_client_queue), &(queue->mutex_client_queue));
483 void __client_q_wake_up_without_lock(void)
485 client_queue_t *queue = DA_NULL;
487 /* DA_LOG_FUNC_START(ClientNoti); */
489 queue = &(client_app_mgr.client_queue);
490 _da_thread_cond_signal(&(queue->cond_client_queue));
493 void __thread_clean_up_handler_for_client_thread(void *arg)
495 DA_LOG_CRITICAL(Thread, "cleanup for thread id = %d", pthread_self());
498 static void *__thread_for_client_noti(void *data)
500 da_result_t ret = DA_RESULT_OK;
501 da_bool_t need_wait = DA_TRUE;
502 client_queue_t *queue = DA_NULL;
503 client_noti_t *client_noti = DA_NULL;
505 //DA_LOG_FUNC_START(Thread);
507 _da_thread_mutex_lock(&(client_app_mgr.mutex_client_mgr));
508 client_app_mgr.is_thread_init = DA_TRUE;
509 _da_thread_mutex_unlock(&(client_app_mgr.mutex_client_mgr));
511 queue = &(client_app_mgr.client_queue);
512 DA_LOG_VERBOSE(ClientNoti, "client queue = %p", queue);
514 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, DA_NULL);
515 pthread_cleanup_push(__thread_clean_up_handler_for_client_thread, (void *)DA_NULL);
518 _da_thread_mutex_lock(&(queue->mutex_client_queue));
519 if (DA_FALSE == IS_CLIENT_Q_HAVING_DATA(queue)) {
520 DA_LOG_VERBOSE(Thread, "Sleep @ thread_for_client_noti!");
521 __client_q_goto_sleep_without_lock();
522 DA_LOG_VERBOSE(Thread, "Woke up @ thread_for_client_noti");
524 _da_thread_mutex_unlock(&(queue->mutex_client_queue));
527 __pop_client_noti(&client_noti);
528 if (client_noti == DA_NULL) {
529 DA_LOG_ERR(ClientNoti, "There is no data on client queue!");
530 ret = DA_ERR_INVALID_STATE;
531 need_wait = DA_FALSE;
533 DA_LOG_VERBOSE(ClientNoti, "noti type[%d]",
534 client_noti->noti_type);
535 switch (client_noti->noti_type) {
536 case Q_CLIENT_NOTI_TYPE_STARTED_INFO:
538 user_download_info_t *update_dl_info = DA_NULL;;
539 update_dl_info = (user_download_info_t*)(&(client_noti->type.update_dl_info));
540 if (client_app_mgr.client_app_info.client_callback.update_dl_info_cb) {
541 client_app_mgr.client_app_info.client_callback.update_dl_info_cb(update_dl_info, client_noti->user_data);
542 if (update_dl_info->etag)
543 DA_LOG(ClientNoti, "Etag:[%s]", update_dl_info->etag);
544 DA_LOG(ClientNoti, "Update download info for slot_id=%d, dl_id=%d, received size=%lu- DONE",
545 client_noti->slot_id,
546 update_dl_info->download_id,
547 update_dl_info->file_size
552 case Q_CLIENT_NOTI_TYPE_PROGRESS_INFO:
554 user_progress_info_t *progress_info = DA_NULL;;
555 progress_info = (user_progress_info_t*)(&(client_noti->type.update_progress_info));
556 if (client_app_mgr.client_app_info.client_callback.update_progress_info_cb) {
557 client_app_mgr.client_app_info.client_callback.update_progress_info_cb(progress_info, client_noti->user_data);
558 DA_LOG_VERBOSE(ClientNoti, "Update downloading info for slot_id=%d, dl_id=%d, received size=%lu - DONE",
559 client_noti->slot_id,
560 progress_info->download_id,
561 progress_info->received_size);
565 case Q_CLIENT_NOTI_TYPE_FINISHED_INFO:
567 user_finished_info_t *finished_info = DA_NULL;;
568 finished_info = (user_finished_info_t*)(&(client_noti->type.finished_info));
569 if (client_app_mgr.client_app_info.client_callback.finished_info_cb) {
570 client_app_mgr.client_app_info.client_callback.finished_info_cb(
571 finished_info, client_noti->user_data);
572 DA_LOG(ClientNoti, "Completed info for slot_id=%d, dl_id=%d, saved_path=%s etag=%s err=%d http_state=%d user_data=%p- DONE",
573 client_noti->slot_id,
574 finished_info->download_id,
575 finished_info->saved_path,
578 finished_info->http_status,
579 client_noti->user_data);
583 case Q_CLIENT_NOTI_TYPE_PAUSED_INFO:
585 user_paused_info_t *da_paused_info = DA_NULL;
586 da_paused_info = (user_paused_info_t *)(&(client_noti->type.paused_info));
588 if (client_app_mgr.client_app_info.client_callback.paused_info_cb) {
589 DA_LOG(ClientNoti, "User Paused info for slot_id=%d, dl_id=%d - Done",
590 client_noti->slot_id,
591 da_paused_info->download_id);
592 client_app_mgr.client_app_info.client_callback.paused_info_cb(
593 da_paused_info, client_noti->user_data);
597 case Q_CLIENT_NOTI_TYPE_TERMINATE:
598 DA_LOG_CRITICAL(ClientNoti, "Q_CLIENT_NOTI_TYPE_TERMINATE");
599 need_wait = DA_FALSE;
602 destroy_client_noti(client_noti);
605 if(DA_TRUE == need_wait) {
606 _da_thread_mutex_lock(&(queue->mutex_client_queue));
607 if (DA_FALSE == IS_CLIENT_Q_HAVING_DATA(queue)) {
608 _da_thread_mutex_unlock (&(queue->mutex_client_queue));
611 _da_thread_mutex_unlock (&(queue->mutex_client_queue));
617 } while (DA_TRUE == need_wait);
619 _da_thread_mutex_destroy(&(queue->mutex_client_queue));
620 _da_thread_cond_destroy(&(queue->cond_client_queue));
622 pthread_cleanup_pop(0);
623 DA_LOG_CRITICAL(Thread, "=====thread_for_client_noti- EXIT=====");
624 pthread_exit((void *)NULL);
628 char *get_client_user_agent_string(void)
630 if (!client_app_mgr.is_init)
633 return client_app_mgr.client_app_info.client_user_agent;