2 * Copyright (c) 2013 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.
17 #ifdef SUPPORT_NOTIFICATION
19 #include <signal.h> // pthread_kill
20 #include <errno.h> // ESRCH
22 #include "download-provider-log.h"
23 #include "download-provider-pthread.h"
24 #include "download-provider-client.h"
25 #include "download-provider-client-manager.h"
26 #include "download-provider-notification.h"
27 #include "download-provider-db-defs.h"
28 #include "download-provider-db.h"
30 #include "download-provider-notification-manager.h"
32 #ifdef SUPPORT_NOTIFICATION
33 typedef struct { // manage clients without mutex
40 void *slot; // client can not be NULL. it will exist in dummy
41 void *request; // this can be NULL after destroy
43 } dp_notification_queue_fmt;
46 pthread_mutex_t g_dp_notification_manager_mutex = PTHREAD_MUTEX_INITIALIZER;
47 pthread_cond_t g_dp_notification_manager_cond = PTHREAD_COND_INITIALIZER;
48 pthread_t g_dp_notification_manager_tid = 0;
50 static dp_notification_queue_fmt *g_dp_notification_clear = NULL;
51 static dp_notification_queue_fmt *g_dp_notification_ongoing = NULL;
52 static dp_notification_queue_fmt *g_dp_notification = NULL;
54 pthread_mutex_t g_dp_notification_queue_mutex = PTHREAD_MUTEX_INITIALIZER;
55 pthread_mutex_t g_dp_notification_queue_ongoing_mutex = PTHREAD_MUTEX_INITIALIZER;
57 // normal . push at the tail of queue.
58 static int __dp_notification_queue_push(dp_notification_queue_fmt **queue, void *slot, void *request, const int id, const int state, const int noti_priv_id, const double received_size, const double file_size, const dp_noti_type type)
61 TRACE_ERROR("check memory address of queue");
64 if (slot == NULL || request == NULL) {
65 TRACE_ERROR("check client and request memory address");
68 TRACE_ERROR("check slot or download id:%d", id);
73 CLIENT_MUTEX_LOCK(&g_dp_notification_queue_mutex);
74 // search the tail of queue
76 dp_notification_queue_fmt *tailp = *queue;
77 dp_notification_queue_fmt *prevp = NULL;
78 for (; tailp != NULL; i++) {
82 dp_notification_queue_fmt *new_queue = (dp_notification_queue_fmt *)malloc(sizeof(dp_notification_queue_fmt));
83 if (new_queue != NULL) {
84 new_queue->slot = slot;
86 new_queue->state = state;
87 new_queue->noti_priv_id = noti_priv_id;
88 new_queue->received_size = received_size;
89 new_queue->file_size = file_size;
90 new_queue->type = type;
91 new_queue->request = request;
92 new_queue->next = NULL;
96 prevp->next = new_queue;
99 //TRACE_DEBUG("queue push %d id:%d", i, id);
100 CLIENT_MUTEX_UNLOCK(&g_dp_notification_queue_mutex);
104 int __dp_notification_queue_pop(dp_notification_queue_fmt **queue, void **slot, void **request, int *id, int *state, int *noti_priv_id, double *received_size, double *file_size, dp_noti_type *type)
107 TRACE_ERROR("check memory address of queue");
111 TRACE_ERROR("check client memory address");
115 int lock = CLIENT_MUTEX_TRYLOCK(&g_dp_notification_queue_mutex);
117 TRACE_DEBUG("skip queue is used by other thread");
120 if (queue == NULL || *queue == NULL) {
121 //TRACE_DEBUG("queue empty");
122 CLIENT_MUTEX_UNLOCK(&g_dp_notification_queue_mutex);
125 // get a head of queue
128 dp_notification_queue_fmt *popp = *queue;
130 if (popp->slot == NULL) {
131 TRACE_DEBUG("queue error slot:%p id:%d", popp->slot, popp->id);
134 if (request != NULL) {
135 *request = popp->request;
138 *state = popp->state;
139 if (noti_priv_id != NULL)
140 *noti_priv_id = popp->noti_priv_id;
141 if (received_size != NULL)
142 *received_size = popp->received_size;
143 if (file_size != NULL)
144 *file_size = popp->file_size;
149 } while (*queue != NULL); // if meet the tail of queue
150 CLIENT_MUTEX_UNLOCK(&g_dp_notification_queue_mutex);
154 int __dp_notification_queue_ongoing_pop(dp_notification_queue_fmt **queue, void **slot, void **request, int *id, int *state, double *received_size, double *file_size, dp_noti_type *type)
157 TRACE_ERROR("check memory address of queue");
161 TRACE_ERROR("check client memory address");
165 int lock = CLIENT_MUTEX_TRYLOCK(&g_dp_notification_queue_ongoing_mutex);
167 TRACE_DEBUG("skip queue is used by other thread");
170 if (queue == NULL || *queue == NULL) {
171 //TRACE_DEBUG("queue empty");
172 CLIENT_MUTEX_UNLOCK(&g_dp_notification_queue_ongoing_mutex);
175 // get a head of queue
178 dp_notification_queue_fmt *popp = *queue;
180 if (popp->slot == NULL) {
181 TRACE_DEBUG("queue error slot:%p id:%d", popp->slot, popp->id);
184 if (request != NULL) {
185 *request = popp->request;
188 *state = popp->state;
189 if (received_size != NULL)
190 *received_size = popp->received_size;
191 if (file_size != NULL)
192 *file_size = popp->file_size;
197 } while (*queue != NULL); // if meet the tail of queue
198 CLIENT_MUTEX_UNLOCK(&g_dp_notification_queue_ongoing_mutex);
202 static int __dp_notification_queue_ongoing_push(dp_notification_queue_fmt **queue, void *slot, void *request, const int id, const int state, const double received_size, const double file_size, const dp_noti_type type)
205 TRACE_ERROR("check memory address of queue");
208 if (slot == NULL || request == NULL) {
209 TRACE_ERROR("check client and request memory address");
211 } else if (id <= 0) {
212 TRACE_ERROR("check slot or download id:%d", id);
217 CLIENT_MUTEX_LOCK(&g_dp_notification_queue_ongoing_mutex);
218 // search the tail of queue
220 dp_notification_queue_fmt *tailp = *queue;
221 dp_notification_queue_fmt *prevp = NULL;
222 for (; tailp != NULL; i++) {
223 if (tailp->slot == slot && tailp->request == request) {
224 if (tailp->id == id && tailp->state == state && tailp->type == type) {
225 CLIENT_MUTEX_UNLOCK(&g_dp_notification_queue_ongoing_mutex);
232 dp_notification_queue_fmt *new_queue = (dp_notification_queue_fmt *)malloc(sizeof(dp_notification_queue_fmt));
233 if (new_queue != NULL) {
234 new_queue->slot = slot;
236 new_queue->state = state;
237 new_queue->received_size = received_size;
238 new_queue->file_size = file_size;
239 new_queue->type = type;
240 new_queue->request = request;
241 new_queue->next = NULL;
245 prevp->next = new_queue;
248 //TRACE_DEBUG("queue push %d id:%d", i, id);
249 CLIENT_MUTEX_UNLOCK(&g_dp_notification_queue_ongoing_mutex);
253 void __dp_notification_queue_clear(dp_notification_queue_fmt **queue, const int id)
256 TRACE_ERROR("check memory address of queue");
259 CLIENT_MUTEX_LOCK(&g_dp_notification_queue_mutex);
261 dp_notification_queue_fmt *tailp = *queue;
262 dp_notification_queue_fmt *prevp = NULL;
263 for (; tailp != NULL; i++) {
264 if (tailp->id == id) {
267 *queue = tailp->next;
269 prevp->next = tailp->next;
270 TRACE_DEBUG("queue clear this %d id:%d", i, tailp->id);
277 CLIENT_MUTEX_UNLOCK(&g_dp_notification_queue_mutex);
280 int dp_notification_manager_clear_notification(void *slot, void *request, const dp_noti_type type)
282 dp_request_fmt *requestp = request;
283 if (request == NULL) {
284 TRACE_DEBUG("check address request:%p id:%d",
285 request, (request == NULL ? 0 : requestp->id));
288 __dp_notification_queue_clear(&g_dp_notification, requestp->id);
289 TRACE_DEBUG("push clear id:%d noti_priv_id:%d", requestp->id, requestp->noti_priv_id);
290 if (__dp_notification_queue_push(&g_dp_notification_clear, slot, request, requestp->id, requestp->state, -1, 0, 0, type) < 0) {
291 TRACE_ERROR("failed to push to notification id:%d", requestp->id);
294 dp_notification_manager_wake_up();
298 int dp_notification_manager_push_notification(void *slot, void *request, const dp_noti_type type)
300 dp_request_fmt *requestp = request;
301 if (slot == NULL || request == NULL) {
302 TRACE_DEBUG("check address client:%p request:%p id:%d", slot,
303 request, (request == NULL ? 0 : requestp->id));
306 // TRACE_DEBUG("push noti id:%d noti_priv_id:%d type:%d", requestp->id, requestp->noti_priv_id, type);
307 if (type == DP_NOTIFICATION) {
308 __dp_notification_queue_clear(&g_dp_notification_ongoing, requestp->id);
309 if (__dp_notification_queue_push(&g_dp_notification, slot, request, requestp->id, requestp->state, requestp->noti_priv_id, 0, (double)requestp->file_size, type) < 0) {
310 TRACE_ERROR("failed to push to notification id:%d", requestp->id);
314 __dp_notification_queue_clear(&g_dp_notification, requestp->id);
315 if (__dp_notification_queue_ongoing_push(&g_dp_notification_ongoing, slot, request, requestp->id, requestp->state, (double)requestp->received_size, (double)requestp->file_size, type) < 0) {
316 TRACE_ERROR("failed to push to notification id:%d", requestp->id);
320 dp_notification_manager_wake_up();
324 static void __dp_notification_manager_check_notification()
328 int errorcode = DP_ERROR_NONE;
329 int download_id = -1;
331 dp_noti_type noti_type = -1;
332 dp_client_slots_fmt *slot = NULL;
333 dp_request_fmt *request = NULL;
335 if (__dp_notification_queue_pop(&g_dp_notification_clear, (void *)&slot, (void *)&request, &download_id, &state, NULL, NULL, NULL, ¬i_type) == 0) {
337 int noti_priv_id = -1;
338 if (CLIENT_MUTEX_CHECKLOCK(&slot->mutex) == 0) {
339 if (request != NULL && request->id == download_id && request->noti_priv_id >= 0) {
340 noti_priv_id = request->noti_priv_id;
341 request->noti_priv_id = -1;
342 if (dp_db_replace_property(slot->client.dbhandle, download_id, DP_TABLE_NOTIFICATION, DP_DB_COL_NOTI_PRIV_ID, (void *)&request->noti_priv_id, 0, 0, &errorcode) < 0) {
343 TRACE_ERROR("failed to set priv_id id:%d error:%s", download_id, dp_print_errorcode(errorcode));
346 CLIENT_MUTEX_UNLOCK(&slot->mutex);
348 TRACE_DEBUG("clear ongoing id:%d noti_priv_id:%d type:%d", download_id, noti_priv_id, noti_type);
349 if (noti_priv_id >= 0) {
350 if (noti_type > DP_NOTIFICATION)
351 dp_notification_delete_ongoing(noti_priv_id);
353 dp_notification_delete(noti_priv_id);
359 int noti_priv_id = -1;
360 if (__dp_notification_queue_pop(&g_dp_notification, (void *)&slot, (void *)&request, &download_id, &state, ¬i_priv_id, NULL, NULL, ¬i_type) == 0) {
362 __dp_notification_queue_clear(&g_dp_notification_ongoing, download_id); // prevent new ongoing
363 if (noti_priv_id >= 0) {
364 TRACE_DEBUG("clear ongoing(%d) id:%d type:%d state:%d", noti_priv_id, download_id, noti_type, state);
365 dp_notification_delete_ongoing(noti_priv_id);
368 if (CLIENT_MUTEX_CHECKLOCK(&slot->mutex) == 0) {
369 TRACE_DEBUG("notification id:%d type:%d state:%d", download_id, noti_type, state);
370 if (request != NULL && request->id == download_id && request->noti_priv_id >= 0) {
371 dp_notification_delete_ongoing(request->noti_priv_id);
372 request->noti_priv_id = -1;
374 dp_content_type content_type = DP_CONTENT_UNKNOWN;
376 content_type = request->content_type;
377 noti_priv_id = dp_notification_new(slot->client.dbhandle, download_id, state, content_type, slot->pkgname); // lazy API
378 TRACE_DEBUG("new notification(%d) id:%d type:%d state:%d", noti_priv_id, download_id, noti_type, state);
379 if (noti_priv_id < 0) {
380 TRACE_ERROR("failed to register notification for id:%d", download_id);
382 if (request != NULL && request->id == download_id) {
383 request->noti_priv_id = noti_priv_id;
385 if (dp_db_replace_property(slot->client.dbhandle, download_id, DP_TABLE_NOTIFICATION, DP_DB_COL_NOTI_PRIV_ID, (void *)¬i_priv_id, 0, 0, &errorcode) < 0) {
386 TRACE_ERROR("failed to set priv_id id:%d error:%s", download_id, dp_print_errorcode(errorcode));
389 CLIENT_MUTEX_UNLOCK(&slot->mutex);
394 double received_size = 0;
395 double file_size = 0;
396 if (__dp_notification_queue_ongoing_pop(&g_dp_notification_ongoing, (void *)&slot, (void *)&request, &download_id, &state, &received_size, &file_size, ¬i_type) == 0) {
398 int noti_priv_id = -1;
400 if (CLIENT_MUTEX_CHECKLOCK(&slot->mutex) == 0) {
401 if (request != NULL && request->id == download_id) {
402 request_id = download_id;
403 if (request->noti_priv_id >= 0) {
404 noti_priv_id = request->noti_priv_id;
407 CLIENT_MUTEX_UNLOCK(&slot->mutex);
409 TRACE_ERROR("ongoing wrong address id:%d noti_priv_id:%d type:%d state:%d", download_id, noti_priv_id, noti_type, state);
413 if (request_id < 0) {
414 TRACE_ERROR("ongoing wrong info id:%d noti_priv_id:%d type:%d state:%d", download_id, noti_priv_id, noti_type, state);
419 if (noti_priv_id < 0 && noti_type > DP_NOTIFICATION_ONGOING) {
420 TRACE_DEBUG("ongoing precheck id:%d noti_priv_id:%d type:%d state:%d", download_id, noti_priv_id, noti_type, state);
421 noti_type = DP_NOTIFICATION_ONGOING;
424 TRACE_DEBUG("ongoing id:%d noti_priv_id:%d type:%d state:%d", download_id, noti_priv_id, noti_type, state);
426 char *subject = NULL;
427 if (noti_type == DP_NOTIFICATION || noti_type == DP_NOTIFICATION_ONGOING_UPDATE) {
429 if (CLIENT_MUTEX_CHECKLOCK(&slot->mutex) == 0) {
430 if (request != NULL) {
431 if (dp_db_get_property_string(slot->client.dbhandle, download_id, DP_TABLE_NOTIFICATION, DP_DB_COL_NOTI_SUBJECT, (unsigned char **)&subject, &length, &errorcode) < 0) {
432 TRACE_ERROR("failed to get subject id:%d error:%s", download_id, dp_print_errorcode(errorcode));
433 } else if (subject == NULL && dp_db_get_property_string(slot->client.dbhandle, download_id, DP_TABLE_DOWNLOAD, DP_DB_COL_CONTENT_NAME, (unsigned char **)&subject, &length, &errorcode) < 0) {
434 TRACE_ERROR("failed to get content_name id:%d error:%s", download_id, dp_print_errorcode(errorcode));
437 CLIENT_MUTEX_UNLOCK(&slot->mutex);
441 if (noti_type > DP_NOTIFICATION_ONGOING) { // update
442 if (noti_priv_id >= 0 && dp_notification_ongoing_update(noti_priv_id, received_size, file_size, subject) < 0) {
443 TRACE_ERROR("failed to update ongoing for id:%d", download_id);
445 } else { // new ongoing
446 if (noti_priv_id >= 0) {
447 TRACE_DEBUG("clear ongoing id:%d noti_priv_id:%d", download_id, noti_priv_id);
448 dp_notification_delete(noti_priv_id);
449 dp_notification_delete_ongoing(noti_priv_id);
452 unsigned char *raws_buffer = NULL;
454 if (CLIENT_MUTEX_CHECKLOCK(&slot->mutex) == 0) {
455 if (dp_db_get_property_string(slot->client.dbhandle, download_id, DP_TABLE_NOTIFICATION, DP_DB_COL_NOTI_RAW_ONGOING, &raws_buffer, &length, &errorcode) < 0) {
456 TRACE_DEBUG("failed to get bundle raws id:%d error:%s", download_id, dp_print_errorcode(errorcode));
458 CLIENT_MUTEX_UNLOCK(&slot->mutex);
460 noti_priv_id = dp_notification_ongoing_new(slot->pkgname, subject, raws_buffer, length);
461 TRACE_DEBUG("new ongoing(%d) id:%d type:%d state:%d", noti_priv_id, download_id, noti_type, state);
463 if (noti_priv_id < 0) {
464 TRACE_ERROR("failed to update ongoing for id:%d", download_id);
466 if (CLIENT_MUTEX_CHECKLOCK(&slot->mutex) == 0) {
468 request->noti_priv_id = noti_priv_id;
469 if (dp_db_replace_property(slot->client.dbhandle, download_id, DP_TABLE_NOTIFICATION, DP_DB_COL_NOTI_PRIV_ID, (void *)¬i_priv_id, 0, 0, &errorcode) < 0) {
470 TRACE_ERROR("failed to set priv_id id:%d error:%s", download_id, dp_print_errorcode(errorcode));
472 CLIENT_MUTEX_UNLOCK(&slot->mutex);
480 } while (pop_queue > 0);
483 static void *__dp_notification_manager(void *arg)
485 pthread_cond_init(&g_dp_notification_manager_cond, NULL);
487 dp_notification_set_locale();
489 while (g_dp_notification_manager_tid > 0) {
491 if (g_dp_notification_manager_tid <= 0) {
492 TRACE_DEBUG("notification-manager is closed by other thread");
496 // check wifi_direct first
497 __dp_notification_manager_check_notification();
499 CLIENT_MUTEX_LOCK(&g_dp_notification_manager_mutex);
500 pthread_cond_wait(&g_dp_notification_manager_cond, &g_dp_notification_manager_mutex);
501 CLIENT_MUTEX_UNLOCK(&g_dp_notification_manager_mutex);
504 TRACE_DEBUG("notification-manager's working is done");
505 dp_notification_clear_locale();
506 pthread_cond_destroy(&g_dp_notification_manager_cond);
511 static int __dp_notification_manager_start()
513 if (g_dp_notification_manager_tid == 0 ||
514 pthread_kill(g_dp_notification_manager_tid, 0) == ESRCH) {
515 TRACE_DEBUG("try to create notification-manager");
516 if (pthread_create(&g_dp_notification_manager_tid, NULL,
517 __dp_notification_manager, NULL) != 0) {
518 TRACE_ERROR("failed to create notification-manager");
525 void dp_notification_manager_wake_up()
527 if (g_dp_notification_manager_tid > 0) {
528 int locked = CLIENT_MUTEX_TRYLOCK(&g_dp_notification_manager_mutex);
530 pthread_cond_signal(&g_dp_notification_manager_cond);
531 CLIENT_MUTEX_UNLOCK(&g_dp_notification_manager_mutex);
534 __dp_notification_manager_start();
538 void dp_notification_manager_kill()
540 if (g_dp_notification_manager_tid > 0 &&
541 pthread_kill(g_dp_notification_manager_tid, 0) != ESRCH) {
542 //send signal to notification thread
545 tid = g_dp_notification_manager_tid;
546 CLIENT_MUTEX_LOCK(&g_dp_notification_manager_mutex);
547 g_dp_notification_manager_tid = 0;
548 pthread_cond_signal(&g_dp_notification_manager_cond);
549 CLIENT_MUTEX_UNLOCK(&g_dp_notification_manager_mutex);
550 pthread_join(tid, (void **)&status);
555 int dp_notification_manager_clear_notification(void *slot, void *request, const dp_noti_type type)
560 int dp_notification_manager_push_notification(void *slot, void *request, const dp_noti_type type)
565 void dp_notification_manager_wake_up()
570 void dp_notification_manager_kill()